import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
private static final String MEASURES_EXTENSION = ".linemeasures";
- private MetricFinder metricFinder;
-
private FileLinesContextFactory contextFactory;
public LineMeasureSensor(FileLinesContextFactory contextFactory) {
this.contextFactory = contextFactory;
- this.metricFinder = metricFinder;
}
private void processFileMeasures(InputFile inputFile, SensorContext context) {
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
+
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.measures.Metric;
* <p/>
* Scanners should not send other metrics, and the Compute Engine should not allow other metrics.
*/
+@Immutable
@ComputeEngineSide
@ScannerSide
public class ScannerMetrics {
import java.util.ArrayList;
import java.util.List;
+
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.ByteArray;
* works with {@link TokensLine},
* sets {@link Block#getStartUnit() startUnit} and {@link Block#getEndUnit() endUnit} - indexes of first and last token for this block.
*/
+@Immutable
public class PmdBlockChunker {
private static final long PRIME_BASE = 31;
*/
package org.sonar.api;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
@ServerSide
@ComputeEngineSide
@SonarLintSide
+@Immutable
public interface SonarRuntime {
/**
* <li>Change project metadata like description or source directories.</li>
* </ul>
*
+ * @deprecated since 6.5. It won't be possible to manipulate the project's structure.
* @since 2.9
*/
@ScannerSide
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
@ExtensionPoint
+@Deprecated
public abstract class ProjectBuilder {
/**
* Plugins can use the implementation {@link org.sonar.api.batch.bootstrap.internal.ProjectBuilderContext}
* for their unit tests.
*/
+ @Deprecated
public interface Context {
ProjectReactor projectReactor();
}
* {@link org.sonar.api.batch.bootstrap.ProjectBuilder extension point} and must not be used
* by other standard extensions.
*
+ * Since 6.5, plugins should no longer manipulate the project's structure.
+ *
* @since 2.9
*/
public class ProjectDefinition {
/**
* Mutable project definitions that can be modified by {@link ProjectBuilder} extensions.
+ *
+ * @deprecated since 6.5 plugins should no longer modify the project's structure
* @since 2.9
*/
+@Deprecated
@ScannerSide
public class ProjectReactor implements ProjectKey {
}
public List<ProjectDefinition> getProjects() {
- return collectProjects(root, new ArrayList<ProjectDefinition>());
+ return collectProjects(root, new ArrayList<>());
}
/**
* Context that is passed to {@link org.sonar.api.batch.bootstrap.ProjectBuilder} as parameter.
* Important - plugins must use this class only for unit test needs.
*
+ * @deprecated since 6.5
* @since 3.7
*/
+@Deprecated
public class ProjectBuilderContext implements ProjectBuilder.Context {
private final ProjectReactor reactor;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.utils.Duration;
+import javax.annotation.concurrent.Immutable;
/**
* @since 4.3
* @deprecated since 6.5 debt model will soon be unavailable on batch side
*/
@Deprecated
+@Immutable
public class DebtRemediationFunction {
public enum Type {
*/
package org.sonar.api.batch.fs;
+
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.sensor.SensorContext;
/**
*
* @since 5.2
*/
+@Immutable
public interface InputModule extends InputComponent {
}
import java.io.InputStream;
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 relativePath;
private final String moduleKey;
private final Path moduleBaseDir;
- private String language;
+ private final String language;
private final Type type;
+ private final Path path;
/**
* Testing purposes only!
*/
- public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath) {
- this(moduleKey, moduleBaseDir, relativePath, TestInputFileBuilder.nextBatchId());
+ public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, @Nullable String language) {
+ this(moduleKey, moduleBaseDir, relativePath, language, TestInputFileBuilder.nextBatchId());
}
- public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, int batchId) {
- this(moduleKey, moduleBaseDir, relativePath, Type.MAIN, batchId);
+ public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, @Nullable String language, int batchId) {
+ this(moduleKey, moduleBaseDir, relativePath, Type.MAIN, language, batchId);
}
- public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, Type type, int batchId) {
+ public DefaultIndexedFile(String moduleKey, Path moduleBaseDir, String relativePath, Type type, @Nullable String language, int batchId) {
super(batchId);
this.moduleKey = moduleKey;
this.relativePath = PathUtils.sanitize(relativePath);
this.moduleBaseDir = moduleBaseDir.normalize();
this.type = type;
- }
-
- public DefaultIndexedFile setLanguage(@Nullable String language) {
this.language = language;
- return this;
+ this.path = this.moduleBaseDir.resolve(this.relativePath);
}
@Override
@Override
public Path path() {
- return moduleBaseDir.resolve(relativePath);
+ return path;
}
@Override
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private final DefaultIndexedFile indexedFile;
+ private final String contents;
private final Consumer<DefaultInputFile> metadataGenerator;
+
private Status status;
private Charset charset;
private Metadata metadata;
private boolean publish;
- private String contents;
public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator) {
this(indexedFile, metadataGenerator, null);
*/
package org.sonar.api.batch.fs.internal;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputModule;
/**
* @since 5.2
*/
+@Immutable
public class DefaultInputModule extends DefaultInputComponent implements InputModule {
+ private final File baseDir;
+ private final File workDir;
+ private final String name;
+ private final String version;
+ private final String originalName;
+ private final String originalVersion;
+ private final String description;
+ private final String keyWithBranch;
+ private final String branch;
+ private final List<String> sources;
+ private final List<String> tests;
+ private final Map<String, String> properties;
private final String moduleKey;
private final ProjectDefinition definition;
public DefaultInputModule(String moduleKey) {
this(ProjectDefinition.create().setKey(moduleKey), TestInputFileBuilder.nextBatchId());
}
+
+ /**
+ * For testing only!
+ */
+ public DefaultInputModule(ProjectDefinition definition) {
+ this(definition, TestInputFileBuilder.nextBatchId());
+ }
public DefaultInputModule(ProjectDefinition definition, int batchId) {
super(batchId);
+ this.baseDir = definition.getBaseDir();
+ this.workDir = definition.getWorkDir();
+ this.name = definition.getName();
+ this.originalName = definition.getOriginalName();
+ this.version = definition.getVersion();
+ this.originalVersion = definition.getOriginalVersion();
+ this.description = definition.getDescription();
+ this.keyWithBranch = definition.getKeyWithBranch();
+ this.branch = definition.getBranch();
+ this.sources = Collections.unmodifiableList(new ArrayList<>(definition.sources()));
+ this.tests = Collections.unmodifiableList(new ArrayList<>(definition.tests()));
+ this.properties = Collections.unmodifiableMap(new HashMap<>(definition.properties()));
+
this.definition = definition;
this.moduleKey = definition.getKey();
}
public ProjectDefinition definition() {
return definition;
}
+
+ public File getBaseDir() {
+ return baseDir;
+ }
+
+ public File getWorkDir() {
+ return workDir;
+ }
+
+ public String getKeyWithBranch() {
+ return keyWithBranch;
+ }
+
+ @CheckForNull
+ public String getBranch() {
+ return branch;
+ }
+
+ public Map<String, String> properties() {
+ return properties;
+ }
+
+ @CheckForNull
+ public String getOriginalVersion() {
+ return originalVersion;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ @CheckForNull
+ public String getOriginalName() {
+ return originalName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @return Source files and folders.
+ */
+ public List<String> sources() {
+ return sources;
+ }
+
+ public List<String> tests() {
+ return tests;
+ }
}
package org.sonar.api.batch.fs.internal;
import java.io.BufferedReader;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.sonar.api.CoreProperties;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
+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.
*/
@ScannerSide
+@Immutable
public class FileMetadata {
-
- private static final Logger LOG = Loggers.get(FileMetadata.class);
-
private static final char LINE_FEED = '\n';
private static final char CARRIAGE_RETURN = '\r';
- public abstract static class CharHandler {
-
- protected void handleAll(char c) {
- }
-
- protected void handleIgnoreEoL(char c) {
- }
-
- protected void newLine() {
- }
-
- protected void eof() {
- }
- }
-
- private static class LineCounter extends CharHandler {
- private int lines = 1;
- private int nonBlankLines = 0;
- private boolean blankLine = true;
- boolean alreadyLoggedInvalidCharacter = false;
- private final String filePath;
- private final Charset encoding;
-
- LineCounter(String filePath, Charset encoding) {
- this.filePath = filePath;
- this.encoding = encoding;
- }
-
- @Override
- protected 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
- protected void newLine() {
- lines++;
- if (!blankLine) {
- nonBlankLines++;
- }
- blankLine = true;
- }
-
- @Override
- protected void handleIgnoreEoL(char c) {
- if (!Character.isWhitespace(c)) {
- blankLine = false;
- }
- }
-
- @Override
- protected void eof() {
- if (!blankLine) {
- nonBlankLines++;
- }
- }
-
- public int lines() {
- return lines;
- }
-
- public int nonBlankLines() {
- return nonBlankLines;
- }
-
- }
-
- private static class FileHashComputer extends CharHandler {
- 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
- protected void handleIgnoreEoL(char c) {
- sb.append(c);
- }
-
- @Override
- protected void newLine() {
- sb.append(LINE_FEED);
- processBuffer();
- sb.setLength(0);
- }
-
- @Override
- protected 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);
- }
- }
-
- @CheckForNull
- public String getHash() {
- return Hex.encodeHexString(globalMd5Digest.digest());
- }
- }
-
- private static 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
- protected void handleIgnoreEoL(char c) {
- if (!Character.isWhitespace(c)) {
- sb.append(c);
- }
- }
-
- @Override
- protected void newLine() {
- processBuffer();
- sb.setLength(0);
- line++;
- }
-
- @Override
- protected 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);
- }
- }
- }
-
- private static class LineOffsetCounter extends CharHandler {
- private long currentOriginalOffset = 0;
- private IntArrayList originalLineOffsets = new IntArrayList();
- private long lastValidOffset = 0;
-
- public LineOffsetCounter() {
- originalLineOffsets.add(0);
- }
-
- @Override
- protected void handleAll(char c) {
- currentOriginalOffset++;
- }
-
- @Override
- protected void newLine() {
- if (currentOriginalOffset > Integer.MAX_VALUE) {
- throw new IllegalStateException("File is too big: " + currentOriginalOffset);
- }
- originalLineOffsets.add((int) currentOriginalOffset);
- }
-
- @Override
- protected void eof() {
- lastValidOffset = currentOriginalOffset;
- }
-
- public int[] getOriginalLineOffsets() {
- return originalLineOffsets.trimAndGet();
- }
-
- public int getLastValidOffset() {
- if (lastValidOffset > Integer.MAX_VALUE) {
- throw new IllegalStateException("File is too big: " + lastValidOffset);
- }
- return (int) lastValidOffset;
- }
-
- }
-
/**
* Compute hash of a file ignoring line ends differences.
* Maximum performance is needed.
import java.util.Collection;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
import org.sonar.api.batch.fs.InputModule;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
+@Immutable
public interface InputModuleHierarchy {
DefaultInputModule root();
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.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) {
- if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
- minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
- }
-
- ensureExplicitCapacity(minCapacity);
- }
-
- 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;
- }
-
-}
*/
package org.sonar.api.batch.fs.internal;
+import java.util.Arrays;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
public class Metadata {
private final int lines;
private final int nonBlankLines;
this.lines = lines;
this.nonBlankLines = nonBlankLines;
this.hash = hash;
- this.originalLineOffsets = originalLineOffsets;
+ this.originalLineOffsets = Arrays.copyOf(originalLineOffsets, originalLineOffsets.length);
this.lastValidOffset = lastValidOffset;
}
*/
package org.sonar.api.batch.fs.internal;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.fs.IndexedFile;
import org.sonar.api.utils.WildcardPattern;
+@ThreadSafe
public abstract class PathPattern {
final WildcardPattern pattern;
this.pattern = WildcardPattern.create(pattern);
}
- public abstract boolean match(IndexedFile inputFile);
+ public abstract boolean match(String absolutePath, String relativePath);
- public abstract boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension);
+ public abstract boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension);
public static PathPattern create(String s) {
String trimmed = StringUtils.trim(s);
}
@Override
- public boolean match(IndexedFile inputFile) {
- return match(inputFile, true);
+ public boolean match(String absolutePath, String relativePath) {
+ return match(absolutePath, relativePath, true);
}
@Override
- public boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension) {
- String path = inputFile.absolutePath();
+ public boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension) {
+ String path = absolutePath;
if (!caseSensitiveFileExtension) {
- String extension = sanitizeExtension(FilenameUtils.getExtension(inputFile.file().getName()));
+ String extension = sanitizeExtension(FilenameUtils.getExtension(relativePath));
if (StringUtils.isNotBlank(extension)) {
path = StringUtils.removeEndIgnoreCase(path, extension);
path = path + extension;
}
@Override
- public boolean match(IndexedFile inputFile) {
- return match(inputFile, true);
+ public boolean match(String absolutePath, String relativePath) {
+ return match(absolutePath, relativePath, true);
}
@Override
- public boolean match(IndexedFile inputFile, boolean caseSensitiveFileExtension) {
- String path = inputFile.relativePath();
+ public boolean match(String absolutePath, String relativePath, boolean caseSensitiveFileExtension) {
+ String path = relativePath;
if (!caseSensitiveFileExtension) {
- String extension = sanitizeExtension(FilenameUtils.getExtension(inputFile.file().getName()));
+ String extension = sanitizeExtension(FilenameUtils.getExtension(relativePath));
if (StringUtils.isNotBlank(extension)) {
path = StringUtils.removeEndIgnoreCase(path, extension);
path = path + extension;
@Override
public boolean apply(InputFile f) {
- return pattern.match(f);
+ return pattern.match(f.absolutePath(), f.relativePath());
}
}
private int lastValidOffset = -1;
private String hash;
private int nonBlankLines;
- private int[] originalLineOffsets;
+ private int[] originalLineOffsets = new int[0];
private boolean publish = true;
private String contents;
}
public DefaultInputFile build() {
- DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, id);
- indexedFile.setLanguage(language);
+ DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, language, id);
DefaultInputFile inputFile = new DefaultInputFile(indexedFile,
f -> f.setMetadata(new Metadata(lines, nonBlankLines, hash, originalLineOffsets, lastValidOffset)),
contents);
}
public static DefaultInputModule newDefaultInputModule(String moduleKey, File baseDir) {
- ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey);
- definition.setBaseDir(baseDir);
- return new DefaultInputModule(definition, TestInputFileBuilder.nextBatchId());
+ ProjectDefinition definition = ProjectDefinition.create().setKey(moduleKey).setBaseDir(baseDir);
+ return newDefaultInputModule(definition);
+ }
+
+ public static DefaultInputModule newDefaultInputModule(ProjectDefinition projectDefinition) {
+ return new DefaultInputModule(projectDefinition, TestInputFileBuilder.nextBatchId());
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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() {
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 javax.annotation.CheckForNull;
+
+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);
+ }
+ }
+
+ @CheckForNull
+ public String getHash() {
+ return Hex.encodeHexString(globalMd5Digest.digest());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 currentOriginalOffset = 0;
+ private IntArrayList originalLineOffsets = new IntArrayList();
+ private long lastValidOffset = 0;
+
+ public LineOffsetCounter() {
+ originalLineOffsets.add(0);
+ }
+
+ @Override
+ public void handleAll(char c) {
+ currentOriginalOffset++;
+ }
+
+ @Override
+ public void newLine() {
+ if (currentOriginalOffset > Integer.MAX_VALUE) {
+ throw new IllegalStateException("File is too big: " + currentOriginalOffset);
+ }
+ originalLineOffsets.add((int) currentOriginalOffset);
+ }
+
+ @Override
+ public void eof() {
+ lastValidOffset = currentOriginalOffset;
+ }
+
+ public int[] getOriginalLineOffsets() {
+ return originalLineOffsets.trimAndGet();
+ }
+
+ public int getLastValidOffset() {
+ if (lastValidOffset > Integer.MAX_VALUE) {
+ throw new IllegalStateException("File is too big: " + lastValidOffset);
+ }
+ return (int) lastValidOffset;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.batch.ScannerSide;
/**
* @since 4.5
*/
@ScannerSide
+@ThreadSafe
public interface MetricFinder {
@CheckForNull
import java.util.Map;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.rule.RuleKey;
/**
* Configuration of a rule activated on a Quality profile
* @since 4.2
*/
+@Immutable
public interface ActiveRule {
RuleKey ruleKey();
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
import java.util.Collection;
*
* @since 4.2
*/
+@Immutable
@ScannerSide
public interface ActiveRules {
*/
package org.sonar.api.batch.rule;
+import javax.annotation.concurrent.Immutable;
+
/**
* @since 4.2
*/
+@Immutable
public interface RuleParam {
String key();
+
String description();
}
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
import java.util.Collection;
* @since 4.2
*/
@ScannerSide
+@Immutable
public interface Rules {
/**
package org.sonar.api.batch.rule.internal;
import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.rule.RuleKey;
import javax.annotation.concurrent.Immutable;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Immutable
public class DefaultActiveRules implements ActiveRules {
-
- // TODO use disk-backed cache (persistit) instead of full in-memory cache ?
- private final ListMultimap<String, ActiveRule> activeRulesByRepository;
+ private final ImmutableListMultimap<String, ActiveRule> activeRulesByRepository;
private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndKey = new HashMap<>();
private final Map<String, Map<String, ActiveRule>> activeRulesByRepositoryAndInternalKey = new HashMap<>();
- private final ListMultimap<String, ActiveRule> activeRulesByLanguage;
+ private final ImmutableListMultimap<String, ActiveRule> activeRulesByLanguage;
public DefaultActiveRules(Collection<NewActiveRule> newActiveRules) {
ImmutableListMultimap.Builder<String, ActiveRule> repoBuilder = ImmutableListMultimap.builder();
ImmutableListMultimap.Builder<String, ActiveRule> langBuilder = ImmutableListMultimap.builder();
for (NewActiveRule newAR : newActiveRules) {
DefaultActiveRule ar = new DefaultActiveRule(newAR);
- repoBuilder.put(ar.ruleKey().repository(), ar);
+ String repo = ar.ruleKey().repository();
+ repoBuilder.put(repo, ar);
if (ar.language() != null) {
langBuilder.put(ar.language(), ar);
}
- if (!activeRulesByRepositoryAndKey.containsKey(ar.ruleKey().repository())) {
- activeRulesByRepositoryAndKey.put(ar.ruleKey().repository(), new HashMap<String, ActiveRule>());
- activeRulesByRepositoryAndInternalKey.put(ar.ruleKey().repository(), new HashMap<String, ActiveRule>());
- }
- activeRulesByRepositoryAndKey.get(ar.ruleKey().repository()).put(ar.ruleKey().rule(), ar);
+
+ activeRulesByRepositoryAndKey.computeIfAbsent(repo, r -> new HashMap<>()).put(ar.ruleKey().rule(), ar);
String internalKey = ar.internalKey();
if (internalKey != null) {
- activeRulesByRepositoryAndInternalKey.get(ar.ruleKey().repository()).put(internalKey, ar);
+ activeRulesByRepositoryAndInternalKey.computeIfAbsent(repo, r -> new HashMap<>()).put(internalKey, ar);
}
}
activeRulesByRepository = repoBuilder.build();
@Override
public ActiveRule find(RuleKey ruleKey) {
- Map<String, ActiveRule> map = activeRulesByRepositoryAndKey.get(ruleKey.repository());
- if(map != null) {
- return map.get(ruleKey.rule());
- }
- return null;
+ return activeRulesByRepositoryAndKey.getOrDefault(ruleKey.repository(), Collections.emptyMap())
+ .get(ruleKey.rule());
}
@Override
import org.sonar.api.batch.rule.Rule;
import com.google.common.collect.Table;
import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ListMultimap;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.rule.Rules;
import org.sonar.api.rule.RuleKey;
@Immutable
class DefaultRules implements Rules {
-
- // TODO use disk-backed cache (persistit) instead of full in-memory cache ?
- private final ListMultimap<String, Rule> rulesByRepository;
+ private final ImmutableListMultimap<String, Rule> rulesByRepository;
private final ImmutableTable<String, String, List<Rule>> rulesByRepositoryAndInternalKey;
DefaultRules(Collection<NewRule> newRules) {
this.inputFile = requireNonNull(inputFile, "file can't be null");
String[] cpdExclusions = config.getStringArray(CoreProperties.CPD_EXCLUSIONS);
for (PathPattern cpdExclusion : PathPattern.create(cpdExclusions)) {
- if (cpdExclusion.match(inputFile)) {
+ if (cpdExclusion.match(inputFile.absolutePath(), inputFile.relativePath())) {
this.excluded = true;
}
}
import org.apache.commons.lang.builder.ToStringBuilder;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.component.Component;
import org.sonar.api.scan.filesystem.PathResolver;
public class Project extends Resource implements Component {
private final ProjectDefinition definition;
+ public Project(DefaultInputModule module) {
+ this(module.definition());
+ }
+
public Project(ProjectDefinition definition) {
this.definition = definition;
this.setKey(definition.getKey());
import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.apache.commons.io.FilenameUtils;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.utils.PathUtils;
* @since 3.5
*/
@ScannerSide
+@Immutable
public class PathResolver {
public File relativeFile(File dir, String path) {
import java.util.Date;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.ThreadSafe;
import org.sonar.api.rule.RuleKey;
/**
* @since 5.3
*/
+@ThreadSafe
public interface FilterableIssue {
String componentKey();
*/
package org.sonar.api.scan.issue.filter;
+
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.ExtensionPoint;
import org.sonar.api.batch.ScannerSide;
import org.sonarsource.api.sonarlint.SonarLintSide;
@SonarLintSide
@ExtensionPoint
@FunctionalInterface
+@ThreadSafe
/**
* @since 5.3
*/
* </ul>
* The <code>chain</code> parameter allows for fine control of the filtering logic: it is each filter's duty to either pass the issue to the next filter, by calling
* the {@link IssueFilterChain#accept} method, or return directly if the issue has to be accepted or not
+ *
+ * Implementations should be thread safe.
+ *
* @param issue the issue being filtered
* @param chain the rest of the filters
* @return <code>true</code> to accept the issue, <code>false</code> to reject it, {@link IssueFilterChain#accept} to let the other filters decide.
*/
package org.sonar.api.scan.issue.filter;
+import javax.annotation.concurrent.ThreadSafe;
+
/**
* A filter chain is an object provided to issues filters for fine control over the filtering logic. Each filter has the choice to:
* <ul>
*
* @since 5.3
*/
+@ThreadSafe
public interface IssueFilterChain {
/**
* Called by a filter to let downstream filters decide the fate of the issue
*/
package org.sonar.api.utils;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.apache.commons.lang.StringUtils;
/**
* <a href="https://github.com/JetBrains/intellij-community/blob/idea/107.743/platform/util/src/com/intellij/openapi/util/io/FileUtil.java#L847">FileUtil</a>
* from IntelliJ OpenAPI.
*
- *
* @since 1.10
*/
+@ThreadSafe
public class WildcardPattern {
- private static final Map<String, WildcardPattern> CACHE = new HashMap<>();
+ private static final Map<String, WildcardPattern> CACHE = Collections.synchronizedMap(new HashMap<>());
private static final String SPECIAL_CHARS = "()[]^$.{}+|";
private Pattern pattern;
*/
public static WildcardPattern create(String pattern, String directorySeparator) {
String key = pattern + directorySeparator;
- WildcardPattern wildcardPattern = CACHE.get(key);
- if (wildcardPattern == null) {
- wildcardPattern = new WildcardPattern(pattern, directorySeparator);
- CACHE.put(key, wildcardPattern);
- }
- return wildcardPattern;
+ return CACHE.computeIfAbsent(key, k -> new WildcardPattern(pattern, directorySeparator));
}
}
Path baseDir = temp.newFolder().toPath();
Metadata metadata = new Metadata(42, 42, "", new int[0], 0);
- DefaultIndexedFile indexedFile = new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0).setLanguage("php");
+ DefaultIndexedFile indexedFile = new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, "php", 0);
DefaultInputFile inputFile = new DefaultInputFile(indexedFile, (f) -> f.setMetadata(metadata))
.setStatus(InputFile.Status.ADDED)
.setCharset(StandardCharsets.ISO_8859_1);
Metadata metadata = new Metadata(42, 30, "", new int[0], 0);
- DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0)
- .setLanguage("php"), f -> f.setMetadata(metadata))
- .setStatus(InputFile.Status.ADDED)
- .setCharset(StandardCharsets.ISO_8859_1);
+ DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, "php", 0), f -> f.setMetadata(metadata))
+ .setStatus(InputFile.Status.ADDED)
+ .setCharset(StandardCharsets.ISO_8859_1);
assertThat(inputFile.contents()).isEqualTo(content);
try (InputStream inputStream = inputFile.inputStream()) {
Metadata metadata = new Metadata(42, 30, "", new int[0], 0);
- DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, 0)
- .setLanguage("php"), f -> f.setMetadata(metadata))
- .setStatus(InputFile.Status.ADDED)
- .setCharset(StandardCharsets.UTF_8);
+ DefaultInputFile inputFile = new DefaultInputFile(new DefaultIndexedFile("ABCDE", baseDir, "src/Foo.php", InputFile.Type.TEST, "php", 0), f -> f.setMetadata(metadata))
+ .setStatus(InputFile.Status.ADDED)
+ .setCharset(StandardCharsets.UTF_8);
assertThat(inputFile.contents()).isEqualTo(content);
try (InputStream inputStream = inputFile.inputStream()) {
@Test
public void test_equals_and_hashcode() throws Exception {
- DefaultInputFile f1 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), (f) -> mock(Metadata.class));
- DefaultInputFile f1a = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), (f) -> mock(Metadata.class));
- DefaultInputFile f2 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Bar.php"), (f) -> mock(Metadata.class));
+ DefaultInputFile f1 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), (f) -> mock(Metadata.class));
+ DefaultInputFile f1a = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), (f) -> mock(Metadata.class));
+ DefaultInputFile f2 = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Bar.php", null), (f) -> mock(Metadata.class));
assertThat(f1).isEqualTo(f1);
assertThat(f1).isEqualTo(f1a);
@Test
public void test_toString() throws Exception {
- DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), (f) -> mock(Metadata.class));
+ DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), (f) -> mock(Metadata.class));
assertThat(file.toString()).isEqualTo("[moduleKey=ABCDE, relative=src/Foo.php, basedir=module]");
}
@Test
public void checkValidPointer() {
Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15);
- DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata));
+ DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata));
assertThat(file.newPointer(1, 0).line()).isEqualTo(1);
assertThat(file.newPointer(1, 0).lineOffset()).isEqualTo(0);
// Don't fail
@Test
public void checkValidPointerUsingGlobalOffset() {
Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15);
- DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata));
+ DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata));
assertThat(file.newPointer(0).line()).isEqualTo(1);
assertThat(file.newPointer(0).lineOffset()).isEqualTo(0);
@Test
public void checkValidRange() {
Metadata metadata = new FileMetadata().readMetadata(new StringReader("bla bla a\nabcde"));
- DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata));
+ DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata));
assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 1)).start().line()).isEqualTo(1);
// Don't fail
@Test
public void selectLine() {
Metadata metadata = new FileMetadata().readMetadata(new StringReader("bla bla a\nabcde\n\nabc"));
- DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata));
+ DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata));
assertThat(file.selectLine(1).start().line()).isEqualTo(1);
assertThat(file.selectLine(1).start().lineOffset()).isEqualTo(0);
@Test
public void checkValidRangeUsingGlobalOffset() {
Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15);
- DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata));
+ DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata));
TextRange newRange = file.newRange(10, 13);
assertThat(newRange.start().line()).isEqualTo(2);
assertThat(newRange.start().lineOffset()).isEqualTo(0);
@Test
public void testRangeOverlap() {
Metadata metadata = new Metadata(2, 2, "", new int[] {0, 10}, 15);
- DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php"), f -> f.setMetadata(metadata));
+ DefaultInputFile file = new DefaultInputFile(new DefaultIndexedFile("ABCDE", Paths.get("module"), "src/Foo.php", null), f -> f.setMetadata(metadata));
// Don't fail
assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)))).isTrue();
assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 2)))).isTrue();
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class IntArrayListTest {
-
- @Test
- public void addElements() {
- IntArrayList list = new IntArrayList();
- assertThat(list.trimAndGet()).isEmpty();
- list.add(1);
- list.add(2);
- assertThat(list.trimAndGet()).containsExactly(1, 2);
- }
-
- @Test
- public void trimIfNeeded() {
- IntArrayList list = new IntArrayList();
- list.add(1);
- list.add(2);
- assertThat(list.trimAndGet()).isSameAs(list.trimAndGet());
- }
-
- @Test
- public void grow() {
- // Default capacity is 10
- IntArrayList list = new IntArrayList();
- for (int i = 1; i <= 11; i++) {
- list.add(i);
- }
- assertThat(list.trimAndGet()).hasSize(11);
- }
-
-}
PathPattern pattern = PathPattern.create("**/*Foo.java");
assertThat(pattern.toString()).isEqualTo("**/*Foo.java");
- IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java");
- assertThat(pattern.match(indexedFile)).isTrue();
+ IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isTrue();
// case sensitive by default
- indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA");
- assertThat(pattern.match(indexedFile)).isFalse();
+ indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse();
- indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java");
- assertThat(pattern.match(indexedFile)).isFalse();
+ indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse();
}
@Test
public void match_relative_path_and_insensitive_file_extension() throws Exception {
PathPattern pattern = PathPattern.create("**/*Foo.java");
- IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA");
- assertThat(pattern.match(indexedFile, false)).isTrue();
+ IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isTrue();
- indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java");
- assertThat(pattern.match(indexedFile, false)).isFalse();
+ indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isFalse();
}
@Test
PathPattern pattern = PathPattern.create("file:**/src/main/**Foo.java");
assertThat(pattern.toString()).isEqualTo("file:**/src/main/**Foo.java");
- IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java");
- assertThat(pattern.match(indexedFile)).isTrue();
+ IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.java", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isTrue();
// case sensitive by default
- indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA");
- assertThat(pattern.match(indexedFile)).isFalse();
+ indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse();
- indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java");
- assertThat(pattern.match(indexedFile)).isFalse();
+ indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.java", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())).isFalse();
}
@Test
PathPattern pattern = PathPattern.create("file:**/src/main/**Foo.java");
assertThat(pattern.toString()).isEqualTo("file:**/src/main/**Foo.java");
- IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA");
- assertThat(pattern.match(indexedFile, false)).isTrue();
+ IndexedFile indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/MyFoo.JAVA", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isTrue();
- indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.JAVA");
- assertThat(pattern.match(indexedFile, false)).isFalse();
+ indexedFile = new DefaultIndexedFile("ABCDE", moduleBasePath, "src/main/java/org/Other.JAVA", null);
+ assertThat(pattern.match(indexedFile.absolutePath(), indexedFile.relativePath(), false)).isFalse();
}
@Test
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 org.junit.Test;
+import org.sonar.api.batch.fs.internal.charhandler.IntArrayList;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IntArrayListTest {
+
+ @Test
+ public void addElements() {
+ IntArrayList list = new IntArrayList();
+ assertThat(list.trimAndGet()).isEmpty();
+ list.add(1);
+ list.add(2);
+ assertThat(list.trimAndGet()).containsExactly(1, 2);
+ }
+
+ @Test
+ public void trimIfNeeded() {
+ IntArrayList list = new IntArrayList();
+ list.add(1);
+ list.add(2);
+ assertThat(list.trimAndGet()).isSameAs(list.trimAndGet());
+ }
+
+ @Test
+ public void grow() {
+ // Default capacity is 10
+ IntArrayList list = new IntArrayList();
+ for (int i = 1; i <= 11; i++) {
+ list.add(i);
+ }
+ assertThat(list.trimAndGet()).hasSize(11);
+ }
+
+}
*/
package org.sonar.scanner;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
+import static java.util.stream.Collectors.toMap;
+
+import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.utils.KeyValueFormat.Converter;
import org.sonar.scanner.scan.measure.MeasureCache;
-import static java.util.stream.Collectors.toMap;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
public class DefaultFileLinesContext implements FileLinesContext {
-
private final SensorContext context;
private final InputFile inputFile;
private final MetricFinder metricFinder;
/**
* metric key -> line -> value
*/
- private final Map<String, Map<Integer, Object>> map = Maps.newHashMap();
+ private final Map<String, Map<Integer, Object>> map = new HashMap<>();
public DefaultFileLinesContext(SensorContext context, InputFile inputFile, MetricFinder metricFinder, MeasureCache measureCache) {
this.context = context;
public Integer getIntValue(String metricKey, int line) {
Preconditions.checkNotNull(metricKey);
checkLineRange(line);
-
- Map<Integer, Object> lines = map.get(metricKey);
- if (lines == null) {
- // not in memory, so load
- lines = loadData(metricKey, KeyValueFormat.newIntegerConverter());
- map.put(metricKey, lines);
- }
+ Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newIntegerConverter()));
return (Integer) lines.get(line);
}
public String getStringValue(String metricKey, int line) {
Preconditions.checkNotNull(metricKey);
checkLineRange(line);
-
- Map<Integer, Object> lines = map.get(metricKey);
- if (lines == null) {
- // not in memory, so load
- lines = loadData(metricKey, KeyValueFormat.newStringConverter());
- map.put(metricKey, lines);
- }
+ Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newStringConverter()));
return (String) lines.get(line);
}
- private Map<Integer, Object> getOrCreateLines(String metricKey) {
- Map<Integer, Object> lines = map.get(metricKey);
- if (lines == null) {
- lines = Maps.newHashMap();
- map.put(metricKey, lines);
- }
- return lines;
- }
-
private void setValue(String metricKey, int line, Object value) {
- getOrCreateLines(metricKey).put(line, value);
+ map.computeIfAbsent(metricKey, k -> new HashMap<>())
+ .put(line, value);
}
@Override
import java.util.Date;
import java.util.Optional;
+
import org.picocontainer.Startable;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.ScannerSide;
/**
* @since 6.3
+ *
+ * Immutable after {@link #start()}
*/
@ScannerSide
public class ProjectAnalysisInfo implements Startable {
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+
import org.picocontainer.ComponentLifecycle;
import org.picocontainer.PicoContainer;
import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.utils.TempFolder;
import org.sonar.api.utils.internal.DefaultTempFolder;
private DefaultTempFolder projectTempFolder;
private boolean started = false;
- public TempFolder provide(ProjectReactor projectReactor) {
+ public TempFolder provide(InputModuleHierarchy moduleHierarchy) {
if (projectTempFolder == null) {
- Path workingDir = projectReactor.getRoot().getWorkDir().toPath();
+ Path workingDir = moduleHierarchy.root().getWorkDir().toPath();
Path tempDir = workingDir.normalize().resolve(TMP_NAME);
try {
Files.deleteIfExists(tempDir);
@Override
public void dispose(PicoContainer container) {
- //nothing to do
+ // nothing to do
}
@Override
import java.util.Map;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
/**
* @since 4.0
*/
+@Immutable
public class DefaultAnalysisMode extends AbstractAnalysisMode {
private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class);
*/
package org.sonar.scanner.bootstrap;
+import javax.annotation.concurrent.Immutable;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
+@Immutable
public class GlobalMode extends AbstractAnalysisMode {
private static final Logger LOG = LoggerFactory.getLogger(GlobalMode.class);
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
+
import org.apache.commons.lang.ClassUtils;
import org.sonar.api.batch.CheckProject;
import org.sonar.api.batch.DependedUpon;
|| (org.sonar.api.batch.Sensor.class.equals(type) && ClassUtils.isAssignable(extension.getClass(), Sensor.class)))
&& (matcher == null || matcher.accept(extension));
if (keep && module != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) {
- keep = ((CheckProject) extension).shouldExecuteOnProject(new Project(module.definition()));
+ keep = ((CheckProject) extension).shouldExecuteOnProject(new Project(module));
}
return keep;
}
*/
package org.sonar.scanner.cpd;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
+import static com.google.common.collect.FluentIterable.from;
+
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputComponent;
-import org.sonar.api.config.Configuration;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.duplications.block.Block;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
import org.sonar.scanner.util.ProgressReport;
-import static com.google.common.collect.FluentIterable.from;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
/**
* Runs on the root module, at the end of the project analysis.
private final SonarCpdBlockIndex index;
private final ReportPublisher publisher;
private final InputComponentStore componentStore;
- private final Configuration settings;
private final ProgressReport progressReport;
+ private final CpdSettings settings;
private int count;
private int total;
- public CpdExecutor(Configuration settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) {
+ public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) {
this.settings = settings;
this.index = index;
this.publisher = publisher;
List<CloneGroup> filtered;
if (!"java".equalsIgnoreCase(inputFile.language())) {
- Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(inputFile.language()));
+ int minTokens = settings.getMinimumTokens(inputFile.language());
+ Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(minTokens);
filtered = from(duplications).filter(minimumTokensPredicate).toList();
} else {
filtered = duplications;
saveDuplications(component, filtered);
}
- @VisibleForTesting
- /**
- * Not applicable to Java, as the {@link BlockChunker} that it uses does not record start and end units of each block.
- * Also, it uses statements instead of tokens.
- * @param languageKey
- * @return
- */
- int getMinimumTokens(String languageKey) {
- return settings.getInt("sonar.cpd." + languageKey + ".minimumTokens").orElse(100);
- }
-
@VisibleForTesting
final void saveDuplications(final DefaultInputComponent component, List<CloneGroup> duplications) {
if (duplications.size() > MAX_CLONE_GROUP_PER_FILE) {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.cpd;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
+import org.sonar.api.config.Configuration;
+import org.sonar.duplications.block.BlockChunker;
+
+public class CpdSettings {
+ private final Configuration settings;
+ private final String branch;
+
+ public CpdSettings(Configuration settings, InputModuleHierarchy hierarchy) {
+ this.settings = settings;
+ this.branch = hierarchy.root().getBranch();
+ }
+
+ public boolean isCrossProjectDuplicationEnabled() {
+ return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false)
+ // No cross project duplication for branches
+ && StringUtils.isBlank(branch);
+ }
+
+ /**
+ * Not applicable to Java, as the {@link BlockChunker} that it uses does not record start and end units of each block.
+ * Also, it uses statements instead of tokens.
+ * @param languageKey
+ * @return
+ */
+ int getMinimumTokens(String languageKey) {
+ return settings.getInt("sonar.cpd." + languageKey + ".minimumTokens").orElse(100);
+ }
+}
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Collectors;
-import org.sonar.api.CoreProperties;
+
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.config.Configuration;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.index.CloneIndex;
import org.sonar.duplications.index.PackedMemoryCloneIndex;
import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks;
+import org.sonar.scanner.cpd.CpdSettings;
import org.sonar.scanner.protocol.output.FileStructure;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.report.ReportPublisher;
private static final Logger LOG = Loggers.get(SonarCpdBlockIndex.class);
private final CloneIndex mem = new PackedMemoryCloneIndex();
private final ReportPublisher publisher;
- private final Configuration settings;
// Files already tokenized
private final Set<InputFile> indexedFiles = new HashSet<>();
+ private final CpdSettings settings;
- public SonarCpdBlockIndex(ReportPublisher publisher, Configuration settings) {
+ public SonarCpdBlockIndex(ReportPublisher publisher, CpdSettings settings) {
this.publisher = publisher;
this.settings = settings;
}
public void insert(InputFile inputFile, Collection<Block> blocks) {
- if (isCrossProjectDuplicationEnabled(settings)) {
+ if (settings.isCrossProjectDuplicationEnabled()) {
int id = ((DefaultInputFile) inputFile).batchId();
if (publisher.getWriter().hasComponentData(FileStructure.Domain.CPD_TEXT_BLOCKS, id)) {
throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + inputFile.absolutePath());
return indexedFiles.contains(inputFile);
}
- public static boolean isCrossProjectDuplicationEnabled(Configuration settings) {
- return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false)
- // No cross project duplication for branches
- && !settings.get(CoreProperties.PROJECT_BRANCH_PROPERTY).isPresent();
- }
-
public Collection<Block> getByInputFile(String resourceKey) {
return mem.getByResourceId(resourceKey);
}
if (component == null) {
throw new IllegalStateException("Invalid component key: " + key);
}
- if (sensorStorage.isDeprecatedMetric(measure.getMetricKey())) {
+ if (DefaultSensorStorage.isDeprecatedMetric(measure.getMetricKey())) {
// Ignore deprecated metrics
return measure;
}
} else if (inputComponent instanceof InputFile) {
r = File.create(((InputFile) inputComponent).relativePath());
} else if (inputComponent instanceof InputModule) {
- r = new Project(((DefaultInputModule) inputComponent).definition());
+ r = new Project(((DefaultInputModule) inputComponent));
} else {
throw new IllegalArgumentException("Unknow input path type: " + inputComponent);
}
package org.sonar.scanner.issue;
import java.util.Date;
+
+import javax.annotation.concurrent.ThreadSafe;
+
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.batch.fs.InputModule;
import org.sonar.scanner.ProjectAnalysisInfo;
import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+@ThreadSafe
public class DefaultFilterableIssue implements FilterableIssue {
private final Issue rawIssue;
private final ProjectAnalysisInfo projectAnalysisInfo;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.scan.issue.filter.FilterableIssue;
import org.sonar.api.scan.issue.filter.IssueFilter;
import org.sonar.api.scan.issue.filter.IssueFilterChain;
+@ThreadSafe
public class DefaultIssueFilterChain implements IssueFilterChain {
private final List<IssueFilter> filters;
@ScannerSide
public class IssueFilters {
- private final IssueFilter[] filters;
+ private final IssueFilterChain filterChain;
private final org.sonar.api.issue.batch.IssueFilter[] deprecatedFilters;
private final DefaultInputModule module;
private final ProjectAnalysisInfo projectAnalysisInfo;
public IssueFilters(DefaultInputModule module, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] exclusionFilters, org.sonar.api.issue.batch.IssueFilter[] filters) {
this.module = module;
- this.filters = exclusionFilters;
+ this.filterChain = new DefaultIssueFilterChain(exclusionFilters);
this.deprecatedFilters = filters;
this.projectAnalysisInfo = projectAnalysisInfo;
}
}
public boolean accept(String componentKey, ScannerReport.Issue rawIssue) {
- IssueFilterChain filterChain = new DefaultIssueFilterChain(filters);
FilterableIssue fIssue = new DefaultFilterableIssue(module, projectAnalysisInfo, rawIssue, componentKey);
if (filterChain.accept(fIssue)) {
return acceptDeprecated(componentKey, rawIssue);
*/
package org.sonar.scanner.issue;
-import com.google.common.base.Strings;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.fs.internal.DefaultInputComponent;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation;
import org.sonar.scanner.report.ReportPublisher;
+import com.google.common.base.Strings;
+
/**
* Initialize the issues raised during scan.
*/
+@ThreadSafe
public class ModuleIssues {
private final ActiveRules activeRules;
return false;
}
- String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? rule.name() : issue.primaryLocation().message();
+ ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.batchId(), rule.name(), activeRule.severity());
+
+ if (filters.accept(inputComponent.key(), rawIssue)) {
+ write(inputComponent.batchId(), rawIssue);
+ return true;
+ }
+ return false;
+ }
+
+ private static ScannerReport.Issue createReportIssue(Issue issue, int batchId, String ruleName, String activeRuleSeverity) {
+ String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? ruleName : issue.primaryLocation().message();
org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity();
- Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRule.severity());
+ Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRuleSeverity);
ScannerReport.Issue.Builder builder = ScannerReport.Issue.newBuilder();
ScannerReport.IssueLocation.Builder locationBuilder = IssueLocation.newBuilder();
builder.setMsg(primaryMessage);
locationBuilder.setMsg(primaryMessage);
- locationBuilder.setComponentRef(inputComponent.batchId());
+ locationBuilder.setComponentRef(batchId);
TextRange primaryTextRange = issue.primaryLocation().textRange();
if (primaryTextRange != null) {
builder.setTextRange(toProtobufTextRange(textRangeBuilder, primaryTextRange));
builder.setGap(gap);
}
applyFlows(builder, locationBuilder, textRangeBuilder, issue);
- ScannerReport.Issue rawIssue = builder.build();
-
- if (filters.accept(inputComponent.key(), rawIssue)) {
- write(inputComponent.batchId(), rawIssue);
- return true;
- }
- return false;
+ return builder.build();
}
private static void applyFlows(ScannerReport.Issue.Builder builder, ScannerReport.IssueLocation.Builder locationBuilder,
*/
package org.sonar.scanner.issue.ignore;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.scanner.issue.ignore.pattern.IssuePattern;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
+@ThreadSafe
public class EnforceIssuesFilter implements IssueFilter {
private static final Logger LOG = LoggerFactory.getLogger(EnforceIssuesFilter.class);
- private IssueInclusionPatternInitializer patternInitializer;
- private InputComponentStore componentStore;
+ private final List<IssuePattern> multicriteriaPatterns;
+ private final InputComponentStore componentStore;
public EnforceIssuesFilter(IssueInclusionPatternInitializer patternInitializer, InputComponentStore componentStore) {
- this.patternInitializer = patternInitializer;
+ this.multicriteriaPatterns = Collections.unmodifiableList(new ArrayList<>(patternInitializer.getMulticriteriaPatterns()));
this.componentStore = componentStore;
}
boolean atLeastOnePatternFullyMatched = false;
IssuePattern matchingPattern = null;
- for (IssuePattern pattern : patternInitializer.getMulticriteriaPatterns()) {
+ for (IssuePattern pattern : multicriteriaPatterns) {
if (pattern.getRulePattern().match(issue.ruleKey().toString())) {
atLeastOneRuleMatched = true;
String relativePath = getRelativePath(issue.componentKey());
import java.util.LinkedHashSet;
import java.util.Set;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.WildcardPattern;
+@Immutable
public class IssuePattern {
private final WildcardPattern resourcePattern;
private final WildcardPattern rulePattern;
- private final Set<Integer> lines = new LinkedHashSet<>();
- private final Set<LineRange> lineRanges = new LinkedHashSet<>();
+ private final Set<Integer> lines;
+ private final Set<LineRange> lineRanges;
private final boolean checkLines;
public IssuePattern(String resourcePattern, String rulePattern) {
this.resourcePattern = WildcardPattern.create(resourcePattern);
this.rulePattern = WildcardPattern.create(rulePattern);
this.checkLines = !lineRanges.isEmpty();
+ Set<Integer> modifiableLines = new LinkedHashSet<>();
+ Set<LineRange> modifiableLineRanges = new LinkedHashSet<>();
+
for (LineRange range : lineRanges) {
if (range.from() == range.to()) {
- this.lines.add(range.from());
+ modifiableLines.add(range.from());
} else {
- this.lineRanges.add(range);
+ modifiableLineRanges.add(range);
}
}
+
+ this.lines = Collections.unmodifiableSet(modifiableLines);
+ this.lineRanges = Collections.unmodifiableSet(modifiableLineRanges);
}
public WildcardPattern getResourcePattern() {
import java.util.List;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.fs.internal.FileMetadata.CharHandler;
+import org.sonar.api.batch.fs.internal.charhandler.CharHandler;
import org.sonar.scanner.issue.ignore.pattern.BlockIssuePattern;
import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer;
import org.sonar.scanner.issue.ignore.pattern.IssuePattern;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.fs.internal.FileMetadata.CharHandler;
+import org.sonar.api.batch.fs.internal.charhandler.CharHandler;
import org.sonar.scanner.issue.ignore.pattern.LineRange;
import org.sonar.scanner.issue.ignore.pattern.PatternMatcher;
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader.DoubleRegexpMatcher;
}
@Override
- protected void handleIgnoreEoL(char c) {
+ public void handleIgnoreEoL(char c) {
sb.append(c);
}
@Override
- protected void newLine() {
+ public void newLine() {
processLine(sb.toString());
sb.setLength(0);
lineIndex++;
}
@Override
- protected void eof() {
+ public void eof() {
processLine(sb.toString());
if (currentMatcher != null && !currentMatcher.hasSecondPattern()) {
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputComponent;
+import org.sonar.api.batch.fs.InputModule;
import org.sonar.api.batch.fs.internal.DefaultInputComponent;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
import org.sonar.core.component.ComponentKeys;
import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue;
import org.sonar.scanner.repository.ServerIssuesLoader;
-import org.sonar.scanner.scan.ImmutableProjectReactor;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
import org.sonar.scanner.storage.Storage;
import org.sonar.scanner.storage.Storages;
private final Storages caches;
private Storage<ServerIssue> issuesCache;
private final ServerIssuesLoader previousIssuesLoader;
- private final ImmutableProjectReactor reactor;
- private final InputComponentStore resourceCache;
+ private final InputComponentStore componentStore;
- public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, ImmutableProjectReactor reactor, InputComponentStore resourceCache) {
+ public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, InputComponentStore componentStore) {
this.caches = caches;
this.previousIssuesLoader = previousIssuesLoader;
- this.reactor = reactor;
- this.resourceCache = resourceCache;
+ this.componentStore = componentStore;
}
public void load() {
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
this.issuesCache = caches.createCache("previousIssues");
caches.registerValueCoder(ServerIssue.class, new ServerIssueValueCoder());
- previousIssuesLoader.load(reactor.getRoot().getKeyWithBranch(), this::store);
+ DefaultInputModule root = (DefaultInputModule) componentStore.root();
+ previousIssuesLoader.load(root.getKeyWithBranch(), this::store);
profiler.stopInfo();
}
private void store(ServerIssue issue) {
String moduleKeyWithBranch = issue.getModuleKey();
- ProjectDefinition projectDefinition = reactor.getProjectDefinition(moduleKeyWithBranch);
- if (projectDefinition != null) {
- String componentKeyWithoutBranch = ComponentKeys.createEffectiveKey(projectDefinition.getKey(), issue.hasPath() ? issue.getPath() : null);
- DefaultInputComponent r = (DefaultInputComponent) resourceCache.getByKey(componentKeyWithoutBranch);
+ InputModule module = componentStore.getModule(moduleKeyWithBranch);
+ if (module != null) {
+ String componentKeyWithoutBranch = ComponentKeys.createEffectiveKey(module.key(), issue.hasPath() ? issue.getPath() : null);
+ DefaultInputComponent r = (DefaultInputComponent) componentStore.getByKey(componentKeyWithoutBranch);
if (r != null) {
issuesCache.put(r.batchId(), issue.getKey(), issue);
return;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.scanner.events.BatchStepEvent;
import org.sonar.scanner.events.EventBus;
import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader;
private final DefaultModuleFileSystem fs;
private final QProfileVerifier profileVerifier;
private final IssueExclusionsLoader issueExclusionsLoader;
+ private final InputModuleHierarchy hierarchy;
public AbstractPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
- SensorContext sensorContext, EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
+ SensorContext sensorContext, InputModuleHierarchy hierarchy, EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
IssueExclusionsLoader issueExclusionsLoader) {
this.postJobsExecutor = postJobsExecutor;
this.initializersExecutor = initializersExecutor;
this.fs = fs;
this.profileVerifier = profileVerifier;
this.issueExclusionsLoader = issueExclusionsLoader;
+ this.hierarchy = hierarchy;
}
/**
afterSensors();
- if (module.definition().getParent() == null) {
+ if (hierarchy.isRoot(module)) {
executeOnRoot();
postJobsExecutor.execute(sensorContext);
}
*/
package org.sonar.scanner.phases;
-import com.google.common.collect.Lists;
import java.util.Collection;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.Initializer;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
import org.sonar.scanner.events.EventBus;
+import com.google.common.collect.Lists;
+
public class InitializersExecutor {
private static final Logger LOG = Loggers.get(SensorsExecutor.class);
LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> "));
}
- Project project = new Project(module.definition());
+ Project project = new Project(module);
for (Initializer initializer : initializers) {
eventBus.fireEvent(new InitializerExecutionEvent(initializer, true));
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.scanner.events.BatchStepEvent;
import org.sonar.scanner.events.EventBus;
import org.sonar.scanner.issue.IssueCallback;
public IssuesPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext,
EventBus eventBus, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
- IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback) {
- super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader);
+ IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback, InputModuleHierarchy moduleHierarchy) {
+ super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, moduleHierarchy, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader);
this.eventBus = eventBus;
this.issuesReport = jsonReport;
this.localIssueTracking = localIssueTracking;
import java.util.ArrayList;
import java.util.Collection;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.PostJob;
import org.sonar.api.batch.ScannerSide;
private void execute(SensorContext context, Collection<PostJob> postJobs) {
logPostJobs(postJobs);
- Project project = new Project(module.definition());
+ Project project = new Project(module);
for (PostJob postJob : postJobs) {
LOG.info("Executing post-job {}", ScannerUtils.describe(postJob));
eventBus.fireEvent(new PostJobExecutionEvent(postJob, true));
@Override
public Project getProject() {
- return new Project(module.definition());
+ return new Project(module);
}
@Override
package org.sonar.scanner.phases;
import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.scanner.cpd.CpdExecutor;
import org.sonar.scanner.events.BatchStepEvent;
import org.sonar.scanner.events.EventBus;
private final ScmPublisher scm;
public PublishPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext,
- EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs,
- QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm) {
- super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader);
+ EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
+ IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy) {
+ super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, hierarchy, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader);
this.eventBus = eventBus;
this.reportPublisher = reportPublisher;
this.cpdExecutor = cpdExecutor;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.resources.Project;
import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
import org.sonar.scanner.events.EventBus;
private final SensorStrategy strategy;
private final boolean isRoot;
- public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus, SensorStrategy strategy) {
+ public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, InputModuleHierarchy hierarchy, EventBus eventBus, SensorStrategy strategy) {
this.selector = selector;
this.module = module;
this.eventBus = eventBus;
this.strategy = strategy;
- this.isRoot = module.definition().getParent() == null;
+ this.isRoot = hierarchy.isRoot(module);
}
public void execute(SensorContext context) {
private void executeSensor(SensorContext context, Sensor sensor) {
eventBus.fireEvent(new SensorExecutionEvent(sensor, true));
- sensor.analyse(new Project(module.definition()), context);
+ sensor.analyse(new Project(module), context);
eventBus.fireEvent(new SensorExecutionEvent(sensor, false));
}
}
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
+
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
private final System2 system;
private final ProjectRepositories projectRepos;
private final GlobalConfiguration globalSettings;
+ private final InputModuleHierarchy hierarchy;
private ScannerReportWriter writer;
public AnalysisContextReportPublisher(AnalysisMode mode, ScannerPluginRepository pluginRepo, System2 system,
- ProjectRepositories projectRepos, GlobalConfiguration globalSettings) {
+ ProjectRepositories projectRepos, GlobalConfiguration globalSettings, InputModuleHierarchy hierarchy) {
this.mode = mode;
this.pluginRepo = pluginRepo;
this.system = system;
this.projectRepos = projectRepos;
this.globalSettings = globalSettings;
+ this.hierarchy = hierarchy;
}
public void init(ScannerReportWriter writer) {
}
}
- public void dumpModuleSettings(ProjectDefinition moduleDefinition) {
+ public void dumpModuleSettings(DefaultInputModule module) {
if (mode.isIssues()) {
return;
}
File analysisLog = writer.getFileStructure().analysisLog();
try (BufferedWriter fileWriter = Files.newBufferedWriter(analysisLog.toPath(), StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
- Map<String, String> moduleSpecificProps = collectModuleSpecificProps(moduleDefinition);
- fileWriter.append(String.format("Settings for module: %s", moduleDefinition.getKey())).append('\n');
+ Map<String, String> moduleSpecificProps = collectModuleSpecificProps(module);
+ fileWriter.append(String.format("Settings for module: %s", module.key())).append('\n');
for (String prop : new TreeSet<>(moduleSpecificProps.keySet())) {
if (isSystemProp(prop) || isEnvVariable(prop) || !isSqProp(prop)) {
continue;
/**
* Only keep props that are not in parent
*/
- private Map<String, String> collectModuleSpecificProps(ProjectDefinition moduleDefinition) {
+ private Map<String, String> collectModuleSpecificProps(DefaultInputModule module) {
Map<String, String> moduleSpecificProps = new HashMap<>();
- if (projectRepos.moduleExists(moduleDefinition.getKeyWithBranch())) {
- moduleSpecificProps.putAll(projectRepos.settings(moduleDefinition.getKeyWithBranch()));
+ if (projectRepos.moduleExists(module.getKeyWithBranch())) {
+ moduleSpecificProps.putAll(projectRepos.settings(module.getKeyWithBranch()));
}
- ProjectDefinition parent = moduleDefinition.getParent();
+ DefaultInputModule parent = hierarchy.parent(module);
if (parent == null) {
- moduleSpecificProps.putAll(moduleDefinition.properties());
+ moduleSpecificProps.putAll(module.properties());
} else {
Map<String, String> parentProps = parent.properties();
- for (Map.Entry<String, String> entry : moduleDefinition.properties().entrySet()) {
+ for (Map.Entry<String, String> entry : module.properties().entrySet()) {
if (!parentProps.containsKey(entry.getKey()) || !parentProps.get(entry.getKey()).equals(entry.getValue())) {
moduleSpecificProps.put(entry.getKey(), entry.getValue());
}
import java.util.Collection;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.config.Configuration;
import org.sonar.scanner.ProjectAnalysisInfo;
-import org.sonar.scanner.cpd.index.SonarCpdBlockIndex;
+import org.sonar.scanner.cpd.CpdSettings;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
import org.sonar.scanner.rule.ModuleQProfiles;
private final ModuleQProfiles qProfiles;
private final ProjectAnalysisInfo projectAnalysisInfo;
private final InputModuleHierarchy moduleHierarchy;
+ private final CpdSettings cpdSettings;
- public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Configuration settings, ModuleQProfiles qProfiles) {
+ public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Configuration settings,
+ ModuleQProfiles qProfiles, CpdSettings cpdSettings) {
this.projectAnalysisInfo = projectAnalysisInfo;
this.moduleHierarchy = moduleHierarchy;
this.settings = settings;
this.qProfiles = qProfiles;
+ this.cpdSettings = cpdSettings;
}
@Override
.setAnalysisDate(projectAnalysisInfo.analysisDate().getTime())
// Here we want key without branch
.setProjectKey(rootDef.getKey())
- .setCrossProjectDuplicationActivated(SonarCpdBlockIndex.isCrossProjectDuplicationEnabled(settings))
+ .setCrossProjectDuplicationActivated(cpdSettings.isCrossProjectDuplicationEnabled())
.setRootComponentRef(rootProject.batchId());
settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(builder::setOrganizationKey);
*/
package org.sonar.scanner.report;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Throwables;
+import static org.sonar.core.util.FileUtils.deleteQuietly;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
+
import javax.annotation.Nullable;
-import okhttp3.HttpUrl;
+
import org.apache.commons.io.FileUtils;
import org.picocontainer.Startable;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.config.Configuration;
import org.sonar.api.platform.Server;
import org.sonar.api.utils.MessageException;
import org.sonar.scanner.analysis.DefaultAnalysisMode;
import org.sonar.scanner.bootstrap.ScannerWsClient;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
-import org.sonar.scanner.scan.ImmutableProjectReactor;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.WsCe;
import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsResponse;
-import static org.sonar.core.util.FileUtils.deleteQuietly;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Throwables;
+
+import okhttp3.HttpUrl;
@ScannerSide
public class ReportPublisher implements Startable {
private final Configuration settings;
private final ScannerWsClient wsClient;
private final AnalysisContextReportPublisher contextPublisher;
- private final ImmutableProjectReactor projectReactor;
+ private final InputModuleHierarchy moduleHierarchy;
private final DefaultAnalysisMode analysisMode;
private final TempFolder temp;
private final ReportPublisherStep[] publishers;
private ScannerReportWriter writer;
public ReportPublisher(Configuration settings, ScannerWsClient wsClient, Server server, AnalysisContextReportPublisher contextPublisher,
- ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) {
+ InputModuleHierarchy moduleHierarchy, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) {
this.settings = settings;
this.wsClient = wsClient;
this.server = server;
this.contextPublisher = contextPublisher;
- this.projectReactor = projectReactor;
+ this.moduleHierarchy = moduleHierarchy;
this.analysisMode = analysisMode;
this.temp = temp;
this.publishers = publishers;
@Override
public void start() {
- reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report");
+ reportDir = new File(moduleHierarchy.root().getWorkDir(), "batch-report");
writer = new ScannerReportWriter(reportDir);
contextPublisher.init(writer);
String upload(File report) {
LOG.debug("Upload report");
long startTime = System.currentTimeMillis();
- ProjectDefinition projectDefinition = projectReactor.getRoot();
PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, report);
PostRequest post = new PostRequest("api/ce/submit")
.setMediaType(MediaTypes.PROTOBUF)
.setParam("organization", settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).orElse(null))
- .setParam("projectKey", projectDefinition.getKey())
- .setParam("projectName", projectDefinition.getOriginalName())
- .setParam("projectBranch", projectDefinition.getBranch())
+ .setParam("projectKey", moduleHierarchy.root().key())
+ .setParam("projectName", moduleHierarchy.root().getOriginalName())
+ .setParam("projectBranch", moduleHierarchy.root().getBranch())
.setPart("report", filePart);
WsResponse response;
HttpUrl httpUrl = HttpUrl.parse(publicUrl);
Map<String, String> metadata = new LinkedHashMap<>();
- String effectiveKey = projectReactor.getRoot().getKeyWithBranch();
+ String effectiveKey = moduleHierarchy.root().getKeyWithBranch();
settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(org -> metadata.put("organization", org));
metadata.put("projectKey", effectiveKey);
metadata.put("serverUrl", publicUrl);
}
private void dumpMetadata(Map<String, String> metadata) {
- Path file = projectReactor.getRoot().getWorkDir().toPath().resolve(METADATA_DUMP_FILENAME);
+ Path file = moduleHierarchy.root().getWorkDir().toPath().resolve(METADATA_DUMP_FILENAME);
try (Writer output = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
for (Map.Entry<String, String> entry : metadata.entrySet()) {
output.write(entry.getKey());
*/
package org.sonar.scanner.repository;
+import static com.google.common.base.Preconditions.checkArgument;
+
import java.util.HashMap;
import java.util.Map;
-import org.sonar.api.batch.ScannerSide;
-import static com.google.common.base.Preconditions.checkArgument;
+import org.sonar.api.batch.ScannerSide;
@ScannerSide
public class ContextPropertiesCache {
*/
package org.sonar.scanner.repository;
-import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import java.util.Date;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+@Immutable
public class ProjectRepositories {
- private final Table<String, String, String> settingsByModule;
- private final Table<String, String, FileData> fileDataByModuleAndPath;
+ private final ImmutableTable<String, String, String> settingsByModule;
+ private final ImmutableTable<String, String, FileData> fileDataByModuleAndPath;
private final Date lastAnalysisDate;
private final boolean exists;
public ProjectRepositories() {
this.exists = false;
- this.settingsByModule = HashBasedTable.create();
- this.fileDataByModuleAndPath = HashBasedTable.create();
+ this.settingsByModule = new ImmutableTable.Builder<String, String, String>().build();
+ this.fileDataByModuleAndPath = new ImmutableTable.Builder<String, String, FileData>().build();
this.lastAnalysisDate = null;
}
public ProjectRepositories(Table<String, String, String> settingsByModule, Table<String, String, FileData> fileDataByModuleAndPath,
@Nullable Date lastAnalysisDate) {
- this.settingsByModule = settingsByModule;
- this.fileDataByModuleAndPath = fileDataByModuleAndPath;
+ this.settingsByModule = ImmutableTable.copyOf(settingsByModule);
+ this.fileDataByModuleAndPath = ImmutableTable.copyOf(fileDataByModuleAndPath);
this.lastAnalysisDate = lastAnalysisDate;
this.exists = true;
}
import java.util.ArrayList;
import java.util.Collection;
+
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.picocontainer.Startable;
import org.sonar.api.resources.Languages;
* Languages repository using {@link Languages}
* @since 4.4
*/
+@Immutable
public class DefaultLanguagesRepository implements LanguagesRepository, Startable {
private Languages languages;
import java.util.Arrays;
import java.util.Collection;
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
public final class Language {
private final String key;
package org.sonar.scanner.repository.language;
import java.util.Collection;
+
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.ScannerSide;
/**
* @since 4.4
*/
@ScannerSide
+@Immutable
public interface LanguagesRepository {
/**
*/
package org.sonar.scanner.rule;
+import org.sonar.api.utils.DateUtils;
+
+import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
+import org.sonar.api.batch.ScannerSide;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import javax.annotation.CheckForNull;
-import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.utils.DateUtils;
-import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile;
/**
* Lists the Quality profiles enabled on the current module.
*/
@ScannerSide
+@Immutable
public class ModuleQProfiles {
public static final String SONAR_PROFILE_PROP = "sonar.profile";
for (QualityProfile qProfile : profiles) {
map.put(qProfile.getLanguage(),
- new QProfile()
+ new QProfile.Builder()
.setKey(qProfile.getKey())
.setName(qProfile.getName())
.setLanguage(qProfile.getLanguage())
- .setRulesUpdatedAt(DateUtils.parseDateTime(qProfile.getRulesUpdatedAt())));
+ .setRulesUpdatedAt(DateUtils.parseDateTime(qProfile.getRulesUpdatedAt())).build());
}
byLanguage = Collections.unmodifiableMap(map);
}
import com.google.common.base.MoreObjects;
import java.util.Date;
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
public class QProfile {
+ private final String key;
+ private final String name;
+ private final String language;
+ private final Date rulesUpdatedAt;
- private String key;
- private String name;
- private String language;
- private Date rulesUpdatedAt;
+ public QProfile(String key, String name, String language, Date rulesUpdatedAt) {
+ this.key = key;
+ this.name = name;
+ this.language = language;
+ this.rulesUpdatedAt = rulesUpdatedAt;
+ }
public String getKey() {
return key;
}
- public QProfile setKey(String key) {
- this.key = key;
- return this;
- }
-
public String getName() {
return name;
}
- public QProfile setName(String name) {
- this.name = name;
- return this;
- }
-
public String getLanguage() {
return language;
}
- public QProfile setLanguage(String language) {
- this.language = language;
- return this;
- }
-
public Date getRulesUpdatedAt() {
return rulesUpdatedAt;
}
- public QProfile setRulesUpdatedAt(Date d) {
- this.rulesUpdatedAt = d;
- return this;
- }
-
@Override
public boolean equals(Object o) {
if (this == o) {
.add("rulesUpdatedAt", rulesUpdatedAt)
.toString();
}
+
+ public static class Builder {
+ private String key;
+ private String name;
+ private String language;
+ private Date rulesUpdatedAt;
+
+ public String getKey() {
+ return key;
+ }
+
+ public Builder setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public Builder setLanguage(String language) {
+ this.language = language;
+ return this;
+ }
+
+ public Date getRulesUpdatedAt() {
+ return rulesUpdatedAt;
+ }
+
+ public Builder setRulesUpdatedAt(Date d) {
+ this.rulesUpdatedAt = d;
+ return this;
+ }
+
+ public QProfile build() {
+ return new QProfile(key, name, language, rulesUpdatedAt);
+ }
+ }
}
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.batch.rule.Rules;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.rules.RuleQuery;
+@Immutable
public class RuleFinderCompatibility implements RuleFinder {
private final Rules rules;
*/
package org.sonar.scanner.scan;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+
import javax.annotation.CheckForNull;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.api.batch.fs.InputModule;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.scan.filesystem.PathResolver;
+import com.google.common.collect.ImmutableMultimap;
+
+@Immutable
public class DefaultInputModuleHierarchy implements InputModuleHierarchy {
private final PathResolver pathResolver = new PathResolver();
- private DefaultInputModule root;
- private final Map<DefaultInputModule, DefaultInputModule> parents = new HashMap<>();
- private final Multimap<DefaultInputModule, DefaultInputModule> children = HashMultimap.create();
+ private final DefaultInputModule root;
+ private final Map<DefaultInputModule, DefaultInputModule> parents;
+ private final ImmutableMultimap<DefaultInputModule, DefaultInputModule> children;
- public void setRoot(DefaultInputModule root) {
+ public DefaultInputModuleHierarchy(DefaultInputModule parent, DefaultInputModule child) {
+ this(Collections.singletonMap(child, parent));
+ }
+
+ public DefaultInputModuleHierarchy(DefaultInputModule root) {
+ this.children = new ImmutableMultimap.Builder<DefaultInputModule, DefaultInputModule>().build();
+ this.parents = Collections.emptyMap();
this.root = root;
}
- public void index(DefaultInputModule child, DefaultInputModule parent) {
- Preconditions.checkNotNull(child);
- Preconditions.checkNotNull(parent);
- parents.put(child, parent);
- children.put(parent, child);
+ /**
+ * Map of child->parent. Neither the Keys or values can be null.
+ */
+ public DefaultInputModuleHierarchy(Map<DefaultInputModule, DefaultInputModule> parents) {
+ ImmutableMultimap.Builder<DefaultInputModule, DefaultInputModule> childrenBuilder = new ImmutableMultimap.Builder<>();
+
+ for (Map.Entry<DefaultInputModule, DefaultInputModule> e : parents.entrySet()) {
+ childrenBuilder.put(e.getValue(), e.getKey());
+ }
+
+ this.children = childrenBuilder.build();
+ this.parents = Collections.unmodifiableMap(new HashMap<>(parents));
+ this.root = findRoot(parents);
+ }
+
+ private static DefaultInputModule findRoot(Map<DefaultInputModule, DefaultInputModule> parents) {
+ DefaultInputModule r = null;
+ for (DefaultInputModule parent : parents.values()) {
+ if (!parents.containsKey(parent)) {
+ if (r != null && r != parent) {
+ throw new IllegalStateException(String.format("Found two modules without parent: '%s' and '%s'", r.key(), parent.key()));
+ }
+ r = parent;
+ }
+ }
+ if (r == null) {
+ throw new IllegalStateException("Found no root module");
+ }
+ return r;
}
@Override
return null;
}
DefaultInputModule inputModule = (DefaultInputModule) module;
-
- ProjectDefinition parentDefinition = parent.definition();
- Path parentBaseDir = parentDefinition.getBaseDir().toPath();
- ProjectDefinition moduleDefinition = inputModule.definition();
- Path moduleBaseDir = moduleDefinition.getBaseDir().toPath();
+ Path parentBaseDir = parent.getBaseDir().toPath();
+ Path moduleBaseDir = inputModule.getBaseDir().toPath();
return pathResolver.relativePath(parentBaseDir, moduleBaseDir);
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.scan;
-
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import javax.annotation.CheckForNull;
-import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-
-/**
- * Immutable copy of project reactor after all modifications have been applied (see {@link ImmutableProjectReactorProvider}).
- */
-@ScannerSide
-public class ImmutableProjectReactor {
-
- private ProjectDefinition root;
- private Map<String, ProjectDefinition> byKey = new LinkedHashMap<>();
-
- public ImmutableProjectReactor(ProjectDefinition root) {
- if (root.getParent() != null) {
- throw new IllegalArgumentException("Not a root project: " + root);
- }
- this.root = root;
- collectProjects(root);
- }
-
- public Collection<ProjectDefinition> getProjects() {
- return byKey.values();
- }
-
- /**
- * Populates list of projects from hierarchy.
- */
- private void collectProjects(ProjectDefinition def) {
- if (byKey.containsKey(def.getKeyWithBranch())) {
- throw new IllegalStateException("Duplicate module key in reactor: " + def.getKeyWithBranch());
- }
- byKey.put(def.getKeyWithBranch(), def);
- for (ProjectDefinition child : def.getSubProjects()) {
- collectProjects(child);
- }
- }
-
- public ProjectDefinition getRoot() {
- return root;
- }
-
- @CheckForNull
- public ProjectDefinition getProjectDefinition(String keyWithBranch) {
- return byKey.get(keyWithBranch);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.scan;
-
-import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-
-public class ImmutableProjectReactorProvider extends ProviderAdapter {
-
- private ImmutableProjectReactor singleton;
-
- public ImmutableProjectReactor provide(ProjectReactor reactor, ProjectBuildersExecutor projectBuildersExecutor, ProjectReactorValidator validator) {
- if (singleton == null) {
- // 1 Apply project builders
- projectBuildersExecutor.execute(reactor);
-
- // 2 Validate final reactor
- validator.validate(reactor);
-
- singleton = new ImmutableProjectReactor(reactor.getRoot());
- }
- return singleton;
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.scan;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.scanner.scan.filesystem.BatchIdGenerator;
+
+public class InputModuleHierarchyProvider extends ProviderAdapter {
+
+ private DefaultInputModuleHierarchy hierarchy = null;
+
+ public DefaultInputModuleHierarchy provide(ProjectBuildersExecutor projectBuildersExecutor, ProjectReactorValidator validator,
+ ProjectReactor projectReactor, BatchIdGenerator batchIdGenerator) {
+ if (hierarchy == null) {
+ // 1 Apply project builders
+ projectBuildersExecutor.execute(projectReactor);
+
+ // 2 Validate final reactor
+ validator.validate(projectReactor);
+
+ // 3 Create modules and the hierarchy
+ DefaultInputModule root = new DefaultInputModule(projectReactor.getRoot(), batchIdGenerator.get());
+ Map<DefaultInputModule, DefaultInputModule> parents = createChildren(root, batchIdGenerator, new HashMap<>());
+ if (parents.isEmpty()) {
+ hierarchy = new DefaultInputModuleHierarchy(root);
+ } else {
+ hierarchy = new DefaultInputModuleHierarchy(parents);
+ }
+ }
+ return hierarchy;
+ }
+
+ private static Map<DefaultInputModule, DefaultInputModule> createChildren(DefaultInputModule parent, BatchIdGenerator batchIdGenerator,
+ Map<DefaultInputModule, DefaultInputModule> parents) {
+ for (ProjectDefinition def : parent.definition().getSubProjects()) {
+ DefaultInputModule child = new DefaultInputModule(def, batchIdGenerator.get());
+ parents.put(child, parent);
+ createChildren(child, batchIdGenerator, parents);
+ }
+ return parents;
+ }
+}
package org.sonar.scanner.scan;
import org.picocontainer.Startable;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import org.sonar.scanner.scan.filesystem.BatchIdGenerator;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
/**
* project definitions provided by the {@link ImmutableProjectReactor}.
*/
public class ModuleIndexer implements Startable {
- private final ImmutableProjectReactor projectReactor;
private final DefaultComponentTree componentTree;
- private final DefaultInputModuleHierarchy moduleHierarchy;
- private final BatchIdGenerator batchIdGenerator;
+ private final InputModuleHierarchy moduleHierarchy;
private final InputComponentStore componentStore;
- public ModuleIndexer(ImmutableProjectReactor projectReactor, DefaultComponentTree componentTree,
- InputComponentStore componentStore, BatchIdGenerator batchIdGenerator, DefaultInputModuleHierarchy moduleHierarchy) {
- this.projectReactor = projectReactor;
+ public ModuleIndexer(DefaultComponentTree componentTree, InputComponentStore componentStore, InputModuleHierarchy moduleHierarchy) {
this.componentTree = componentTree;
this.componentStore = componentStore;
this.moduleHierarchy = moduleHierarchy;
- this.batchIdGenerator = batchIdGenerator;
}
@Override
public void start() {
- DefaultInputModule root = new DefaultInputModule(projectReactor.getRoot(), batchIdGenerator.get());
- moduleHierarchy.setRoot(root);
- componentStore.put(root);
- createChildren(root);
+ DefaultInputModule root = moduleHierarchy.root();
+ indexChildren(root);
}
- private void createChildren(DefaultInputModule parent) {
- for (ProjectDefinition def : parent.definition().getSubProjects()) {
- DefaultInputModule child = new DefaultInputModule(def, batchIdGenerator.get());
- moduleHierarchy.index(child, parent);
- componentTree.index(child, parent);
- componentStore.put(child);
- createChildren(child);
+ private void indexChildren(DefaultInputModule parent) {
+ for (DefaultInputModule module : moduleHierarchy.children(parent)) {
+ componentTree.index(module, parent);
+ componentStore.put(module);
+ indexChildren(module);
}
}
add(
module.definition(),
// still injected by some plugins
- new Project(module.definition()),
+ new Project(module),
module,
MutableModuleSettings.class,
new ModuleSettingsProvider());
import org.picocontainer.injectors.ProviderAdapter;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.scanner.bootstrap.GlobalConfiguration;
import org.sonar.scanner.report.AnalysisContextReportPublisher;
import org.sonar.scanner.repository.ProjectRepositories;
private ModuleSettings projectSettings;
- public ModuleSettings provide(GlobalConfiguration globalSettings, ProjectDefinition moduleDefinition, ProjectRepositories projectRepos,
+ public ModuleSettings provide(GlobalConfiguration globalSettings, DefaultInputModule module, ProjectRepositories projectRepos,
AnalysisMode analysisMode, AnalysisContextReportPublisher contextReportPublisher) {
if (projectSettings == null) {
Map<String, String> settings = new LinkedHashMap<>();
settings.putAll(globalSettings.getProperties());
- settings.putAll(addServerSidePropertiesIfModuleExists(projectRepos, moduleDefinition));
- addScannerSideProperties(settings, moduleDefinition);
- contextReportPublisher.dumpModuleSettings(moduleDefinition);
+ settings.putAll(addServerSidePropertiesIfModuleExists(projectRepos, module.definition()));
+ addScannerSideProperties(settings, module.definition());
+ contextReportPublisher.dumpModuleSettings(module);
projectSettings = new ModuleSettings(globalSettings.getDefinitions(), globalSettings.getEncryption(), analysisMode, settings);
}
import java.nio.channels.OverlappingFileLockException;
import java.nio.file.Files;
import java.nio.file.Path;
+
import org.picocontainer.Startable;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.home.cache.DirectoryLock;
import org.sonar.scanner.bootstrap.Slf4jLogger;
public class ProjectLock implements Startable {
private final DirectoryLock lock;
- public ProjectLock(ProjectReactor projectReactor) {
- Path directory = projectReactor.getRoot().getWorkDir().toPath();
+ public ProjectLock(InputModuleHierarchy moduleHierarchy) {
+ Path directory = moduleHierarchy.root().getWorkDir().toPath();
try {
if (!directory.toFile().exists()) {
Files.createDirectories(directory);
*/
package org.sonar.scanner.scan;
-import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.List;
+
import javax.annotation.Nullable;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.config.Settings;
import org.sonar.api.utils.MessageException;
import org.sonar.core.component.ComponentKeys;
import org.sonar.scanner.analysis.DefaultAnalysisMode;
+import com.google.common.base.Joiner;
+
/**
* This class aims at validating project reactor
* @since 3.6
*/
public class ProjectReactorValidator {
-
- private static final String SONAR_PHASE = "sonar.phase";
- private final Settings settings;
private final DefaultAnalysisMode mode;
- public ProjectReactorValidator(Settings settings, DefaultAnalysisMode mode) {
- this.settings = settings;
+ public ProjectReactorValidator(DefaultAnalysisMode mode) {
this.mode = mode;
}
String branch = reactor.getRoot().getBranch();
List<String> validationMessages = new ArrayList<>();
- checkDeprecatedProperties(validationMessages);
for (ProjectDefinition moduleDef : reactor.getProjects()) {
if (mode.isIssues()) {
}
}
- private void checkDeprecatedProperties(List<String> validationMessages) {
- if (settings.getString(SONAR_PHASE) != null) {
- validationMessages.add(String.format("Property \"%s\" is deprecated. Please remove it from your configuration.", SONAR_PHASE));
- }
- }
-
private static void validateBranch(List<String> validationMessages, @Nullable String branch) {
if (StringUtils.isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) {
validationMessages.add(String.format("\"%s\" is not a valid branch name. "
*/
package org.sonar.scanner.scan;
-import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.scanner.bootstrap.ExtensionUtils;
import org.sonar.scanner.bootstrap.MetricProvider;
import org.sonar.scanner.cpd.CpdExecutor;
+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.rule.RulesLoader;
import org.sonar.scanner.rule.RulesProvider;
import org.sonar.scanner.scan.filesystem.BatchIdGenerator;
-import org.sonar.scanner.scan.filesystem.InputComponentStore;
+import org.sonar.scanner.scan.filesystem.InputComponentStoreProvider;
import org.sonar.scanner.scan.measure.DefaultMetricFinder;
import org.sonar.scanner.scan.measure.DeprecatedMetricFinder;
import org.sonar.scanner.scan.measure.MeasureCache;
import org.sonar.scanner.storage.Storages;
+import com.google.common.annotations.VisibleForTesting;
+
public class ProjectScanContainer extends ComponentContainer {
private static final Logger LOG = Loggers.get(ProjectScanContainer.class);
@Override
protected void doBeforeStart() {
addBatchComponents();
+ addBatchExtensions();
ProjectLock lock = getComponentByType(ProjectLock.class);
lock.tryLock();
getComponentByType(WorkDirectoryCleaner.class).execute();
- addBatchExtensions();
Settings settings = getComponentByType(Settings.class);
if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) {
add(PhasesSumUpTimeProfiler.class);
ProjectReactorBuilder.class,
WorkDirectoryCleaner.class,
new MutableProjectReactorProvider(),
- new ImmutableProjectReactorProvider(),
ProjectBuildersExecutor.class,
ProjectLock.class,
EventBus.class,
// file system
ModuleIndexer.class,
- InputComponentStore.class,
+ new InputComponentStoreProvider(),
PathResolver.class,
- DefaultInputModuleHierarchy.class,
+ new InputModuleHierarchyProvider(),
DefaultComponentTree.class,
BatchIdGenerator.class,
// Cpd
CpdExecutor.class,
+ CpdSettings.class,
SonarCpdBlockIndex.class,
ScanTaskObservers.class);
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.core.util.FileUtils;
import org.sonar.home.cache.DirectoryLock;
public class WorkDirectoryCleaner {
- private Path workDir;
+ private final Path workDir;
- public WorkDirectoryCleaner(ProjectReactor projectReactor) {
- workDir = projectReactor.getRoot().getWorkDir().toPath();
+ public WorkDirectoryCleaner(InputModuleHierarchy moduleHierarchy) {
+ workDir = moduleHierarchy.root().getWorkDir().toPath();
}
public void execute() {
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
+
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.batch.fs.InputComponent;
/**
* The IDs must be unique among all types of components and for all modules in the project.
* The ID should never be 0, as it is sometimes used to indicate invalid components.
*/
+@ThreadSafe
public class BatchIdGenerator implements Supplier<Integer> {
private AtomicInteger nextBatchId = new AtomicInteger(1);
if (inclusionPatterns.length > 0) {
boolean matchInclusion = false;
for (PathPattern pattern : inclusionPatterns) {
- matchInclusion |= pattern.match(indexedFile);
+ matchInclusion |= pattern.match(indexedFile.absolutePath(), indexedFile.relativePath());
}
if (!matchInclusion) {
return false;
}
if (exclusionPatterns.length > 0) {
for (PathPattern pattern : exclusionPatterns) {
- if (pattern.match(indexedFile)) {
+ if (pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())) {
return false;
}
}
DefaultInputFile inputFile = inputFileBuilder.create(realFile, type, fileSystem.encoding());
if (inputFile != null) {
if (exclusionFilters.accept(inputFile, type) && accept(inputFile)) {
+ String parentRelativePath = getParentRelativePath(fileSystem, inputFile);
synchronized (this) {
fileSystem.add(inputFile);
- indexParentDir(fileSystem, inputFile);
+ indexParentDir(fileSystem, inputFile, parentRelativePath);
progress.markAsIndexed(inputFile);
}
LOG.debug("'{}' indexed {}with language '{}'", inputFile.relativePath(), type == Type.TEST ? "as test " : "", inputFile.language());
return null;
}
- private void indexParentDir(DefaultModuleFileSystem fileSystem, InputFile inputFile) {
+ private static String getParentRelativePath(DefaultModuleFileSystem fileSystem, InputFile inputFile) {
Path parentDir = inputFile.path().getParent();
String relativePath = new PathResolver().relativePath(fileSystem.baseDirPath(), parentDir);
if (relativePath == null) {
throw new IllegalStateException("Failed to compute relative path of file: " + inputFile);
}
+ return relativePath;
+ }
- DefaultInputDir inputDir = (DefaultInputDir) componentStore.getDir(module.key(), relativePath);
+ private void indexParentDir(DefaultModuleFileSystem fileSystem, InputFile inputFile, String parentRelativePath) {
+ DefaultInputDir inputDir = (DefaultInputDir) componentStore.getDir(module.key(), parentRelativePath);
if (inputDir == null) {
- inputDir = new DefaultInputDir(fileSystem.moduleKey(), relativePath, batchIdGenerator.get());
+ inputDir = new DefaultInputDir(fileSystem.moduleKey(), parentRelativePath, batchIdGenerator.get());
inputDir.setModuleBaseDir(fileSystem.baseDirPath());
fileSystem.add(inputDir);
componentTree.index(inputDir, module);
private final Table<String, String, InputFile> inputFileCache = TreeBasedTable.create();
private final Map<String, InputDir> globalInputDirCache = new HashMap<>();
private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create();
+ // indexed by key with branch
private final Map<String, InputModule> inputModuleCache = new HashMap<>();
private final Map<String, InputComponent> inputComponents = new HashMap<>();
private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create();
private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create();
- private InputModule root;
+ private final InputModule root;
- public InputComponentStore(PathResolver pathResolver) {
+ public InputComponentStore(PathResolver pathResolver, DefaultInputModule root) {
this.pathResolver = pathResolver;
+ this.root = root;
+ this.put(root);
}
public Collection<InputComponent> all() {
return inputComponents.get(key);
}
- @CheckForNull
public InputModule root() {
return root;
}
}
private Path getProjectBaseDir() {
- return ((DefaultInputModule) root).definition().getBaseDir().toPath();
+ return ((DefaultInputModule) root).getBaseDir().toPath();
}
@CheckForNull
}
@CheckForNull
- public InputModule getModule(String moduleKey) {
- return inputModuleCache.get(moduleKey);
+ public InputModule getModule(String moduleKeyWithBranch) {
+ return inputModuleCache.get(moduleKeyWithBranch);
}
public void put(DefaultInputModule inputModule) {
String key = inputModule.key();
+ String keyWithBranch = inputModule.getKeyWithBranch();
+ Preconditions.checkNotNull(inputModule);
Preconditions.checkState(!inputComponents.containsKey(key), "Module '%s' already indexed", key);
- Preconditions.checkState(!inputModuleCache.containsKey(key), "Module '%s' already indexed", key);
+ Preconditions.checkState(!inputModuleCache.containsKey(keyWithBranch), "Module '%s' already indexed", keyWithBranch);
inputComponents.put(key, inputModule);
- inputModuleCache.put(key, inputModule);
- if (inputModule.definition().getParent() == null) {
- if (root != null) {
- throw new IllegalStateException("Root module already indexed: '" + root.key() + "', '" + key + "'");
- }
- root = inputModule;
- }
+ inputModuleCache.put(keyWithBranch, inputModule);
}
public Iterable<InputFile> getFilesByName(String filename) {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.scan.filesystem;
+
+import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
+import org.sonar.api.scan.filesystem.PathResolver;
+
+public class InputComponentStoreProvider extends ProviderAdapter {
+ private InputComponentStore store;
+
+ public InputComponentStore provide(PathResolver pathResolver, InputModuleHierarchy hierarchy) {
+ if (store == null) {
+ store = new InputComponentStore(pathResolver, hierarchy.root());
+ }
+ return store;
+ }
+}
LOG.warn("File '{}' is ignored. It is not located in module basedir '{}'.", file.toAbsolutePath(), moduleBaseDir);
return null;
}
- DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, idGenerator.get());
- String language = langDetection.language(indexedFile);
+ String language = langDetection.language(file.toAbsolutePath().normalize().toString(), relativePath);
if (language == null && langDetection.forcedLanguage() != null) {
LOG.warn("File '{}' is ignored because it doesn't belong to the forced language '{}'", file.toAbsolutePath(), langDetection.forcedLanguage());
return null;
}
- indexedFile.setLanguage(language);
+ DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, language, idGenerator.get());
DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(f, defaultEncoding));
if (language != null) {
inputFile.setPublish(true);
*/
package org.sonar.scanner.scan.filesystem;
-import com.google.common.base.Joiner;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.ScannerSide;
-import org.sonar.api.batch.fs.internal.DefaultIndexedFile;
import org.sonar.api.batch.fs.internal.PathPattern;
import org.sonar.api.config.Configuration;
import org.sonar.api.utils.MessageException;
import org.sonar.scanner.repository.language.Language;
import org.sonar.scanner.repository.language.LanguagesRepository;
+import com.google.common.base.Joiner;
+
/**
* Detect language of a source file based on its suffix and configured patterns.
*/
@ScannerSide
+@ThreadSafe
public class LanguageDetection {
private static final Logger LOG = LoggerFactory.getLogger(LanguageDetection.class);
/**
* Lower-case extension -> languages
*/
- private final Map<String, PathPattern[]> patternsByLanguage = new LinkedHashMap<>();
- private final List<String> languagesToConsider = new ArrayList<>();
+ private final Map<String, PathPattern[]> patternsByLanguage;
+ private final List<String> languagesToConsider;
private final String forcedLanguage;
public LanguageDetection(Configuration settings, LanguagesRepository languages) {
+ Map<String, PathPattern[]> patternsByLanguageBuilder = new LinkedHashMap<>();
for (Language language : languages.all()) {
String[] filePatterns = settings.getStringArray(getFileLangPatternPropKey(language.key()));
PathPattern[] pathPatterns = PathPattern.create(filePatterns);
if (pathPatterns.length > 0) {
- patternsByLanguage.put(language.key(), pathPatterns);
+ patternsByLanguageBuilder.put(language.key(), pathPatterns);
} else {
// If no custom language pattern is defined then fallback to suffixes declared by language
String[] patterns = language.fileSuffixes().toArray(new String[language.fileSuffixes().size()]);
patterns[i] = new StringBuilder().append("**/*.").append(extension).toString();
}
PathPattern[] defaultLanguagePatterns = PathPattern.create(patterns);
- patternsByLanguage.put(language.key(), defaultLanguagePatterns);
- LOG.debug("Declared extensions of language {} were converted to {}", language, getDetails(language.key()));
+ patternsByLanguageBuilder.put(language.key(), defaultLanguagePatterns);
+ LOG.debug("Declared extensions of language {} were converted to {}", language, getDetails(language.key(), defaultLanguagePatterns));
}
}
forcedLanguage = StringUtils.defaultIfBlank(settings.get(CoreProperties.PROJECT_LANGUAGE_PROPERTY).orElse(null), null);
// First try with lang patterns
if (forcedLanguage != null) {
- if (!patternsByLanguage.containsKey(forcedLanguage)) {
+ if (!patternsByLanguageBuilder.containsKey(forcedLanguage)) {
throw MessageException.of("You must install a plugin that supports the language '" + forcedLanguage + "'");
}
LOG.info("Language is forced to {}", forcedLanguage);
- languagesToConsider.add(forcedLanguage);
+ languagesToConsider = Collections.singletonList(forcedLanguage);
} else {
- languagesToConsider.addAll(patternsByLanguage.keySet());
+ languagesToConsider = Collections.unmodifiableList(new ArrayList<>(patternsByLanguageBuilder.keySet()));
}
+
+ patternsByLanguage = Collections.unmodifiableMap(patternsByLanguageBuilder);
}
public String forcedLanguage() {
}
@CheckForNull
- String language(DefaultIndexedFile inputFile) {
+ String language(String absolutePath, String relativePath) {
String detectedLanguage = null;
for (String languageKey : languagesToConsider) {
- if (isCandidateForLanguage(inputFile, languageKey)) {
+ if (isCandidateForLanguage(absolutePath, relativePath, languageKey)) {
if (detectedLanguage == null) {
detectedLanguage = languageKey;
} else {
// Language was already forced by another pattern
throw MessageException.of(MessageFormat.format("Language of file ''{0}'' can not be decided as the file matches patterns of both {1} and {2}",
- inputFile.relativePath(), getDetails(detectedLanguage), getDetails(languageKey)));
+ relativePath, getDetails(detectedLanguage), getDetails(languageKey)));
}
}
}
return null;
}
- private boolean isCandidateForLanguage(DefaultIndexedFile inputFile, String languageKey) {
+ private boolean isCandidateForLanguage(String absolutePath, String relativePath, String languageKey) {
PathPattern[] patterns = patternsByLanguage.get(languageKey);
if (patterns != null) {
for (PathPattern pathPattern : patterns) {
- if (pathPattern.match(inputFile, false)) {
+ if (pathPattern.match(absolutePath, relativePath, false)) {
return true;
}
}
}
private String getDetails(String detectedLanguage) {
- return getFileLangPatternPropKey(detectedLanguage) + " : " + Joiner.on(",").join(patternsByLanguage.get(detectedLanguage));
+ return getDetails(detectedLanguage, patternsByLanguage.get(detectedLanguage));
+ }
+
+ private static String getDetails(String detectedLanguage, PathPattern[] patterns) {
+ return getFileLangPatternPropKey(detectedLanguage) + " : " + Joiner.on(",").join(patterns);
}
static String sanitizeExtension(String suffix) {
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+
import org.apache.commons.io.FileUtils;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
*/
package org.sonar.scanner.scan.filesystem;
+import javax.annotation.concurrent.Immutable;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.scanner.repository.FileData;
import org.sonar.scanner.repository.ProjectRepositories;
+@Immutable
class StatusDetection {
private final ProjectRepositories projectSettings;
import com.google.common.collect.Lists;
import java.io.Serializable;
import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.scanner.repository.MetricsRepository;
+@ThreadSafe
public class DefaultMetricFinder implements MetricFinder {
- private Map<String, Metric<Serializable>> metricsByKey = new LinkedHashMap<>();
+ private Map<String, Metric<Serializable>> metricsByKey;
public DefaultMetricFinder(MetricsRepository metricsRepository) {
+ Map<String, Metric<Serializable>> metrics = new LinkedHashMap<>();
for (org.sonar.api.measures.Metric metric : metricsRepository.metrics()) {
- metricsByKey.put(metric.key(), new org.sonar.api.measures.Metric.Builder(metric.key(), metric.key(), metric.getType()).create());
+ metrics.put(metric.key(), new org.sonar.api.measures.Metric.Builder(metric.key(), metric.key(), metric.getType()).create());
}
+ metricsByKey = Collections.unmodifiableMap(metrics);
}
@Override
package org.sonar.scanner.scan.report;
import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
import org.apache.commons.lang.StringEscapeUtils;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.rule.Rule;
import org.sonar.api.rule.RuleKey;
@ScannerSide
+@Immutable
public class RuleNameProvider {
private Rules rules;
*/
package org.sonar.scanner.scm;
-import com.google.common.base.Joiner;
import java.util.LinkedHashMap;
import java.util.Map;
+
import org.apache.commons.lang.StringUtils;
import org.picocontainer.Startable;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.batch.scm.ScmProvider;
import org.sonar.api.config.Configuration;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
-import org.sonar.scanner.scan.ImmutableProjectReactor;
+
+import com.google.common.base.Joiner;
@Properties({
@Property(
public static final String FORCE_RELOAD_KEY = "sonar.scm.forceReloadAll";
- private final ImmutableProjectReactor projectReactor;
private final Configuration settings;
private final Map<String, ScmProvider> providerPerKey = new LinkedHashMap<>();
private final AnalysisMode analysisMode;
+ private final InputModuleHierarchy moduleHierarchy;
private ScmProvider provider;
- public ScmConfiguration(ImmutableProjectReactor projectReactor, AnalysisMode analysisMode, Configuration settings, ScmProvider... providers) {
- this.projectReactor = projectReactor;
+ public ScmConfiguration(InputModuleHierarchy moduleHierarchy, AnalysisMode analysisMode, Configuration settings, ScmProvider... providers) {
+ this.moduleHierarchy = moduleHierarchy;
this.analysisMode = analysisMode;
this.settings = settings;
for (ScmProvider scmProvider : providers) {
}
}
- public ScmConfiguration(ImmutableProjectReactor projectReactor, AnalysisMode analysisMode, Configuration settings) {
- this(projectReactor, analysisMode, settings, new ScmProvider[0]);
+ public ScmConfiguration(InputModuleHierarchy moduleHierarchy, AnalysisMode analysisMode, Configuration settings) {
+ this(moduleHierarchy, analysisMode, settings, new ScmProvider[0]);
}
@Override
private void autodetection() {
for (ScmProvider installedProvider : providerPerKey.values()) {
- if (installedProvider.supports(projectReactor.getRoot().getBaseDir())) {
+ if (installedProvider.supports(moduleHierarchy.root().getBaseDir())) {
if (this.provider == null) {
this.provider = installedProvider;
} else {
package org.sonar.scanner.sensor;
import java.io.Serializable;
+
+import javax.annotation.concurrent.ThreadSafe;
+
import org.sonar.api.SonarRuntime;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.scanner.sensor.noop.NoOpNewHighlighting;
import org.sonar.scanner.sensor.noop.NoOpNewSymbolTable;
+@ThreadSafe
public class DefaultSensorContext implements SensorContext {
private static final NoOpNewHighlighting NO_OP_NEW_HIGHLIGHTING = new NoOpNewHighlighting();
*/
package org.sonar.scanner.sensor;
-import com.google.common.annotations.VisibleForTesting;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.sonar.api.batch.fs.InputComponent;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.TextRange;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.measure.Metric;
-import org.sonar.api.batch.measure.MetricFinder;
-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.Issue;
-import org.sonar.api.batch.sensor.measure.Measure;
-import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
-import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
-import org.sonar.api.config.Configuration;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.utils.KeyValueFormat;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.core.metric.ScannerMetrics;
-import org.sonar.duplications.block.Block;
-import org.sonar.duplications.internal.pmd.PmdBlockChunker;
-import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer;
-import org.sonar.scanner.cpd.index.SonarCpdBlockIndex;
-import org.sonar.scanner.issue.ModuleIssues;
-import org.sonar.scanner.protocol.output.FileStructure;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReportWriter;
-import org.sonar.scanner.report.ReportPublisher;
-import org.sonar.scanner.report.ScannerReportUtils;
-import org.sonar.scanner.repository.ContextPropertiesCache;
-import org.sonar.scanner.scan.measure.MeasureCache;
-import org.sonar.scanner.sensor.coverage.CoverageExclusions;
-
import static java.util.stream.Collectors.toList;
import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE;
import static org.sonar.api.measures.CoreMetrics.COMMENTED_OUT_CODE_LINES_KEY;
import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS;
import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.sonar.api.batch.fs.InputComponent;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.measure.Metric;
+import org.sonar.api.batch.measure.MetricFinder;
+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.Issue;
+import org.sonar.api.batch.sensor.measure.Measure;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
+import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.metric.ScannerMetrics;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.internal.pmd.PmdBlockChunker;
+import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer;
+import org.sonar.scanner.cpd.index.SonarCpdBlockIndex;
+import org.sonar.scanner.issue.ModuleIssues;
+import org.sonar.scanner.protocol.output.FileStructure;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReportWriter;
+import org.sonar.scanner.report.ReportPublisher;
+import org.sonar.scanner.report.ScannerReportUtils;
+import org.sonar.scanner.repository.ContextPropertiesCache;
+import org.sonar.scanner.scan.measure.MeasureCache;
+import org.sonar.scanner.sensor.coverage.CoverageExclusions;
+
+import com.google.common.annotations.VisibleForTesting;
+
public class DefaultSensorStorage implements SensorStorage {
private static final Logger LOG = Loggers.get(DefaultSensorStorage.class);
private final Map<Metric<?>, Metric<?>> deprecatedCoverageMetricMapping = new HashMap<>();
private final Set<Metric<?>> coverageMetrics = new HashSet<>();
private final Set<Metric<?>> byLineMetrics = new HashSet<>();
- private Set<String> alreadyLogged = new HashSet<>();
+ private final Set<String> alreadyLogged = new HashSet<>();
- public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues,
- Configuration settings,
- CoverageExclusions coverageExclusions, ReportPublisher reportPublisher,
- MeasureCache measureCache, SonarCpdBlockIndex index,
+ public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, Configuration settings, CoverageExclusions coverageExclusions,
+ ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index,
ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics) {
this.metricFinder = metricFinder;
this.moduleIssues = moduleIssues;
saveMeasure(newMeasure.inputComponent(), (DefaultMeasure<?>) newMeasure);
}
+ /**
+ * Thread safe
+ */
private void logOnce(String metricKey, String msg, Object... params) {
- if (!alreadyLogged.contains(metricKey)) {
+ if (alreadyLogged.add(metricKey)) {
LOG.warn(msg, params);
- alreadyLogged.add(metricKey);
}
}
}
}
- public boolean isDeprecatedMetric(String metricKey) {
+ public static boolean isDeprecatedMetric(String metricKey) {
return DEPRECATED_METRICS_KEYS.contains(metricKey);
}
- public boolean isPlatformMetric(String metricKey) {
+ public static boolean isPlatformMetric(String metricKey) {
return PLATFORM_METRICS_KEYS.contains(metricKey);
}
return this.byLineMetrics.contains(metric);
}
- public void validateCoverageMeasure(String value, InputFile inputFile) {
+ public static void validateCoverageMeasure(String value, InputFile inputFile) {
Map<Integer, Integer> m = KeyValueFormat.parseIntInt(value);
validatePositiveLine(m, inputFile.absolutePath());
validateMaxLine(m, inputFile);
}
}
+ /**
+ * Thread safe assuming that each issues for each file are only written once.
+ */
@Override
public void store(Issue issue) {
if (issue.primaryLocation().inputComponent() instanceof DefaultInputFile) {
*/
package org.sonar.scanner.sensor.coverage;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import java.util.Collection;
import java.util.Iterator;
+
+import javax.annotation.concurrent.Immutable;
+
import org.picocontainer.Startable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.config.Configuration;
import org.sonar.api.utils.WildcardPattern;
-public class CoverageExclusions implements Startable {
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+@Immutable
+public class CoverageExclusions implements Startable {
private static final Logger LOG = LoggerFactory.getLogger(CoverageExclusions.class);
- private final Configuration settings;
private Collection<WildcardPattern> exclusionPatterns;
public CoverageExclusions(Configuration settings) {
- this.settings = settings;
+ Builder<WildcardPattern> builder = ImmutableList.builder();
+ for (String pattern : settings.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)) {
+ builder.add(WildcardPattern.create(pattern));
+ }
+ exclusionPatterns = builder.build();
}
@Override
public void start() {
- initPatterns();
+ log("Excluded sources for coverage: ", exclusionPatterns);
}
@Override
return found;
}
- @VisibleForTesting
- final void initPatterns() {
- Builder<WildcardPattern> builder = ImmutableList.builder();
- for (String pattern : settings.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)) {
- builder.add(WildcardPattern.create(pattern));
- }
- exclusionPatterns = builder.build();
- log("Excluded sources for coverage: ", exclusionPatterns);
- }
-
private static void log(String title, Collection<WildcardPattern> patterns) {
if (!patterns.isEmpty()) {
LOG.info(title);
*/
package org.sonar.scanner.storage;
-import com.google.common.collect.Sets;
import com.persistit.Exchange;
import com.persistit.Key;
import com.persistit.KeyFilter;
import com.persistit.exception.PersistitException;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.CheckForNull;
@SuppressWarnings("rawtypes")
public Set keySet(Object key) {
try {
- Set<Object> keys = Sets.newLinkedHashSet();
+ Set<Object> keys = new LinkedHashSet<>();
exchange.clear();
Exchange iteratorExchange = new Exchange(exchange);
iteratorExchange.append(key);
@SuppressWarnings("rawtypes")
public Set keySet(Object firstKey, Object secondKey) {
try {
- Set<Object> keys = Sets.newLinkedHashSet();
+ Set<Object> keys = new LinkedHashSet<>();
exchange.clear();
Exchange iteratorExchange = new Exchange(exchange);
iteratorExchange.append(firstKey);
*/
public Set<Object> keySet() {
try {
- Set<Object> keys = Sets.newLinkedHashSet();
+ Set<Object> keys = new LinkedHashSet<>();
exchange.clear();
Exchange iteratorExchange = new Exchange(exchange);
iteratorExchange.append(Key.BEFORE);
*/
package org.sonar.scanner.analysis;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.io.IOException;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.utils.TempFolder;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class AnalysisTempFolderProviderTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
private AnalysisTempFolderProvider tempFolderProvider;
- private ProjectReactor projectReactor;
+ private InputModuleHierarchy moduleHierarchy;
@Before
public void setUp() {
tempFolderProvider = new AnalysisTempFolderProvider();
- projectReactor = mock(ProjectReactor.class);
- ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
- when(projectReactor.getRoot()).thenReturn(projectDefinition);
- when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot());
+ moduleHierarchy = mock(InputModuleHierarchy.class);
+ DefaultInputModule module = mock(DefaultInputModule.class);
+ when(moduleHierarchy.root()).thenReturn(module);
+ when(module.getWorkDir()).thenReturn(temp.getRoot());
}
@Test
public void createTempFolder() throws IOException {
File defaultDir = new File(temp.getRoot(), AnalysisTempFolderProvider.TMP_NAME);
- TempFolder tempFolder = tempFolderProvider.provide(projectReactor);
+ TempFolder tempFolder = tempFolderProvider.provide(moduleHierarchy);
tempFolder.newDir();
tempFolder.newFile();
assertThat(defaultDir).exists();
*/
package org.sonar.scanner.cpd;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
-import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.scanner.report.ReportPublisher;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class CpdExecutorTest {
@Rule
public LogTester logTester = new LogTester();
public ExpectedException thrown = ExpectedException.none();
private CpdExecutor executor;
- private MapSettings settings;
+ private CpdSettings settings;
private SonarCpdBlockIndex index;
private ReportPublisher publisher;
private ScannerReportReader reader;
File outputDir = temp.newFolder();
baseDir = temp.newFolder();
- settings = new MapSettings();
+ settings = mock(CpdSettings.class);
publisher = mock(ReportPublisher.class);
when(publisher.getWriter()).thenReturn(new ScannerReportWriter(outputDir));
- index = new SonarCpdBlockIndex(publisher, settings.asConfig());
- componentStore = new InputComponentStore(new PathResolver());
- executor = new CpdExecutor(settings.asConfig(), index, publisher, componentStore);
- reader = new ScannerReportReader(outputDir);
- componentStore.put(TestInputFileBuilder.newDefaultInputModule("foo", baseDir));
+ index = new SonarCpdBlockIndex(publisher, settings);
+ DefaultInputModule inputModule = TestInputFileBuilder.newDefaultInputModule("foo", baseDir);
+ componentStore = new InputComponentStore(new PathResolver(), inputModule);
+ executor = new CpdExecutor(settings, index, publisher, componentStore);
+ reader = new ScannerReportReader(outputDir);
batchComponent1 = createComponent("src/Foo.php", 5);
batchComponent2 = createComponent("src/Foo2.php", 5);
return file;
}
- @Test
- public void defaultMinimumTokens() {
- assertThat(executor.getMinimumTokens("java")).isEqualTo(100);
- }
-
- @Test
- public void minimumTokensByLanguage() {
- settings.setProperty("sonar.cpd.java.minimumTokens", "42");
- settings.setProperty("sonar.cpd.php.minimumTokens", "33");
- assertThat(executor.getMinimumTokens("java")).isEqualTo(42);
-
- settings.setProperty("sonar.cpd.java.minimumTokens", "42");
- settings.setProperty("sonar.cpd.php.minimumTokens", "33");
- assertThat(executor.getMinimumTokens("php")).isEqualTo(33);
- }
-
@Test
public void testNothingToSave() {
executor.saveDuplications(batchComponent1, Collections.<CloneGroup>emptyList());
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.cpd;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Optional;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
+import org.sonar.api.config.Configuration;
+
+public class CpdSettingsTest {
+ private CpdSettings cpdSettings;
+ private Configuration configuration;
+ private DefaultInputModule module;
+
+ @Before
+ public void setUp() {
+ module = mock(DefaultInputModule.class);
+ InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class);
+ when(hierarchy.root()).thenReturn(module);
+ configuration = mock(Configuration.class);
+ cpdSettings = new CpdSettings(configuration, hierarchy);
+ }
+
+ @Test
+ public void defaultMinimumTokens() {
+ when(configuration.getInt(anyString())).thenReturn(Optional.empty());
+ assertThat(cpdSettings.getMinimumTokens("java")).isEqualTo(100);
+ }
+
+ @Test
+ public void minimumTokensByLanguage() {
+ when(configuration.getInt("sonar.cpd.java.minimumTokens")).thenReturn(Optional.of(42));
+ when(configuration.getInt("sonar.cpd.php.minimumTokens")).thenReturn(Optional.of(33));
+
+ assertThat(cpdSettings.getMinimumTokens("java")).isEqualTo(42);
+ assertThat(cpdSettings.getMinimumTokens("php")).isEqualTo(33);
+ }
+}
*/
package org.sonar.scanner.index;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.IOException;
import java.util.Collections;
import org.junit.Before;
import org.sonar.scanner.scan.measure.MeasureCache;
import org.sonar.scanner.sensor.DefaultSensorStorage;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class DefaultIndexTest {
@org.junit.Rule
rootDef.addSubProject(moduleBDef);
moduleBDef.addSubProject(moduleB1Def);
- project = new Project(rootDef);
- moduleA = new Project(moduleADef);
- moduleB = new Project(moduleBDef);
- moduleB1 = new Project(moduleB1Def);
+ project = new Project(new DefaultInputModule(rootDef));
+ moduleA = new Project(new DefaultInputModule(moduleADef));
+ moduleB = new Project(new DefaultInputModule(moduleBDef));
+ moduleB1 = new Project(new DefaultInputModule(moduleB1Def));
RulesProfile rulesProfile = RulesProfile.create();
rule = Rule.create("repoKey", "ruleKey", "Rule");
issue = mock(FilterableIssue.class);
chain = mock(IssueFilterChain.class);
when(chain.accept(issue)).thenReturn(true);
-
- ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore);
}
@Test
public void shouldPassToChainIfNoConfiguredPatterns() {
+ ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore);
assertThat(ignoreFilter.accept(issue, chain)).isTrue();
verify(chain).accept(issue);
}
when(rulePattern.match(rule)).thenReturn(false);
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
+ ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore);
assertThat(ignoreFilter.accept(issue, chain)).isTrue();
verify(chain).accept(issue);
}
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
when(inputComponentStore.getByKey(componentKey)).thenReturn(createComponentWithPath(path));
+ ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore);
assertThat(ignoreFilter.accept(issue, chain)).isTrue();
verifyZeroInteractions(chain);
}
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
when(inputComponentStore.getByKey(componentKey)).thenReturn(createComponentWithPath(path));
+ ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore);
assertThat(ignoreFilter.accept(issue, chain)).isFalse();
verifyZeroInteractions(chain);
}
when(exclusionPatternInitializer.getMulticriteriaPatterns()).thenReturn(ImmutableList.of(matching));
when(inputComponentStore.getByKey(componentKey)).thenReturn(null);
+ ignoreFilter = new EnforceIssuesFilter(exclusionPatternInitializer, inputComponentStore);
assertThat(ignoreFilter.accept(issue, chain)).isFalse();
verifyZeroInteractions(chain);
}
*/
package org.sonar.scanner.issue.tracking;
+import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.util.Collections;
+
import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Rule;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import static org.apache.commons.codec.digest.DigestUtils.md5Hex;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
public class SourceHashHolderTest {
@Rule
DefaultInputFile file;
private File ioFile;
- private ProjectDefinition def = ProjectDefinition.create();
+ private ProjectDefinition def;
@Before
public void setUp() throws Exception {
+ def = mock(ProjectDefinition.class);
lastSnapshots = mock(ServerLineHashesLoader.class);
file = mock(DefaultInputFile.class);
ioFile = temp.newFile();
public void should_lazy_load_reference_hashes_when_status_changed() throws Exception {
final String source = "source";
FileUtils.write(ioFile, source, StandardCharsets.UTF_8);
- def.setKey("foo");
+ when(def.getKeyWithBranch()).thenReturn("foo");
when(file.relativePath()).thenReturn("src/Foo.java");
String key = "foo:src/Foo.java";
when(file.status()).thenReturn(InputFile.Status.CHANGED);
public void should_lazy_load_reference_hashes_when_status_changed_on_branch() throws Exception {
final String source = "source";
FileUtils.write(ioFile, source, StandardCharsets.UTF_8);
- def.setKey("foo");
- def.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
+ when(def.getKeyWithBranch()).thenReturn("foo:myBranch");
+ when(def.properties()).thenReturn(Collections.singletonMap(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch"));
when(file.relativePath()).thenReturn("src/Foo.java");
String key = "foo:myBranch:src/Foo.java";
when(file.status()).thenReturn(InputFile.Status.CHANGED);
.newScanTask(new File(projectDir, "sonar-project.properties"))
.start();
+ System.out.println(logs.getAsString());
assertThat(result.inputFiles()).hasSize(4);
assertThat(result.inputDirs()).hasSize(4);
}
*/
package org.sonar.scanner.phases;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.IOException;
import java.util.Collections;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.resources.Project;
import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary;
import org.sonar.scanner.events.EventBus;
import org.sonar.scanner.sensor.SensorStrategy;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class SensorsExecutorTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
when(selector.selectSensors(any(DefaultInputModule.class), eq(false))).thenReturn(Collections.singleton(perModuleSensor));
when(selector.selectSensors(any(DefaultInputModule.class), eq(true))).thenReturn(Collections.singleton(globalSensor));
- DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule("root", temp.newFolder());
- rootModuleExecutor = new SensorsExecutor(selector, rootModule, mock(EventBus.class), strategy);
+ ProjectDefinition childDef = ProjectDefinition.create().setKey("sub").setBaseDir(temp.newFolder());
+ ProjectDefinition rootDef = ProjectDefinition.create().setKey("root").setBaseDir(temp.newFolder());
+
+ DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(rootDef);
+ DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(childDef);
+
+ InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class);
+ when(hierarchy.isRoot(rootModule)).thenReturn(true);
- DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule("sub", temp.newFolder());
- rootModule.definition().addSubProject(subModule.definition());
- subModuleExecutor = new SensorsExecutor(selector, subModule, mock(EventBus.class), strategy);
+ rootModuleExecutor = new SensorsExecutor(selector, rootModule, hierarchy, mock(EventBus.class), strategy);
+ subModuleExecutor = new SensorsExecutor(selector, subModule, hierarchy, mock(EventBus.class), strategy);
}
@Test
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.postjob.issue.PostJobIssue;
import org.sonar.api.batch.rule.Severity;
private AnalysisMode analysisMode;
@Before
- public void prepare() {
+ public void setUp() throws IOException {
issueCache = mock(IssueCache.class);
- componentStore = new InputComponentStore(new PathResolver());
+ DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule("foo", temp.newFolder());
+ componentStore = new InputComponentStore(new PathResolver(), rootModule);
settings = new MapSettings();
analysisMode = mock(AnalysisMode.class);
context = new DefaultPostJobContext(settings.asConfig(), settings, issueCache, componentStore, analysisMode);
assertThat(issue.inputComponent()).isNull();
String moduleKey = "foo";
- componentStore.put(TestInputFileBuilder.newDefaultInputModule("foo", temp.newFolder()));
-
componentStore.put(new TestInputFileBuilder(moduleKey, "src/Foo.php").build());
assertThat(issue.inputComponent()).isNotNull();
import org.sonar.api.batch.events.SensorExecutionHandler.SensorExecutionEvent;
import org.sonar.api.batch.events.SensorsPhaseHandler;
import org.sonar.api.batch.events.SensorsPhaseHandler.SensorsPhaseEvent;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.System2;
import org.sonar.scanner.bootstrap.GlobalProperties;
}
private Project mockProject(String name, boolean isRoot) {
- return new Project(ProjectDefinition.create().setName(name).setKey(name));
+ return new Project(new DefaultInputModule(ProjectDefinition.create().setName(name).setKey(name)));
}
private void fakeAnalysis(PhasesSumUpTimeProfiler profiler, final Project module) {
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
private System2 system2;
private ProjectRepositories projectRepos;
private GlobalConfiguration globalSettings;
+ private InputModuleHierarchy hierarchy;
@Before
public void prepare() throws Exception {
when(system2.properties()).thenReturn(new Properties());
projectRepos = mock(ProjectRepositories.class);
globalSettings = mock(GlobalConfiguration.class);
- publisher = new AnalysisContextReportPublisher(analysisMode, pluginRepo, system2, projectRepos, globalSettings);
+ hierarchy = mock(InputModuleHierarchy.class);
+ publisher = new AnalysisContextReportPublisher(analysisMode, pluginRepo, system2, projectRepos, globalSettings, hierarchy);
}
@Test
ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
publisher.init(writer);
- publisher.dumpModuleSettings(ProjectDefinition.create().setProperty("sonar.projectKey", "foo"));
+ publisher.dumpModuleSettings(new DefaultInputModule("foo"));
assertThat(writer.getFileStructure().analysisLog()).doesNotExist();
}
when(projectRepos.moduleExists("foo")).thenReturn(true);
when(projectRepos.settings("foo")).thenReturn(ImmutableMap.of(COM_FOO, "bar", SONAR_SKIP, "true"));
- publisher.dumpModuleSettings(ProjectDefinition.create()
- .setProperty("sonar.projectKey", "foo"));
+ publisher.dumpModuleSettings(new DefaultInputModule("foo"));
String content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
assertThat(content).doesNotContain(COM_FOO);
assertThat(content).containsOnlyOnce(COM_FOO);
assertThat(content).doesNotContain(SONAR_SKIP);
- publisher.dumpModuleSettings(ProjectDefinition.create()
+ publisher.dumpModuleSettings(new DefaultInputModule(ProjectDefinition.create()
.setProperty("sonar.projectKey", "foo")
.setProperty(COM_FOO, "bar")
- .setProperty(SONAR_SKIP, "true"));
+ .setProperty(SONAR_SKIP, "true")));
content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
assertThat(content).containsOnlyOnce(COM_FOO);
assertThat(content).containsOnlyOnce(BIZ);
assertThat(content).containsSequence(BIZ, FOO);
- publisher.dumpModuleSettings(ProjectDefinition.create()
+ publisher.dumpModuleSettings(new DefaultInputModule(ProjectDefinition.create()
.setProperty("sonar.projectKey", "foo")
- .setProperty("env." + FOO, "BAR"));
+ .setProperty("env." + FOO, "BAR")));
content = FileUtils.readFileToString(writer.getFileStructure().analysisLog());
assertThat(content).containsOnlyOnce(FOO);
assertThat(writer.getFileStructure().analysisLog()).exists();
- publisher.dumpModuleSettings(ProjectDefinition.create()
+ publisher.dumpModuleSettings(new DefaultInputModule(ProjectDefinition.create()
.setProperty("sonar.projectKey", "foo")
.setProperty("sonar.projectKey", "foo")
.setProperty("sonar.login", "my_token")
.setProperty("sonar.password", "azerty")
- .setProperty("sonar.cpp.license.secured", "AZERTY"));
+ .setProperty("sonar.cpp.license.secured", "AZERTY")));
assertThat(FileUtils.readFileToString(writer.getFileStructure().analysisLog())).containsSequence(
"sonar.cpp.license.secured=******",
ScannerReportWriter writer = new ScannerReportWriter(temp.newFolder());
publisher.init(writer);
- ProjectDefinition module = ProjectDefinition.create()
+ DefaultInputModule module = new DefaultInputModule(ProjectDefinition.create()
.setProperty("sonar.projectKey", "foo")
- .setProperty(SONAR_SKIP, "true");
+ .setProperty(SONAR_SKIP, "true"));
- ProjectDefinition.create()
+ DefaultInputModule parent = new DefaultInputModule(ProjectDefinition.create()
.setProperty("sonar.projectKey", "parent")
- .setProperty(SONAR_SKIP, "true")
- .addSubProject(module);
+ .setProperty(SONAR_SKIP, "true"));
+
+ when(hierarchy.parent(module)).thenReturn(parent);
publisher.dumpModuleSettings(module);
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.measures.CoreMetrics;
public void prepare() throws IOException {
String moduleKey = "foo";
inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php").setLines(5).build();
- InputComponentStore componentCache = new InputComponentStore(new PathResolver());
- componentCache.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder()));
+ DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder());
+ InputComponentStore componentCache = new InputComponentStore(new PathResolver(), rootModule);
componentCache.put(inputFile);
measureCache = mock(MeasureCache.class);
String moduleKey = "foo";
inputModule = TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder());
inputFile = new TestInputFileBuilder(moduleKey, "src/Foo.php").setPublish(true).build();
- InputComponentStore componentCache = new InputComponentStore(new PathResolver());
- componentCache.put(inputModule);
+ InputComponentStore componentCache = new InputComponentStore(new PathResolver(), inputModule);
componentCache.put(inputFile);
measureCache = mock(MeasureCache.class);
when(measureCache.byComponentKey(anyString())).thenReturn(Collections.<DefaultMeasure<?>>emptyList());
*/
package org.sonar.scanner.report;
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.util.Date;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.scanner.ProjectAnalysisInfo;
+import org.sonar.scanner.cpd.CpdSettings;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportReader;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
import org.sonar.scanner.rule.ModuleQProfiles;
import org.sonar.scanner.rule.QProfile;
-import static java.util.Arrays.asList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.entry;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class MetadataPublisherTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
- private ProjectDefinition projectDef;
private DefaultInputModule rootModule;
private MetadataPublisher underTest;
private MapSettings settings;
private ModuleQProfiles qProfiles;
private ProjectAnalysisInfo projectAnalysisInfo;
+ private CpdSettings cpdSettings;
private InputModuleHierarchy inputModuleHierarchy;
@Before
public void prepare() {
- projectDef = ProjectDefinition.create().setKey("foo");
- rootModule = new DefaultInputModule(projectDef, TestInputFileBuilder.nextBatchId());
projectAnalysisInfo = mock(ProjectAnalysisInfo.class);
+ cpdSettings = mock(CpdSettings.class);
when(projectAnalysisInfo.analysisDate()).thenReturn(new Date(1234567L));
- inputModuleHierarchy = mock(InputModuleHierarchy.class);
- when(inputModuleHierarchy.root()).thenReturn(rootModule);
settings = new MapSettings();
qProfiles = mock(ModuleQProfiles.class);
- underTest = new MetadataPublisher(projectAnalysisInfo, inputModuleHierarchy, settings.asConfig(), qProfiles);
+ createPublisher(ProjectDefinition.create().setKey("foo"));
+ }
+
+ private void createPublisher(ProjectDefinition def) {
+ rootModule = new DefaultInputModule(def, TestInputFileBuilder.nextBatchId());
+ inputModuleHierarchy = mock(InputModuleHierarchy.class);
+ when(inputModuleHierarchy.root()).thenReturn(rootModule);
+ underTest = new MetadataPublisher(projectAnalysisInfo, inputModuleHierarchy, settings.asConfig(), qProfiles, cpdSettings);
}
@Test
public void write_metadata() throws Exception {
settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
Date date = new Date();
- when(qProfiles.findAll()).thenReturn(asList(new QProfile()
- .setKey("q1")
- .setName("Q1")
- .setLanguage("java")
- .setRulesUpdatedAt(date)));
+ when(qProfiles.findAll()).thenReturn(asList(new QProfile("q1", "Q1", "java", date)));
File outputDir = temp.newFolder();
ScannerReportWriter writer = new ScannerReportWriter(outputDir);
assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
assertThat(metadata.getProjectKey()).isEqualTo("foo");
assertThat(metadata.getProjectKey()).isEqualTo("foo");
- assertThat(metadata.getCrossProjectDuplicationActivated()).isTrue();
assertThat(metadata.getQprofilesPerLanguage()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder()
.setKey("q1")
.setName("Q1")
@Test
public void write_project_branch() throws Exception {
+ when(cpdSettings.isCrossProjectDuplicationEnabled()).thenReturn(false);
settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
- projectDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
- projectDef.setKey("foo");
+
+ ProjectDefinition projectDef = ProjectDefinition.create()
+ .setKey("foo")
+ .setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
+ createPublisher(projectDef);
File outputDir = temp.newFolder();
ScannerReportWriter writer = new ScannerReportWriter(outputDir);
assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
assertThat(metadata.getProjectKey()).isEqualTo("foo");
assertThat(metadata.getBranch()).isEqualTo("myBranch");
- // Cross project duplication disabled on branches
assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse();
}
*/
package org.sonar.scanner.report;
+import static org.apache.commons.io.FileUtils.readFileToString;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.platform.Server;
import org.sonar.core.config.CorePropertyDefinitions;
import org.sonar.scanner.analysis.DefaultAnalysisMode;
import org.sonar.scanner.bootstrap.ScannerWsClient;
-import org.sonar.scanner.scan.ImmutableProjectReactor;
import org.sonarqube.ws.WsCe;
import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.WsRequest;
import org.sonarqube.ws.client.WsResponse;
-import static org.apache.commons.io.FileUtils.readFileToString;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.entry;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
public class ReportPublisherTest {
@Rule
MapSettings settings = new MapSettings(new PropertyDefinitions(CorePropertyDefinitions.all()));
ScannerWsClient wsClient;
Server server = mock(Server.class);
- ImmutableProjectReactor reactor = mock(ImmutableProjectReactor.class);
- ProjectDefinition root;
+ InputModuleHierarchy moduleHierarchy = mock(InputModuleHierarchy.class);
+ DefaultInputModule root;
AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class);
@Before
public void setUp() {
wsClient = mock(ScannerWsClient.class, Mockito.RETURNS_DEEP_STUBS);
- root = ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot());
- when(reactor.getRoot()).thenReturn(root);
+ root = new DefaultInputModule(ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot()));
+ when(moduleHierarchy.root()).thenReturn(root);
when(server.getPublicRootUrl()).thenReturn("https://localhost");
when(server.getVersion()).thenReturn("6.4");
}
@Test
public void log_and_dump_information_about_report_uploading() throws IOException {
- ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg");
underTest.logSuccess("TASK-123");
@Test
public void parse_upload_error_message() throws IOException {
- ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
HttpException ex = new HttpException("url", 404, "{\"errors\":[{\"msg\":\"Organization with key 'MyOrg' does not exist\"}]}");
WsResponse response = mock(WsResponse.class);
when(response.failIfNotSuccessful()).thenThrow(ex);
@Test
public void log_public_url_if_defined() throws IOException {
when(server.getPublicRootUrl()).thenReturn("https://publicserver/sonarqube");
- ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
underTest.logSuccess("TASK-123");
@Test
public void fail_if_public_url_malformed() throws IOException {
when(server.getPublicRootUrl()).thenReturn("invalid");
- ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
exception.expect(MessageException.class);
exception.expectMessage("Failed to parse public URL set in SonarQube server: invalid");
@Test
public void log_but_not_dump_information_when_report_is_not_uploaded() {
- ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
underTest.logSuccess(/* report not uploaded, no server task */null);
settings.setProperty("sonar.batch.keepReport", true);
Path reportDir = temp.getRoot().toPath().resolve("batch-report");
Files.createDirectory(reportDir);
- ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
underTest.start();
underTest.stop();
public void should_delete_report_by_default() throws IOException {
Path reportDir = temp.getRoot().toPath().resolve("batch-report");
Files.createDirectory(reportDir);
- ReportPublisher job = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher job = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
job.start();
job.stop();
@Test
public void test_ws_parameters() throws Exception {
- ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]);
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg");
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
.setCharset(StandardCharsets.ISO_8859_1)
.build();
- InputComponentStore componentStore = new InputComponentStore(new PathResolver());
- componentStore.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, baseDir));
+ DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(moduleKey, baseDir);
+ InputComponentStore componentStore = new InputComponentStore(new PathResolver(), rootModule);
componentStore.put(inputFile);
publisher = new SourcePublisher(componentStore);
public class QProfileTest {
@Test
public void testEquals() {
- QProfile q1 = new QProfile();
- QProfile q2 = new QProfile();
- QProfile q3 = new QProfile();
-
- q1.setKey("k1");
- q1.setName("name1");
-
- q2.setKey("k1");
- q2.setName("name2");
-
- q3.setKey("k3");
- q3.setName("name3");
+ QProfile q1 = new QProfile("k1", "name1", null, null);
+ QProfile q2 = new QProfile("k1", "name2", null, null);
+ QProfile q3 = new QProfile("k3", "name3", null, null);
assertThat(q1).isEqualTo(q2);
assertThat(q1).isNotEqualTo(q3);
public void before() throws Exception {
fs = new DefaultFileSystem(temp.newFolder().toPath());
profiles = mock(ModuleQProfiles.class);
- QProfile javaProfile = new QProfile().setKey("p1").setName("My Java profile").setLanguage("java");
+ QProfile javaProfile = new QProfile("p1", "My Java profile", "java", null);
when(profiles.findByLanguage("java")).thenReturn(javaProfile);
- QProfile cobolProfile = new QProfile().setKey("p2").setName("My Cobol profile").setLanguage("cobol");
+ QProfile cobolProfile = new QProfile("p2", "My Cobol profile", "cobol", null);
when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile);
}
@Test
public void merge_profiles() {
- QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java");
+ QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null);
when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile));
RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings.asConfig());
public void keep_compatibility_with_single_language_projects() {
settings.setProperty("sonar.language", "java");
- QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java");
+ QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null);
when(qProfiles.findByLanguage("java")).thenReturn(qProfile);
RulesProfile profile = provider.provide(qProfiles, new ActiveRulesBuilder().build(), settings.asConfig());
@Test
public void support_rule_templates() {
- QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java");
+ QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null);
when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile));
ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();
activeRulesBuilder.create(RuleKey.of("java", "S001")).setTemplateRuleKey("T001").setLanguage("java").activate();
*/
package org.sonar.scanner.scan;
-import org.junit.Before;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+
import org.junit.Test;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import static org.assertj.core.api.Assertions.assertThat;
-
public class DefaultInputModuleHierarchyTest {
private DefaultInputModuleHierarchy moduleHierarchy;
- @Before
- public void setUp() {
- moduleHierarchy = new DefaultInputModuleHierarchy();
- }
-
@Test
public void test() {
DefaultInputModule root = new DefaultInputModule("root");
DefaultInputModule mod3 = new DefaultInputModule("mod3");
DefaultInputModule mod4 = new DefaultInputModule("mod4");
- moduleHierarchy.setRoot(root);
- moduleHierarchy.index(mod1, root);
- moduleHierarchy.index(mod2, mod1);
- moduleHierarchy.index(mod3, root);
- moduleHierarchy.index(mod4, root);
+ Map<DefaultInputModule, DefaultInputModule> parents = new HashMap<>();
+
+ parents.put(mod1, root);
+ parents.put(mod2, mod1);
+ parents.put(mod3, root);
+ parents.put(mod4, root);
+
+ moduleHierarchy = new DefaultInputModuleHierarchy(parents);
assertThat(moduleHierarchy.children(root)).containsOnly(mod1, mod3, mod4);
assertThat(moduleHierarchy.children(mod4)).isEmpty();
assertThat(moduleHierarchy.root()).isEqualTo(root);
}
+
+ @Test
+ public void testOnlyRoot() {
+ DefaultInputModule root = new DefaultInputModule("root");
+ moduleHierarchy = new DefaultInputModuleHierarchy(root);
+
+ assertThat(moduleHierarchy.children(root)).isEmpty();
+ assertThat(moduleHierarchy.parent(root)).isNull();
+ assertThat(moduleHierarchy.root()).isEqualTo(root);
+ }
+
+ @Test
+ public void testParentChild() {
+ DefaultInputModule root = new DefaultInputModule("root");
+ DefaultInputModule mod1 = new DefaultInputModule("mod1");
+ moduleHierarchy = new DefaultInputModuleHierarchy(root, mod1);
+
+ assertThat(moduleHierarchy.children(root)).containsOnly(mod1);
+ assertThat(moduleHierarchy.children(mod1)).isEmpty();
+
+ assertThat(moduleHierarchy.parent(mod1)).isEqualTo(root);
+ assertThat(moduleHierarchy.parent(root)).isNull();
+ assertThat(moduleHierarchy.root()).isEqualTo(root);
+ }
}
*/
package org.sonar.scanner.scan;
-import org.junit.Before;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+
import org.junit.Test;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.scan.filesystem.PathResolver;
-import org.sonar.scanner.scan.filesystem.BatchIdGenerator;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class ModuleIndexerTest {
private ModuleIndexer indexer;
private DefaultComponentTree tree;
private DefaultInputModuleHierarchy moduleHierarchy;
- private ImmutableProjectReactor reactor;
private InputComponentStore componentStore;
- @Before
- public void setUp() {
- reactor = mock(ImmutableProjectReactor.class);
- componentStore = new InputComponentStore(new PathResolver());
+ public void createIndexer(DefaultInputModule rootModule) {
+ componentStore = new InputComponentStore(new PathResolver(), rootModule);
tree = new DefaultComponentTree();
- moduleHierarchy = new DefaultInputModuleHierarchy();
- indexer = new ModuleIndexer(reactor, tree, componentStore, new BatchIdGenerator(), moduleHierarchy);
+ moduleHierarchy = mock(DefaultInputModuleHierarchy.class);
+ indexer = new ModuleIndexer(tree, componentStore, moduleHierarchy);
}
-
+
@Test
public void testIndex() {
- ProjectDefinition root = ProjectDefinition.create().setKey("root");
- ProjectDefinition mod1 = ProjectDefinition.create().setKey("mod1");
- ProjectDefinition mod2 = ProjectDefinition.create().setKey("mod2");
- ProjectDefinition mod3 = ProjectDefinition.create().setKey("mod3");
- ProjectDefinition mod4 = ProjectDefinition.create().setKey("mod4");
+ ProjectDefinition rootDef = mock(ProjectDefinition.class);
+ ProjectDefinition def = mock(ProjectDefinition.class);
+ when(rootDef.getParent()).thenReturn(null);
+ when(def.getParent()).thenReturn(rootDef);
+
+ DefaultInputModule root = mock(DefaultInputModule.class);
+ DefaultInputModule mod1 = mock(DefaultInputModule.class);
+ DefaultInputModule mod2 = mock(DefaultInputModule.class);
+ DefaultInputModule mod3 = mock(DefaultInputModule.class);
+
+ when(root.key()).thenReturn("root");
+ when(mod1.key()).thenReturn("mod1");
+ when(mod2.key()).thenReturn("mod2");
+ when(mod3.key()).thenReturn("mod3");
+
+ when(root.getKeyWithBranch()).thenReturn("root");
+ when(mod1.getKeyWithBranch()).thenReturn("mod1");
+ when(mod2.getKeyWithBranch()).thenReturn("mod2");
+ when(mod3.getKeyWithBranch()).thenReturn("mod3");
+
+ when(root.definition()).thenReturn(rootDef);
+ when(mod1.definition()).thenReturn(def);
+ when(mod2.definition()).thenReturn(def);
+ when(mod3.definition()).thenReturn(def);
- root.addSubProject(mod1);
- mod1.addSubProject(mod2);
- root.addSubProject(mod3);
- root.addSubProject(mod4);
+ createIndexer(root);
+ when(moduleHierarchy.root()).thenReturn(root);
+ when(moduleHierarchy.children(root)).thenReturn(Arrays.asList(mod1, mod2, mod3));
- when(reactor.getRoot()).thenReturn(root);
indexer.start();
InputModule rootModule = moduleHierarchy.root();
*/
package org.sonar.scanner.scan;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.home.cache.DirectoryLock;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class ProjectLockTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
}
private ProjectLock setUpTest(File file) {
- ProjectReactor projectReactor = mock(ProjectReactor.class);
- ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
- when(projectReactor.getRoot()).thenReturn(projectDefinition);
- when(projectDefinition.getWorkDir()).thenReturn(file);
+ InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class);
+ DefaultInputModule root = mock(DefaultInputModule.class);
+ when(hierarchy.root()).thenReturn(root);
+ when(root.getWorkDir()).thenReturn(file);
- return new ProjectLock(projectReactor);
+ return new ProjectLock(hierarchy);
}
@Test
*/
package org.sonar.scanner.scan;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.config.Settings;
-import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.utils.MessageException;
import org.sonar.scanner.analysis.DefaultAnalysisMode;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class ProjectReactorValidatorTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private ProjectReactorValidator validator;
- private Settings settings;
private DefaultAnalysisMode mode;
@Before
public void prepare() {
mode = mock(DefaultAnalysisMode.class);
- settings = new MapSettings();
- validator = new ProjectReactorValidator(settings, mode);
+ validator = new ProjectReactorValidator(mode);
}
@Test
validator.validate(createProjectReactor("3-3"));
validator.validate(createProjectReactor("-:"));
}
-
+
@Test
public void allow_slash_issues_mode() {
when(mode.isIssues()).thenReturn(true);
validator.validate(createProjectReactor("project/key"));
-
+
when(mode.isIssues()).thenReturn(false);
thrown.expect(MessageException.class);
thrown.expectMessage("is not a valid project or module key");
validator.validate(reactor);
}
- @Test
- public void fail_with_deprecated_sonar_phase() {
- ProjectReactor reactor = createProjectReactor("foo");
- settings.setProperty("sonar.phase", "phase");
-
- thrown.expect(MessageException.class);
- thrown.expectMessage("\"sonar.phase\" is deprecated");
- validator.validate(reactor);
- }
-
private ProjectReactor createProjectReactor(String projectKey) {
ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey);
ProjectReactor reactor = new ProjectReactor(def);
ProjectDefinition def = ProjectDefinition.create()
.setProperty(CoreProperties.PROJECT_KEY_PROPERTY, projectKey)
.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch);
- ProjectReactor reactor = new ProjectReactor(def);
- settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, branch);
- return reactor;
+ return new ProjectReactor(def);
}
}
*/
package org.sonar.scanner.scan;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.io.IOException;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
import org.sonar.home.cache.DirectoryLock;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class WorkDirectoryCleanerTest {
private WorkDirectoryCleaner cleaner;
@Rule
lock.createNewFile();
// mock project
- ProjectReactor projectReactor = mock(ProjectReactor.class);
- ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
- when(projectReactor.getRoot()).thenReturn(projectDefinition);
- when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot());
+ InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class);
+ DefaultInputModule root = mock(DefaultInputModule.class);
+ when(hierarchy.root()).thenReturn(root);
+ when(root.getWorkDir()).thenReturn(temp.getRoot());
assertThat(temp.getRoot().list().length).isGreaterThan(1);
- cleaner = new WorkDirectoryCleaner(projectReactor);
+ cleaner = new WorkDirectoryCleaner(hierarchy);
}
-
+
@Test
public void testNonExisting() {
temp.delete();
cleaner.execute();
}
-
+
@Test
public void testClean() {
File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME);
*/
package org.sonar.scanner.scan.filesystem;
+import static org.assertj.core.api.Assertions.assertThat;
+
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.scan.filesystem.FileExclusions;
-import static org.assertj.core.api.Assertions.assertThat;
-
public class ExclusionFiltersTest {
@Rule
@Test
public void no_inclusions_nor_exclusions() throws IOException {
filter.prepare();
- IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java");
+
+ IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue();
assertThat(filter.accept(indexedFile, InputFile.Type.TEST)).isTrue();
}
settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java");
filter.prepare();
- IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java");
+ IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue();
- indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java");
+ indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse();
}
settings.setProperty(CoreProperties.PROJECT_INCLUSIONS_PROPERTY, "**/*Dao.java,**/*Dto.java");
filter.prepare();
- IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java");
+ IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse();
- indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDto.java");
+ indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDto.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue();
}
settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "**/*Dao.java");
filter.prepare();
- IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java");
+ IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/FooDao.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse();
- indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java");
+ indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/com/mycompany/Foo.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue();
// source exclusions do not apply to tests
- indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/test/java/com/mycompany/FooDao.java");
+ indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/test/java/com/mycompany/FooDao.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.TEST)).isTrue();
}
settings.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, "file:" + excludedFile.getAbsolutePath());
filter.prepare();
- IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Foo.java");
+ IndexedFile indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Foo.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isTrue();
- indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Bar.java");
+ indexedFile = new DefaultIndexedFile("foo", moduleBaseDir, "src/main/java/org/bar/Bar.java", null);
assertThat(filter.accept(indexedFile, InputFile.Type.MAIN)).isFalse();
}
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputFile.Status;
import org.sonar.api.batch.fs.InputFile.Type;
@Test
public void should_add_input_file() throws Exception {
- InputComponentStore cache = new InputComponentStore(new PathResolver());
-
String rootModuleKey = "struts";
+ String subModuleKey = "struts-core";
+
File rootBaseDir = temp.newFolder();
- DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(rootModuleKey, rootBaseDir);
- cache.put(rootModule);
- String subModuleKey = "struts-core";
- DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(subModuleKey, temp.newFolder());
- rootModule.definition().addSubProject(subModule.definition());
+ ProjectDefinition moduleDef = ProjectDefinition.create()
+ .setKey(subModuleKey).setBaseDir(rootBaseDir);
+ ProjectDefinition rootDef = ProjectDefinition.create()
+ .setKey(rootModuleKey).setBaseDir(rootBaseDir).addSubProject(moduleDef);
+
+ DefaultInputModule rootModule = TestInputFileBuilder.newDefaultInputModule(rootDef);
+ DefaultInputModule subModule = TestInputFileBuilder.newDefaultInputModule(moduleDef);
+
+ InputComponentStore cache = new InputComponentStore(new PathResolver(), rootModule);
cache.put(subModule);
DefaultInputFile fooFile = new TestInputFileBuilder(rootModuleKey, "src/main/java/Foo.java")
static class InputComponentStoreTester extends InputComponentStore {
InputComponentStoreTester() throws IOException {
- super(new PathResolver());
- DefaultInputModule root = TestInputFileBuilder.newDefaultInputModule("root", temp.newFolder());
- put(root);
+ super(new PathResolver(), TestInputFileBuilder.newDefaultInputModule("root", temp.newFolder()));
}
InputFile addFile(String moduleKey, String relpath, String language) {
*/
package org.sonar.scanner.scan.filesystem;
+import static junit.framework.Assert.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.spy;
+
import java.io.File;
import java.io.IOException;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.fs.internal.DefaultIndexedFile;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
import org.sonar.scanner.repository.language.DefaultLanguagesRepository;
import org.sonar.scanner.repository.language.LanguagesRepository;
-import static junit.framework.Assert.fail;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.spy;
-
public class LanguageDetectionTest {
@Rule
LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("java", "java", "jav"), new MockLanguage("cobol", "cbl", "cob")));
LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages);
- assertThat(detection.language(newIndexedFile("Foo.java"))).isEqualTo("java");
- assertThat(detection.language(newIndexedFile("src/Foo.java"))).isEqualTo("java");
- assertThat(detection.language(newIndexedFile("Foo.JAVA"))).isEqualTo("java");
- assertThat(detection.language(newIndexedFile("Foo.jav"))).isEqualTo("java");
- assertThat(detection.language(newIndexedFile("Foo.Jav"))).isEqualTo("java");
+ assertThat(detectLanguage(detection, "Foo.java")).isEqualTo("java");
+ assertThat(detectLanguage(detection, "src/Foo.java")).isEqualTo("java");
+ assertThat(detectLanguage(detection, "Foo.JAVA")).isEqualTo("java");
+ assertThat(detectLanguage(detection, "Foo.jav")).isEqualTo("java");
+ assertThat(detectLanguage(detection, "Foo.Jav")).isEqualTo("java");
- assertThat(detection.language(newIndexedFile("abc.cbl"))).isEqualTo("cobol");
- assertThat(detection.language(newIndexedFile("abc.CBL"))).isEqualTo("cobol");
+ assertThat(detectLanguage(detection, "abc.cbl")).isEqualTo("cobol");
+ assertThat(detectLanguage(detection, "abc.CBL")).isEqualTo("cobol");
- assertThat(detection.language(newIndexedFile("abc.php"))).isNull();
- assertThat(detection.language(newIndexedFile("abc"))).isNull();
+ assertThat(detectLanguage(detection, "abc.php")).isNull();
+ assertThat(detectLanguage(detection, "abc")).isNull();
}
@Test
public void should_not_fail_if_no_language() throws Exception {
LanguageDetection detection = spy(new LanguageDetection(settings.asConfig(), new DefaultLanguagesRepository(new Languages())));
- assertThat(detection.language(newIndexedFile("Foo.java"))).isNull();
+ assertThat(detectLanguage(detection, "Foo.java")).isNull();
}
@Test
LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("abap", "abap", "ABAP")));
LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages);
- assertThat(detection.language(newIndexedFile("abc.abap"))).isEqualTo("abap");
+ assertThat(detectLanguage(detection, "abc.abap")).isEqualTo("abap");
}
@Test
// No side-effect on non-ABAP projects
LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages);
- assertThat(detection.language(newIndexedFile("abc"))).isNull();
- assertThat(detection.language(newIndexedFile("abc.abap"))).isNull();
- assertThat(detection.language(newIndexedFile("abc.java"))).isEqualTo("java");
+ assertThat(detectLanguage(detection, "abc")).isNull();
+ assertThat(detectLanguage(detection, "abc.abap")).isNull();
+ assertThat(detectLanguage(detection, "abc.java")).isEqualTo("java");
settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "abap");
detection = new LanguageDetection(settings.asConfig(), languages);
- assertThat(detection.language(newIndexedFile("abc"))).isEqualTo("abap");
- assertThat(detection.language(newIndexedFile("abc.txt"))).isEqualTo("abap");
- assertThat(detection.language(newIndexedFile("abc.java"))).isEqualTo("abap");
+ assertThat(detectLanguage(detection, "abc")).isEqualTo("abap");
+ assertThat(detectLanguage(detection, "abc.txt")).isEqualTo("abap");
+ assertThat(detectLanguage(detection, "abc.java")).isEqualTo("abap");
}
@Test
settings.setProperty(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "java");
LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages);
- assertThat(detection.language(newIndexedFile("abc"))).isNull();
- assertThat(detection.language(newIndexedFile("abc.php"))).isNull();
- assertThat(detection.language(newIndexedFile("abc.java"))).isEqualTo("java");
- assertThat(detection.language(newIndexedFile("src/abc.java"))).isEqualTo("java");
+ assertThat(detectLanguage(detection, "abc")).isNull();
+ assertThat(detectLanguage(detection, "abc.php")).isNull();
+ assertThat(detectLanguage(detection, "abc.java")).isEqualTo("java");
+ assertThat(detectLanguage(detection, "src/abc.java")).isEqualTo("java");
}
@Test
LanguagesRepository languages = new DefaultLanguagesRepository(new Languages(new MockLanguage("xml", "xhtml"), new MockLanguage("web", "xhtml")));
LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages);
try {
- detection.language(newIndexedFile("abc.xhtml"));
+ detectLanguage(detection, "abc.xhtml");
fail();
} catch (MessageException e) {
assertThat(e.getMessage())
settings.setProperty("sonar.lang.patterns.xml", "xml/**");
settings.setProperty("sonar.lang.patterns.web", "web/**");
LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages);
- assertThat(detection.language(newIndexedFile("xml/abc.xhtml"))).isEqualTo("xml");
- assertThat(detection.language(newIndexedFile("web/abc.xhtml"))).isEqualTo("web");
+ assertThat(detectLanguage(detection, "xml/abc.xhtml")).isEqualTo("xml");
+ assertThat(detectLanguage(detection, "web/abc.xhtml")).isEqualTo("web");
}
@Test
LanguageDetection detection = new LanguageDetection(settings.asConfig(), languages);
- assertThat(detection.language(newIndexedFile("abc.abap"))).isEqualTo("abap");
- assertThat(detection.language(newIndexedFile("abc.cobol"))).isEqualTo("cobol");
+ assertThat(detectLanguage(detection, "abc.abap")).isEqualTo("abap");
+ assertThat(detectLanguage(detection, "abc.cobol")).isEqualTo("cobol");
try {
- detection.language(newIndexedFile("abc.txt"));
+ detectLanguage(detection, "abc.txt");
fail();
} catch (MessageException e) {
assertThat(e.getMessage())
}
}
- private DefaultIndexedFile newIndexedFile(String path) throws IOException {
- File basedir = temp.newFolder();
- return new DefaultIndexedFile("foo", basedir.toPath(), path);
+ private String detectLanguage(LanguageDetection detection, String path) {
+ return detection.language(new File(temp.getRoot(), path).getAbsolutePath(), path);
}
static class MockLanguage implements Language {
*/
package org.sonar.scanner.scan.filesystem;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
import java.io.File;
import java.io.IOException;
+
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.junit.Rule;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.api.utils.TempFolder;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
public class ModuleFileSystemInitializerTest {
@Rule
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.scanner.sensor.SensorStrategy;
@Before
public void setUp() throws IOException {
- componentStore = new InputComponentStore(new PathResolver());
- componentStore.put(TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder()));
+ DefaultInputModule root = TestInputFileBuilder.newDefaultInputModule(moduleKey, temp.newFolder());
+ componentStore = new InputComponentStore(new PathResolver(), root);
}
@Test
*/
package org.sonar.scanner.scan.report;
+import static net.javacrumbs.jsonunit.assertj.JsonAssert.assertThatJson;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.TimeZone;
+
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.sonar.scanner.scan.DefaultComponentTree;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
-import static net.javacrumbs.jsonunit.assertj.JsonAssert.assertThatJson;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
public class JSONReportTest {
private SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
when(server.getVersion()).thenReturn("3.6");
- InputComponentStore inputComponentStore = new InputComponentStore(new PathResolver());
DefaultComponentTree inputComponentTree = new DefaultComponentTree();
- DefaultInputModule rootModule = new DefaultInputModule(ProjectDefinition.create().setBaseDir(projectBaseDir).setKey("struts"), 1);
- inputComponentStore.put(rootModule);
+ ProjectDefinition def = ProjectDefinition.create().setBaseDir(projectBaseDir).setKey("struts");
+ DefaultInputModule rootModule = new DefaultInputModule(def, 1);
+ InputComponentStore inputComponentStore = new InputComponentStore(new PathResolver(), rootModule);
+
DefaultInputModule moduleA = new DefaultInputModule("struts-core");
inputComponentTree.index(moduleA, rootModule);
DefaultInputModule moduleB = new DefaultInputModule("struts-ui");
@Before
public void prepare() {
settings = new MapSettings(new PropertyDefinitions(ExclusionProperties.all()));
- coverageExclusions = new CoverageExclusions(settings.asConfig());
}
@Test
public void shouldExcludeFileBasedOnPattern() {
InputFile file = new TestInputFileBuilder("foo", "src/org/polop/File.php").build();
settings.setProperty("sonar.coverage.exclusions", "src/org/polop/*");
- coverageExclusions.initPatterns();
+ coverageExclusions = new CoverageExclusions(settings.asConfig());
assertThat(coverageExclusions.isExcluded(file)).isTrue();
}
public void shouldNotExcludeFileBasedOnPattern() {
InputFile file = new TestInputFileBuilder("foo", "src/org/polop/File.php").build();
settings.setProperty("sonar.coverage.exclusions", "src/org/other/*");
- coverageExclusions.initPatterns();
+ coverageExclusions = new CoverageExclusions(settings.asConfig());
assertThat(coverageExclusions.isExcluded(file)).isFalse();
}
}
import java.io.File;
+import javax.annotation.concurrent.Immutable;
+
/**
* Structure of files in the zipped report
*/
+@Immutable
public class FileStructure {
public enum Domain {
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
+
+import javax.annotation.concurrent.Immutable;
+
import org.sonar.core.util.ContextException;
import org.sonar.core.util.Protobuf;
+@Immutable
public class ScannerReportWriter {
private final FileStructure fileStructure;