diff options
96 files changed, 1677 insertions, 1862 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/AbstractNewCoverageFileAnalyzer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/AbstractNewCoverageFileAnalyzer.java index 1ac4c1a4f3a..c6ecc1ba900 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/AbstractNewCoverageFileAnalyzer.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/AbstractNewCoverageFileAnalyzer.java @@ -36,7 +36,7 @@ import org.sonar.batch.index.ResourceCache; import org.sonar.batch.protocol.output.BatchReport.Scm; import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset; import org.sonar.batch.protocol.output.BatchReportReader; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; import javax.annotation.Nullable; @@ -53,17 +53,17 @@ import java.util.Map; public abstract class AbstractNewCoverageFileAnalyzer implements Decorator { private final List<PeriodStruct> structs; - private final PublishReportJob publishReportJob; + private final ReportPublisher publishReportJob; private final ResourceCache resourceCache; - public AbstractNewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) { + public AbstractNewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) { this(Lists.<PeriodStruct>newArrayList(), publishReportJob, resourceCache); for (Period period : timeMachineConfiguration.periods()) { structs.add(new PeriodStruct(period.getIndex(), period.getDate())); } } - AbstractNewCoverageFileAnalyzer(List<PeriodStruct> structs, PublishReportJob publishReportJob, ResourceCache resourceCache) { + AbstractNewCoverageFileAnalyzer(List<PeriodStruct> structs, ReportPublisher publishReportJob, ResourceCache resourceCache) { this.resourceCache = resourceCache; this.publishReportJob = publishReportJob; this.structs = structs; diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java index 38aac00f0ea..d13df7224ef 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzer.java @@ -23,17 +23,17 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.batch.components.TimeMachineConfiguration; import org.sonar.batch.index.ResourceCache; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; import java.util.List; public class NewCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer { - public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) { + public NewCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) { super(timeMachineConfiguration, publishReportJob, resourceCache); } - NewCoverageFileAnalyzer(List<PeriodStruct> structs, PublishReportJob publishReportJob, ResourceCache resourceCache) { + NewCoverageFileAnalyzer(List<PeriodStruct> structs, ReportPublisher publishReportJob, ResourceCache resourceCache) { super(structs, publishReportJob, resourceCache); } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewItCoverageFileAnalyzer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewItCoverageFileAnalyzer.java index 48222f59ce2..f069a633b4e 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewItCoverageFileAnalyzer.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewItCoverageFileAnalyzer.java @@ -23,11 +23,11 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.batch.components.TimeMachineConfiguration; import org.sonar.batch.index.ResourceCache; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; public class NewItCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer { - public NewItCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) { + public NewItCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) { super(timeMachineConfiguration, publishReportJob, resourceCache); } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewOverallCoverageFileAnalyzer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewOverallCoverageFileAnalyzer.java index 75a1a83686c..fc61e4eafe3 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewOverallCoverageFileAnalyzer.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/NewOverallCoverageFileAnalyzer.java @@ -23,11 +23,11 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.batch.components.TimeMachineConfiguration; import org.sonar.batch.index.ResourceCache; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; public class NewOverallCoverageFileAnalyzer extends AbstractNewCoverageFileAnalyzer { - public NewOverallCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, PublishReportJob publishReportJob, ResourceCache resourceCache) { + public NewOverallCoverageFileAnalyzer(TimeMachineConfiguration timeMachineConfiguration, ReportPublisher publishReportJob, ResourceCache resourceCache) { super(timeMachineConfiguration, publishReportJob, resourceCache); } diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java index af6d25828a6..6f02ac2f626 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/NewCoverageFileAnalyzerTest.java @@ -35,7 +35,7 @@ import org.sonar.batch.index.ResourceCache; import org.sonar.batch.protocol.output.BatchReport.Scm; import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset; import org.sonar.batch.protocol.output.BatchReportWriter; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -69,7 +69,7 @@ public class NewCoverageFileAnalyzerTest { List<AbstractNewCoverageFileAnalyzer.PeriodStruct> structs = Arrays.asList( new AbstractNewCoverageFileAnalyzer.PeriodStruct(1, newDate("2009-12-25")), new AbstractNewCoverageFileAnalyzer.PeriodStruct(3, newDate("2011-02-18"))); - PublishReportJob publishReportJob = mock(PublishReportJob.class); + ReportPublisher publishReportJob = mock(ReportPublisher.class); java.io.File reportBaseDir = temp.newFolder(); when(publishReportJob.getReportDir()).thenReturn(reportBaseDir); writer = new BatchReportWriter(reportBaseDir); diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SyntaxHighlightingSensorTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SyntaxHighlightingSensorTest.java index d31724c73e8..9d27f526e33 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SyntaxHighlightingSensorTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SyntaxHighlightingSensorTest.java @@ -25,12 +25,14 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; import java.io.File; import java.io.IOException; +import java.io.StringReader; import static org.assertj.core.api.Assertions.assertThat; @@ -66,12 +68,13 @@ public class SyntaxHighlightingSensorTest { public void testExecution() throws IOException { File symbol = new File(baseDir, "src/foo.xoo.highlighting"); FileUtils.write(symbol, "1:4:k\n12:15:cppd\n\n#comment"); - DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo").setLastValidOffset(100); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setLanguage("xoo") + .initMetadata(new FileMetadata().readMetadata(new StringReader(" xoo\nazertyazer\nfoo"))); context.fileSystem().add(inputFile); sensor.execute(context); - assertThat(context.highlightingTypeFor("foo:src/foo.xoo", 2)).containsOnly(TypeOfText.KEYWORD); - assertThat(context.highlightingTypeFor("foo:src/foo.xoo", 13)).containsOnly(TypeOfText.CPP_DOC); + assertThat(context.highlightingTypeAt("foo:src/foo.xoo", 1, 2)).containsOnly(TypeOfText.KEYWORD); + assertThat(context.highlightingTypeAt("foo:src/foo.xoo", 2, 8)).containsOnly(TypeOfText.CPP_DOC); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index 0358d1428f6..6a19dc49ab7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -29,6 +29,7 @@ import org.sonar.batch.maven.MavenProjectConverter; import org.sonar.batch.scan.report.*; import org.sonar.batch.scm.ScmConfiguration; import org.sonar.batch.scm.ScmSensor; +import org.sonar.batch.source.CodeColorizerSensor; import org.sonar.batch.source.LinesSensor; import org.sonar.core.computation.dbcleaner.DefaultPurgeTask; import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; @@ -59,6 +60,7 @@ public class BatchComponents { ScmSensor.class, LinesSensor.class, + CodeColorizerSensor.class, // Issues tracking IssueTracking.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRuleValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/InputFileComponent.java index 8700c81f345..c79b9afd6b6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRuleValueCoder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/deprecated/InputFileComponent.java @@ -17,29 +17,44 @@ * 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.batch.highlighting; +package org.sonar.batch.deprecated; -import com.persistit.Value; -import com.persistit.encoding.CoderContext; -import com.persistit.encoding.ValueCoder; -import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.component.Component; +import org.sonar.api.resources.Qualifiers; -class SyntaxHighlightingRuleValueCoder implements ValueCoder { +public class InputFileComponent implements Component { + + private final DefaultInputFile inputFile; + + public InputFileComponent(DefaultInputFile inputFile) { + this.inputFile = inputFile; + } @Override - public void put(Value value, Object object, CoderContext context) { - SyntaxHighlightingRule rule = (SyntaxHighlightingRule) object; - value.put(rule.getStartPosition()); - value.put(rule.getEndPosition()); - value.put(rule.getTextType().ordinal()); + public String key() { + return inputFile.key(); } @Override - public Object get(Value value, Class clazz, CoderContext context) { - int startPosition = value.getInt(); - int endPosition = value.getInt(); - TypeOfText type = TypeOfText.values()[value.getInt()]; - return SyntaxHighlightingRule.create(startPosition, endPosition, type); + public String path() { + return inputFile.relativePath(); } + + @Override + public String name() { + return inputFile.file().getName(); + } + + @Override + public String longName() { + return inputFile.relativePath(); + } + + @Override + public String qualifier() { + return inputFile.type() == Type.MAIN ? Qualifiers.FILE : Qualifiers.UNIT_TEST_FILE; + } + } diff --git a/sonar-core/src/main/java/org/sonar/core/component/ScanPerspectives.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/perspectives/BatchPerspectives.java index 09cf4c72b54..d743cca7301 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ScanPerspectives.java +++ b/sonar-batch/src/main/java/org/sonar/batch/deprecated/perspectives/BatchPerspectives.java @@ -17,7 +17,11 @@ * 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.core.component; +package org.sonar.batch.deprecated.perspectives; + +import org.sonar.core.component.PerspectiveBuilder; +import org.sonar.core.component.PerspectiveNotFoundException; +import org.sonar.core.component.ResourceComponent; import com.google.common.collect.Maps; import org.sonar.api.batch.SonarIndex; @@ -32,14 +36,15 @@ import org.sonar.api.resources.File; import org.sonar.api.resources.Resource; import javax.annotation.CheckForNull; + import java.util.Map; -public class ScanPerspectives implements ResourcePerspectives { +public class BatchPerspectives implements ResourcePerspectives { private final Map<Class<?>, PerspectiveBuilder<?>> builders = Maps.newHashMap(); private final SonarIndex resourceIndex; - public ScanPerspectives(PerspectiveBuilder[] builders, SonarIndex resourceIndex) { + public BatchPerspectives(PerspectiveBuilder[] builders, SonarIndex resourceIndex) { this.resourceIndex = resourceIndex; for (PerspectiveBuilder builder : builders) { // TODO check duplications diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java deleted file mode 100644 index 7b9b334e15a..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingData.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.highlighting; - -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; -import org.sonar.batch.index.Data; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class SyntaxHighlightingData implements Data { - - public static final String FIELD_SEPARATOR = ","; - public static final String RULE_SEPARATOR = ";"; - - private List<SyntaxHighlightingRule> syntaxHighlightingRuleSet; - - public SyntaxHighlightingData(Collection<SyntaxHighlightingRule> syntaxHighlightingRuleSet) { - this.syntaxHighlightingRuleSet = new ArrayList<SyntaxHighlightingRule>(syntaxHighlightingRuleSet); - } - - public List<SyntaxHighlightingRule> syntaxHighlightingRuleSet() { - return syntaxHighlightingRuleSet; - } - - @Override - public String writeString() { - StringBuilder sb = new StringBuilder(); - for (SyntaxHighlightingRule highlightingRule : syntaxHighlightingRuleSet) { - if (sb.length() > 0) { - sb.append(RULE_SEPARATOR); - } - sb.append(highlightingRule.getStartPosition()) - .append(FIELD_SEPARATOR) - .append(highlightingRule.getEndPosition()) - .append(FIELD_SEPARATOR) - .append(highlightingRule.getTextType().cssClass()); - } - - return sb.toString(); - } - -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java deleted file mode 100644 index be0fa73a3e9..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.highlighting; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Ordering; -import com.google.common.collect.Sets; -import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; - -import javax.annotation.Nullable; - -import java.util.Iterator; -import java.util.Set; - -public class SyntaxHighlightingDataBuilder { - - private Set<SyntaxHighlightingRule> syntaxHighlightingRuleSet; - - public SyntaxHighlightingDataBuilder() { - syntaxHighlightingRuleSet = Sets.newTreeSet(new Ordering<SyntaxHighlightingRule>() { - @Override - public int compare(@Nullable SyntaxHighlightingRule left, - @Nullable SyntaxHighlightingRule right) { - int result = left.getStartPosition() - right.getStartPosition(); - if (result == 0) { - result = right.getEndPosition() - left.getEndPosition(); - } - return result; - } - }); - } - - @VisibleForTesting - public Set<SyntaxHighlightingRule> getSyntaxHighlightingRuleSet() { - return syntaxHighlightingRuleSet; - } - - public SyntaxHighlightingDataBuilder registerHighlightingRule(int startOffset, int endOffset, TypeOfText typeOfText) { - SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(startOffset, endOffset, - typeOfText); - this.syntaxHighlightingRuleSet.add(syntaxHighlightingRule); - return this; - } - - public SyntaxHighlightingData build() { - checkOverlappingBoudaries(); - return new SyntaxHighlightingData(syntaxHighlightingRuleSet); - } - - private void checkOverlappingBoudaries() { - if (syntaxHighlightingRuleSet.size() > 1) { - Iterator<SyntaxHighlightingRule> it = syntaxHighlightingRuleSet.iterator(); - SyntaxHighlightingRule previous = it.next(); - while (it.hasNext()) { - SyntaxHighlightingRule current = it.next(); - if (previous.getEndPosition() > current.getStartPosition() && !(previous.getEndPosition() >= current.getEndPosition())) { - String errorMsg = String.format("Cannot register highlighting rule for characters from %s to %s as it " + - "overlaps at least one existing rule", current.getStartPosition(), current.getEndPosition()); - throw new IllegalStateException(errorMsg); - } - previous = current; - } - } - } - -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataValueCoder.java deleted file mode 100644 index a748b499d57..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataValueCoder.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.highlighting; - -import com.persistit.Value; -import com.persistit.encoding.CoderContext; -import com.persistit.encoding.ValueCoder; -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; - -import java.util.ArrayList; -import java.util.List; - -public class SyntaxHighlightingDataValueCoder implements ValueCoder { - - private SyntaxHighlightingRuleValueCoder rulesCoder = new SyntaxHighlightingRuleValueCoder(); - - @Override - public void put(Value value, Object object, CoderContext context) { - SyntaxHighlightingData data = (SyntaxHighlightingData) object; - value.put(data.syntaxHighlightingRuleSet().size()); - for (SyntaxHighlightingRule rule : data.syntaxHighlightingRuleSet()) { - rulesCoder.put(value, rule, context); - } - } - - @Override - public Object get(Value value, Class clazz, CoderContext context) { - int count = value.getInt(); - List<SyntaxHighlightingRule> rules = new ArrayList<SyntaxHighlightingRule>(count); - for (int i = 0; i < count; i++) { - rules.add((SyntaxHighlightingRule) rulesCoder.get(value, SyntaxHighlightingRule.class, context)); - } - return new SyntaxHighlightingData(rules); - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java b/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java index a07eded789c..ff8af997c2f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java @@ -19,6 +19,7 @@ */ package org.sonar.batch.index; +import org.sonar.api.batch.fs.InputPath; import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; @@ -36,6 +37,7 @@ public class BatchResource { private Snapshot s; private final BatchResource parent; private final Collection<BatchResource> children = new ArrayList<BatchResource>(); + private InputPath inputPath; public BatchResource(int batchId, Resource r, @Nullable BatchResource parent) { this.batchId = batchId; @@ -91,4 +93,14 @@ public class BatchResource { public boolean isDir() { return ResourceUtils.isDirectory(r); } + + public BatchResource setInputPath(InputPath inputPath) { + this.inputPath = inputPath; + return this; + } + + @CheckForNull + public InputPath inputPath() { + return inputPath; + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java b/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java deleted file mode 100644 index b94aa0973b1..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/index/ComponentDataCache.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.index; - -import org.sonar.api.BatchComponent; -import org.sonar.batch.highlighting.SyntaxHighlightingData; -import org.sonar.batch.highlighting.SyntaxHighlightingDataValueCoder; - -import javax.annotation.CheckForNull; - -public class ComponentDataCache implements BatchComponent { - private final Cache cache; - - public ComponentDataCache(Caches caches) { - caches.registerValueCoder(SyntaxHighlightingData.class, new SyntaxHighlightingDataValueCoder()); - cache = caches.createCache("componentData"); - } - - public <D extends Data> ComponentDataCache setData(String componentKey, String dataType, D data) { - cache.put(componentKey, dataType, data); - return this; - } - - public ComponentDataCache setStringData(String componentKey, String dataType, String data) { - return setData(componentKey, dataType, new StringData(data)); - } - - @CheckForNull - public <D extends Data> D getData(String componentKey, String dataType) { - return (D) cache.get(componentKey, dataType); - } - - @CheckForNull - public String getStringData(String componentKey, String dataType) { - Data data = (Data) cache.get(componentKey, dataType); - return data == null ? null : ((StringData) data).data(); - } - - public <D extends Data> Iterable<Cache.Entry<D>> entries(String componentKey) { - return cache.entries(componentKey); - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java b/sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java index d2197170309..8a0184be3e3 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java @@ -26,22 +26,19 @@ import org.sonar.api.BatchComponent; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.duplication.Duplication; import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication; -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; -import org.sonar.api.source.Symbol; import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.duplication.DuplicationCache; -import org.sonar.batch.highlighting.SyntaxHighlightingData; +import org.sonar.batch.protocol.output.BatchReport.Range; import org.sonar.batch.protocol.output.BatchReport.Scm; import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset; -import org.sonar.batch.protocol.output.BatchReportReader; -import org.sonar.batch.report.PublishReportJob; -import org.sonar.batch.scan.filesystem.InputFileMetadata; +import org.sonar.batch.protocol.output.BatchReport.Symbols; +import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule; +import org.sonar.batch.protocol.output.*; +import org.sonar.batch.report.BatchReportUtils; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.scan.measure.MeasureCache; -import org.sonar.batch.source.CodeColorizers; -import org.sonar.batch.symbol.SymbolData; -import org.sonar.core.source.SnapshotDataTypes; import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.source.db.FileSourceDb; import org.sonar.server.source.db.FileSourceDb.Data.Builder; @@ -58,30 +55,25 @@ public class SourceDataFactory implements BatchComponent { private static final String BOM = "\uFEFF"; private final MeasureCache measureCache; - private final ComponentDataCache componentDataCache; private final DuplicationCache duplicationCache; - private final CodeColorizers codeColorizers; - private final PublishReportJob publishReportJob; + private final ReportPublisher reportPublisher; private final ResourceCache resourceCache; - public SourceDataFactory(MeasureCache measureCache, ComponentDataCache componentDataCache, - DuplicationCache duplicationCache, CodeColorizers codeColorizers, PublishReportJob publishReportJob, ResourceCache resourceCache) { + public SourceDataFactory(MeasureCache measureCache, DuplicationCache duplicationCache, ReportPublisher reportPublisher, ResourceCache resourceCache) { this.measureCache = measureCache; - this.componentDataCache = componentDataCache; this.duplicationCache = duplicationCache; - this.codeColorizers = codeColorizers; - this.publishReportJob = publishReportJob; + this.reportPublisher = reportPublisher; this.resourceCache = resourceCache; } - public byte[] consolidateData(DefaultInputFile inputFile, InputFileMetadata metadata) throws IOException { + public byte[] consolidateData(DefaultInputFile inputFile) throws IOException { FileSourceDb.Data.Builder dataBuilder = createForSource(inputFile); applyLineMeasures(inputFile, dataBuilder); applyScm(inputFile, dataBuilder); applyDuplications(inputFile.key(), dataBuilder); - applyHighlighting(inputFile, metadata, dataBuilder); - applySymbolReferences(inputFile, metadata, dataBuilder); + applyHighlighting(inputFile, dataBuilder); + applySymbolReferences(inputFile, dataBuilder); return FileSourceDto.encodeData(dataBuilder.build()); } @@ -101,7 +93,7 @@ public class SourceDataFactory implements BatchComponent { } void applyScm(DefaultInputFile inputFile, Builder dataBuilder) { - BatchReportReader reader = new BatchReportReader(publishReportJob.getReportDir()); + BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir()); Scm componentScm = reader.readComponentScm(resourceCache.get(inputFile).batchId()); if (componentScm != null) { for (int i = 0; i < componentScm.getChangesetIndexByLineCount(); i++) { @@ -202,25 +194,22 @@ public class SourceDataFactory implements BatchComponent { void apply(String value, FileSourceDb.Line.Builder lineBuilder); } - void applyHighlighting(DefaultInputFile inputFile, InputFileMetadata metadata, FileSourceDb.Data.Builder to) { - SyntaxHighlightingData highlighting = componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING); - String language = inputFile.language(); - if (highlighting == null && language != null) { - highlighting = codeColorizers.toSyntaxHighlighting(inputFile.file(), inputFile.charset(), language); - } - if (highlighting == null) { + void applyHighlighting(DefaultInputFile inputFile, FileSourceDb.Data.Builder to) { + BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir()); + List<HighlightingRule> highlightingRules = reader.readComponentSyntaxHighlighting(resourceCache.get(inputFile).batchId()); + if (highlightingRules.isEmpty()) { return; } StringBuilder[] highlightingPerLine = new StringBuilder[inputFile.lines()]; RuleItemWriter ruleItemWriter = new RuleItemWriter(); int currentLineIdx = 1; - for (SyntaxHighlightingRule rule : highlighting.syntaxHighlightingRuleSet()) { - while (currentLineIdx < inputFile.lines() && rule.getStartPosition() >= metadata.originalLineOffsets()[currentLineIdx]) { + for (HighlightingRule rule : highlightingRules) { + while (currentLineIdx < inputFile.lines() && rule.getRange().getStartLine() > currentLineIdx) { // This rule starts on another line so advance currentLineIdx++; } // Now we know current rule starts on current line - writeDataPerLine(metadata.originalLineOffsets(), rule, rule.getStartPosition(), rule.getEndPosition(), highlightingPerLine, currentLineIdx, ruleItemWriter); + writeDataPerLine(inputFile.originalLineOffsets(), rule, rule.getRange(), highlightingPerLine, ruleItemWriter); } for (int i = 0; i < highlightingPerLine.length; i++) { StringBuilder sb = highlightingPerLine[i]; @@ -230,76 +219,60 @@ public class SourceDataFactory implements BatchComponent { } } - void applySymbolReferences(DefaultInputFile file, InputFileMetadata metadata, FileSourceDb.Data.Builder to) { - SymbolData symbolRefs = componentDataCache.getData(file.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING); - if (symbolRefs != null) { - StringBuilder[] refsPerLine = new StringBuilder[file.lines()]; - int symbolId = 1; - List<Symbol> symbols = new ArrayList<Symbol>(symbolRefs.referencesBySymbol().keySet()); - // Sort symbols to avoid false variation that would lead to an unnecessary update - Collections.sort(symbols, new Comparator<Symbol>() { - @Override - public int compare(Symbol o1, Symbol o2) { - return o1.getDeclarationStartOffset() - o2.getDeclarationStartOffset(); - } - }); - for (Symbol symbol : symbols) { - int declarationStartOffset = symbol.getDeclarationStartOffset(); - int declarationEndOffset = symbol.getDeclarationEndOffset(); - int length = declarationEndOffset - declarationStartOffset; - addSymbol(symbolId, declarationStartOffset, declarationEndOffset, metadata.originalLineOffsets(), refsPerLine); - for (Integer referenceStartOffset : symbolRefs.referencesBySymbol().get(symbol)) { - if (referenceStartOffset == declarationStartOffset) { - // Ignore old API that used to store reference as first declaration - continue; - } - addSymbol(symbolId, referenceStartOffset, referenceStartOffset + length, metadata.originalLineOffsets(), refsPerLine); + void applySymbolReferences(DefaultInputFile inputFile, FileSourceDb.Data.Builder to) { + BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir()); + List<Symbols.Symbol> symbols = new ArrayList<Symbols.Symbol>(reader.readComponentSymbols(resourceCache.get(inputFile).batchId())); + if (symbols.isEmpty()) { + return; + } + StringBuilder[] refsPerLine = new StringBuilder[inputFile.lines()]; + int symbolId = 1; + // Sort symbols to avoid false variation that would lead to an unnecessary update + Collections.sort(symbols, new Comparator<Symbols.Symbol>() { + @Override + public int compare(Symbols.Symbol o1, Symbols.Symbol o2) { + if (o1.getDeclaration().getStartLine() == o2.getDeclaration().getStartLine()) { + return Integer.compare(o1.getDeclaration().getStartOffset(), o2.getDeclaration().getStartOffset()); + } else { + return Integer.compare(o1.getDeclaration().getStartLine(), o2.getDeclaration().getStartLine()); } - symbolId++; } - for (int i = 0; i < refsPerLine.length; i++) { - StringBuilder sb = refsPerLine[i]; - if (sb != null) { - to.getLinesBuilder(i).setSymbols(sb.toString()); - } + }); + for (Symbols.Symbol symbol : symbols) { + addSymbol(symbolId, symbol.getDeclaration(), inputFile.originalLineOffsets(), refsPerLine); + for (Range reference : symbol.getReferenceList()) { + addSymbol(symbolId, reference, inputFile.originalLineOffsets(), refsPerLine); } + symbolId++; } - } - - private void addSymbol(int symbolId, int startOffset, int endOffset, int[] originalLineOffsets, StringBuilder[] result) { - int startLine = binarySearchLine(startOffset, originalLineOffsets); - writeDataPerLine(originalLineOffsets, symbolId, startOffset, endOffset, result, startLine, new SymbolItemWriter()); - } - - private int binarySearchLine(int declarationStartOffset, int[] originalLineOffsets) { - int begin = 0; - int end = originalLineOffsets.length - 1; - while (begin < end) { - int mid = (int) Math.round((begin + end) / 2D); - if (declarationStartOffset < originalLineOffsets[mid]) { - end = mid - 1; - } else { - begin = mid; + for (int i = 0; i < refsPerLine.length; i++) { + StringBuilder sb = refsPerLine[i]; + if (sb != null) { + to.getLinesBuilder(i).setSymbols(sb.toString()); } } - return begin + 1; } - private <G> void writeDataPerLine(int[] originalLineOffsets, G item, int globalStartOffset, int globalEndOffset, StringBuilder[] dataPerLine, int startLine, - RangeItemWriter<G> writer) { - int currentLineIdx = startLine; - // We know current item starts on current line - long ruleStartOffsetCurrentLine = globalStartOffset; - while (currentLineIdx < originalLineOffsets.length && globalEndOffset >= originalLineOffsets[currentLineIdx]) { + private void addSymbol(int symbolId, Range range, int[] originalLineOffsets, StringBuilder[] result) { + writeDataPerLine(originalLineOffsets, symbolId, range, result, new SymbolItemWriter()); + } + + private <G> void writeDataPerLine(int[] originalLineOffsets, G item, Range range, StringBuilder[] dataPerLine, RangeItemWriter<G> writer) { + int currentLineIdx = range.getStartLine(); + long ruleStartOffsetCurrentLine = range.getStartOffset(); + while (currentLineIdx < dataPerLine.length && range.getEndLine() > currentLineIdx) { // item continue on next line so write current line and continue on next line with same item - writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], originalLineOffsets[currentLineIdx] - - originalLineOffsets[currentLineIdx - 1], writer); + writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine, lineLength(originalLineOffsets, currentLineIdx), writer); currentLineIdx++; - ruleStartOffsetCurrentLine = originalLineOffsets[currentLineIdx - 1]; + ruleStartOffsetCurrentLine = 0; } // item ends on current line - writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], globalEndOffset - - originalLineOffsets[currentLineIdx - 1], writer); + writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine, range.getEndOffset(), writer); + } + + private int lineLength(int[] originalLineOffsets, int currentLineIdx) { + return originalLineOffsets[currentLineIdx] + - originalLineOffsets[currentLineIdx - 1]; } private <G> void writeItem(G item, StringBuilder[] dataPerLine, int currentLineIdx, long startLineOffset, long endLineOffset, RangeItemWriter<G> writer) { @@ -321,17 +294,17 @@ public class SourceDataFactory implements BatchComponent { void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, G item); } - private static class RuleItemWriter implements RangeItemWriter<SyntaxHighlightingRule> { + private static class RuleItemWriter implements RangeItemWriter<HighlightingRule> { @Override - public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, SyntaxHighlightingRule item) { + public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, HighlightingRule item) { if (currentLineSb.length() > 0) { - currentLineSb.append(SyntaxHighlightingData.RULE_SEPARATOR); + currentLineSb.append(';'); } currentLineSb.append(startLineOffset) - .append(SyntaxHighlightingData.FIELD_SEPARATOR) + .append(',') .append(endLineOffset) - .append(SyntaxHighlightingData.FIELD_SEPARATOR) - .append(item.getTextType().cssClass()); + .append(',') + .append(BatchReportUtils.toCssClass(item.getType())); } } @@ -340,12 +313,12 @@ public class SourceDataFactory implements BatchComponent { @Override public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, Integer symbolId) { if (currentLineSb.length() > 0) { - currentLineSb.append(SymbolData.SYMBOL_SEPARATOR); + currentLineSb.append(";"); } currentLineSb.append(startLineOffset) - .append(SymbolData.FIELD_SEPARATOR) + .append(",") .append(endLineOffset) - .append(SymbolData.FIELD_SEPARATOR) + .append(",") .append(symbolId); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java index bf537550a8c..a55562c88f2 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java @@ -19,6 +19,9 @@ */ package org.sonar.batch.index; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer; + import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.apache.ibatis.session.ResultContext; @@ -27,9 +30,6 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.utils.System2; import org.sonar.batch.ProjectTree; -import org.sonar.batch.scan.filesystem.FileMetadata; -import org.sonar.batch.scan.filesystem.FileMetadata.LineHashConsumer; -import org.sonar.batch.scan.filesystem.InputFileMetadata; import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; @@ -89,8 +89,7 @@ public class SourcePersister implements ScanPersister { private void persist(DbSession session, FileSourceMapper mapper, DefaultInputFile inputFile, Map<String, FileSourceDto> previousDtosByUuid) { String fileUuid = resourceCache.get(inputFile).resource().getUuid(); - InputFileMetadata metadata = inputPathCache.getFileMetadata(inputFile); - byte[] data = computeData(inputFile, metadata); + byte[] data = computeData(inputFile); String dataHash = DigestUtils.md5Hex(data); FileSourceDto previousDto = previousDtosByUuid.get(fileUuid); if (previousDto == null) { @@ -99,7 +98,7 @@ public class SourcePersister implements ScanPersister { .setFileUuid(fileUuid) .setBinaryData(data) .setDataHash(dataHash) - .setSrcHash(metadata.hash()) + .setSrcHash(inputFile.hash()) .setLineHashes(lineHashesAsMd5Hex(inputFile)) .setCreatedAt(system2.now()) .setUpdatedAt(0L); @@ -108,12 +107,12 @@ public class SourcePersister implements ScanPersister { } else { // Update only if data_hash has changed or if src_hash is missing (progressive migration) boolean binaryDataUpdated = !dataHash.equals(previousDto.getDataHash()); - boolean srcHashUpdated = !metadata.hash().equals(previousDto.getSrcHash()); + boolean srcHashUpdated = !inputFile.hash().equals(previousDto.getSrcHash()); if (binaryDataUpdated || srcHashUpdated) { previousDto .setBinaryData(data) .setDataHash(dataHash) - .setSrcHash(metadata.hash()) + .setSrcHash(inputFile.hash()) .setLineHashes(lineHashesAsMd5Hex(inputFile)); // Optimization only change updated at when updating binary data to avoid unecessary indexation by E/S if (binaryDataUpdated) { @@ -147,9 +146,9 @@ public class SourcePersister implements ScanPersister { return result.toString(); } - private byte[] computeData(DefaultInputFile inputFile, InputFileMetadata metadata) { + private byte[] computeData(DefaultInputFile inputFile) { try { - return dataFactory.consolidateData(inputFile, metadata); + return dataFactory.consolidateData(inputFile); } catch (IOException e) { throw new IllegalStateException("Fail to read file " + inputFile, e); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java index 7491bbb7814..d287364b046 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java @@ -47,7 +47,7 @@ public class IssuableFactory extends PerspectiveBuilder<Issuable> { @CheckForNull @Override - protected Issuable loadPerspective(Class<Issuable> perspectiveClass, Component component) { + public Issuable loadPerspective(Class<Issuable> perspectiveClass, Component component) { boolean supported = true; if (component instanceof ResourceComponent) { supported = Scopes.isHigherThanOrEquals(((ResourceComponent) component).scope(), Scopes.FILE); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/FileHashes.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/FileHashes.java index 996351c26f6..9f50e3abbfb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/FileHashes.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/FileHashes.java @@ -19,13 +19,14 @@ */ package org.sonar.batch.issue.tracking; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer; + import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang.ObjectUtils; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.batch.scan.filesystem.FileMetadata; -import org.sonar.batch.scan.filesystem.FileMetadata.LineHashConsumer; import javax.annotation.Nullable; diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java index 6200c172993..e274375076b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -36,7 +36,7 @@ import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonar.batch.issue.tracking.ServerLineHashesLoader; import org.sonar.batch.protocol.input.*; import org.sonar.batch.protocol.input.BatchInput.ServerIssue; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.repository.GlobalRepositoriesLoader; import org.sonar.batch.repository.ProjectRepositoriesLoader; import org.sonar.batch.repository.ServerIssuesLoader; @@ -62,7 +62,7 @@ public class BatchMediumTester { public static BatchMediumTesterBuilder builder() { BatchMediumTesterBuilder builder = new BatchMediumTesterBuilder().registerCoreMetrics(); builder.bootstrapProperties.put(MEDIUM_TEST_ENABLED, "true"); - builder.bootstrapProperties.put(PublishReportJob.KEEP_REPORT_PROP_KEY, "true"); + builder.bootstrapProperties.put(ReportPublisher.KEEP_REPORT_PROP_KEY, "true"); builder.bootstrapProperties.put(CoreProperties.WORKING_DIRECTORY, Files.createTempDir().getAbsolutePath()); return builder; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java index 3dc913b005b..423f430c14f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java @@ -23,31 +23,37 @@ import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextPointer; +import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency; import org.sonar.api.batch.sensor.duplication.Duplication; import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.measures.Measure; -import org.sonar.api.source.Symbol; import org.sonar.batch.dependency.DependencyCache; import org.sonar.batch.duplication.DuplicationCache; -import org.sonar.batch.highlighting.SyntaxHighlightingData; import org.sonar.batch.index.Cache.Entry; -import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.protocol.output.BatchReport.Component; +import org.sonar.batch.protocol.output.BatchReport.Metadata; +import org.sonar.batch.protocol.output.BatchReport.Range; +import org.sonar.batch.protocol.output.BatchReport.Symbols.Symbol; +import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule; +import org.sonar.batch.protocol.output.*; +import org.sonar.batch.report.BatchReportUtils; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.scan.ProjectScanContainer; import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.batch.scan.measure.MeasureCache; -import org.sonar.batch.symbol.SymbolData; -import org.sonar.core.source.SnapshotDataTypes; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import java.io.Serializable; import java.util.*; @@ -60,10 +66,10 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { private List<org.sonar.api.batch.sensor.measure.Measure> measures = new ArrayList<>(); private Map<String, List<Duplication>> duplications = new HashMap<>(); private Map<String, InputFile> inputFiles = new HashMap<>(); + private Map<String, Component> reportComponents = new HashMap<>(); private Map<String, InputDir> inputDirs = new HashMap<>(); - private Map<InputFile, SyntaxHighlightingData> highlightingPerFile = new HashMap<>(); - private Map<InputFile, SymbolData> symbolTablePerFile = new HashMap<>(); private Map<String, Map<String, Integer>> dependencies = new HashMap<>(); + private BatchReportReader reader; @Override public void scanTaskCompleted(ProjectScanContainer container) { @@ -72,16 +78,37 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { issues.add(issue); } + if (!container.getComponentByType(AnalysisMode.class).isPreview()) { + ReportPublisher reportPublisher = container.getComponentByType(ReportPublisher.class); + reader = new BatchReportReader(reportPublisher.getReportDir()); + Metadata readMetadata = getReportReader().readMetadata(); + int rootComponentRef = readMetadata.getRootComponentRef(); + storeReportComponents(rootComponentRef, null, readMetadata.hasBranch() ? readMetadata.getBranch() : null); + } + storeFs(container); storeMeasures(container); - storeComponentData(container); storeDuplication(container); - // storeTestCases(container); - // storeCoveragePerTest(container); storeDependencies(container); + } + + private void storeReportComponents(int componentRef, String parentModuleKey, @Nullable String branch) { + Component component = getReportReader().readComponent(componentRef); + if (component.hasKey()) { + reportComponents.put(component.getKey() + (branch != null ? ":" + branch : ""), component); + } else { + reportComponents.put(parentModuleKey + (branch != null ? ":" + branch : "") + ":" + component.getPath(), component); + } + for (int childId : component.getChildRefList()) { + storeReportComponents(childId, component.hasKey() ? component.getKey() : parentModuleKey, branch); + } + + } + public BatchReportReader getReportReader() { + return reader; } private void storeMeasures(ProjectScanContainer container) { @@ -108,20 +135,6 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { } } - private void storeComponentData(ProjectScanContainer container) { - ComponentDataCache componentDataCache = container.getComponentByType(ComponentDataCache.class); - for (InputFile file : inputFiles.values()) { - SyntaxHighlightingData highlighting = componentDataCache.getData(((DefaultInputFile) file).key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING); - if (highlighting != null) { - highlightingPerFile.put(file, highlighting); - } - SymbolData symbolTable = componentDataCache.getData(((DefaultInputFile) file).key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING); - if (symbolTable != null) { - symbolTablePerFile.put(file, symbolTable); - } - } - } - private void storeFs(ProjectScanContainer container) { InputPathCache inputFileCache = container.getComponentByType(InputPathCache.class); for (InputFile inputPath : inputFileCache.allFiles()) { @@ -178,37 +191,45 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver { * Get highlighting types at a given position in an inputfile * @param charIndex 0-based offset in file */ - public List<TypeOfText> highlightingTypeFor(InputFile file, int charIndex) { - SyntaxHighlightingData syntaxHighlightingData = highlightingPerFile.get(file); - if (syntaxHighlightingData == null) { + public List<TypeOfText> highlightingTypeFor(InputFile file, int line, int lineOffset) { + int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef(); + List<HighlightingRule> syntaxHighlightingRules = getReportReader().readComponentSyntaxHighlighting(ref); + if (syntaxHighlightingRules.isEmpty()) { return Collections.emptyList(); } + TextPointer pointer = file.newPointer(line, lineOffset); List<TypeOfText> result = new ArrayList<TypeOfText>(); - for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.syntaxHighlightingRuleSet()) { - if (sortedRule.getStartPosition() <= charIndex && sortedRule.getEndPosition() > charIndex) { - result.add(sortedRule.getTextType()); + for (HighlightingRule sortedRule : syntaxHighlightingRules) { + TextRange ruleRange = toRange(file, sortedRule.getRange()); + if (ruleRange.start().compareTo(pointer) <= 0 && ruleRange.end().compareTo(pointer) > 0) { + result.add(BatchReportUtils.toBatchType(sortedRule.getType())); } } return result; } + private static TextRange toRange(InputFile file, Range reportRange) { + return file.newRange(file.newPointer(reportRange.getStartLine(), reportRange.getStartOffset()), file.newPointer(reportRange.getEndLine(), reportRange.getEndOffset())); + } + /** - * Get list of all positions of a symbol in an inputfile + * Get list of all start positions of a symbol in an inputfile * @param symbolStartOffset 0-based start offset for the symbol in file * @param symbolEndOffset 0-based end offset for the symbol in file */ @CheckForNull - public Set<Integer> symbolReferencesFor(InputFile file, int symbolStartOffset, int symbolEndOffset) { - SymbolData data = symbolTablePerFile.get(file); - if (data == null) { - return null; + public List<Range> symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) { + int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef(); + List<Symbol> symbols = getReportReader().readComponentSymbols(ref); + if (symbols.isEmpty()) { + return Collections.emptyList(); } - for (Symbol symbol : data.referencesBySymbol().keySet()) { - if (symbol.getDeclarationStartOffset() == symbolStartOffset && symbol.getDeclarationEndOffset() == symbolEndOffset) { - return data.referencesBySymbol().get(symbol); + for (Symbol symbol : symbols) { + if (symbol.getDeclaration().getStartLine() == symbolStartLine && symbol.getDeclaration().getStartOffset() == symbolStartLineOffset) { + return symbol.getReferenceList(); } } - return null; + return Collections.emptyList(); } /** diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseLessPhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseLessPhaseExecutor.java index f53ce1b715e..644cf8bf724 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseLessPhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseLessPhaseExecutor.java @@ -26,7 +26,7 @@ import org.sonar.batch.events.EventBus; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader; import org.sonar.batch.issue.tracking.LocalIssueTracking; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.rule.QProfileVerifier; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.filesystem.FileSystemLogger; @@ -47,12 +47,12 @@ public final class DatabaseLessPhaseExecutor implements PhaseExecutor { private final IssueExclusionsLoader issueExclusionsLoader; private final IssuesReports issuesReport; private final LocalIssueTracking localIssueTracking; - private final PublishReportJob publishReportJob; + private final ReportPublisher publishReportJob; public DatabaseLessPhaseExecutor(Phases phases, InitializersExecutor initializersExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, DefaultIndex index, EventBus eventBus, ProjectInitializer pi, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, - IssueExclusionsLoader issueExclusionsLoader, LocalIssueTracking localIssueTracking, PublishReportJob publishReportJob) { + IssueExclusionsLoader issueExclusionsLoader, LocalIssueTracking localIssueTracking, ReportPublisher publishReportJob) { this.phases = phases; this.initializersExecutor = initializersExecutor; this.sensorsExecutor = sensorsExecutor; diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseModePhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseModePhaseExecutor.java index 13ec298b8d4..657a58083f2 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseModePhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/DatabaseModePhaseExecutor.java @@ -31,7 +31,7 @@ import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.index.ResourcePersister; import org.sonar.batch.index.ScanPersister; import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader; -import org.sonar.batch.report.PublishReportJob; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.rule.QProfileVerifier; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.filesystem.FileSystemLogger; @@ -49,7 +49,7 @@ public final class DatabaseModePhaseExecutor implements PhaseExecutor { private final PostJobsExecutor postJobsExecutor; private final InitializersExecutor initializersExecutor; private final SensorsExecutor sensorsExecutor; - private final PublishReportJob publishReportJob; + private final ReportPublisher publishReportJob; private final SensorContext sensorContext; private final DefaultIndex index; private final ProjectInitializer pi; @@ -66,7 +66,7 @@ public final class DatabaseModePhaseExecutor implements PhaseExecutor { public DatabaseModePhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor, InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, DefaultIndex index, - EventBus eventBus, PublishReportJob publishReportJob, ProjectInitializer pi, + EventBus eventBus, ReportPublisher publishReportJob, ProjectInitializer pi, ScanPersister[] persisters, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, DefaultAnalysisMode analysisMode, DatabaseSession session, ResourcePersister resourcePersister) { this.phases = phases; diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/BatchReportUtils.java b/sonar-batch/src/main/java/org/sonar/batch/report/BatchReportUtils.java new file mode 100644 index 00000000000..4e20cd508c2 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/BatchReportUtils.java @@ -0,0 +1,83 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.report; + +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.batch.protocol.Constants.HighlightingType; + +public class BatchReportUtils { + + private BatchReportUtils() { + } + + public static HighlightingType toProtocolType(TypeOfText textType) { + switch (textType) { + case ANNOTATION: + return HighlightingType.ANNOTATION; + case COMMENT: + return HighlightingType.COMMENT; + case CONSTANT: + return HighlightingType.CONSTANT; + case CPP_DOC: + return HighlightingType.CPP_DOC; + case KEYWORD: + return HighlightingType.KEYWORD; + case KEYWORD_LIGHT: + return HighlightingType.KEYWORD_LIGHT; + case PREPROCESS_DIRECTIVE: + return HighlightingType.PREPROCESS_DIRECTIVE; + case STRING: + return HighlightingType.HIGHLIGHTING_STRING; + case STRUCTURED_COMMENT: + return HighlightingType.STRUCTURED_COMMENT; + default: + throw new IllegalArgumentException("Unknow highlighting type: " + textType); + } + } + + public static TypeOfText toBatchType(HighlightingType type) { + switch (type) { + case ANNOTATION: + return TypeOfText.ANNOTATION; + case COMMENT: + return TypeOfText.COMMENT; + case CONSTANT: + return TypeOfText.CONSTANT; + case CPP_DOC: + return TypeOfText.CPP_DOC; + case HIGHLIGHTING_STRING: + return TypeOfText.STRING; + case KEYWORD: + return TypeOfText.KEYWORD; + case KEYWORD_LIGHT: + return TypeOfText.KEYWORD_LIGHT; + case PREPROCESS_DIRECTIVE: + return TypeOfText.PREPROCESS_DIRECTIVE; + case STRUCTURED_COMMENT: + return TypeOfText.STRUCTURED_COMMENT; + default: + throw new IllegalArgumentException(type + " is not a valid type"); + } + } + + public static String toCssClass(HighlightingType type) { + return toBatchType(type).cssClass(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java index 28dabefe4ad..7bed3e9db12 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java @@ -40,7 +40,7 @@ import javax.annotation.CheckForNull; /** * Adds components and analysis metadata to output report */ -public class ComponentsPublisher implements ReportPublisher { +public class ComponentsPublisher implements ReportPublisherStep { private final ResourceCache resourceCache; private final ProjectReactor reactor; diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java index 896ef5aba08..0295e13bb2b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java @@ -31,7 +31,7 @@ import org.sonar.batch.protocol.output.BatchReport.Duplicate; import org.sonar.batch.protocol.output.BatchReport.Duplication; import org.sonar.batch.protocol.output.BatchReport.Range; -public class DuplicationsPublisher implements ReportPublisher { +public class DuplicationsPublisher implements ReportPublisherStep { private final ResourceCache resourceCache; private final DuplicationCache duplicationCache; diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java index 640552eccfe..f2268cd20a9 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java @@ -39,7 +39,7 @@ import java.util.Collection; import java.util.Date; import java.util.Iterator; -public class IssuesPublisher implements ReportPublisher { +public class IssuesPublisher implements ReportPublisherStep { private final ResourceCache resourceCache; private final IssueCache issueCache; diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java index 8aadc2180ee..0613432376b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java @@ -43,7 +43,7 @@ import javax.annotation.Nullable; import java.io.Serializable; -public class MeasuresPublisher implements ReportPublisher { +public class MeasuresPublisher implements ReportPublisherStep { private final ResourceCache resourceCache; private final MeasureCache measureCache; diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java b/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java deleted file mode 100644 index 9fe5d4bbd8b..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.report; - -import com.github.kevinsawicki.http.HttpRequest; -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.io.FileUtils; -import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable; -import org.codehaus.plexus.personality.plexus.lifecycle.phase.StartingException; -import org.codehaus.plexus.personality.plexus.lifecycle.phase.StoppingException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.BatchComponent; -import org.sonar.api.CoreProperties; -import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.api.config.Settings; -import org.sonar.api.platform.Server; -import org.sonar.api.utils.TempFolder; -import org.sonar.api.utils.ZipUtils; -import org.sonar.batch.bootstrap.DefaultAnalysisMode; -import org.sonar.batch.bootstrap.ServerClient; -import org.sonar.batch.protocol.output.BatchReportWriter; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; - -public class PublishReportJob implements BatchComponent, Startable { - - private static final Logger LOG = LoggerFactory.getLogger(PublishReportJob.class); - public static final String KEEP_REPORT_PROP_KEY = "sonar.batch.keepReport"; - - private final ServerClient serverClient; - private final Server server; - private final Settings settings; - private final ProjectReactor projectReactor; - private final DefaultAnalysisMode analysisMode; - private final TempFolder temp; - - private ReportPublisher[] publishers; - - private File reportDir; - private BatchReportWriter writer; - - public PublishReportJob(Settings settings, ServerClient serverClient, Server server, - ProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisher[] publishers) { - this.serverClient = serverClient; - this.server = server; - this.projectReactor = projectReactor; - this.settings = settings; - this.analysisMode = analysisMode; - this.temp = temp; - this.publishers = publishers; - } - - @Override - public void start() throws StartingException { - reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report"); - writer = new BatchReportWriter(reportDir); - } - - @Override - public void stop() throws StoppingException { - if (!settings.getBoolean(KEEP_REPORT_PROP_KEY)) { - FileUtils.deleteQuietly(reportDir); - } else { - LOG.info("Batch report generated in " + reportDir); - } - } - - public File getReportDir() { - return reportDir; - } - - public BatchReportWriter getWriter() { - return writer; - } - - public void execute() { - // If this is a preview analysis then we should not upload reports - if (!analysisMode.isPreview()) { - File report = prepareReport(); - if (!analysisMode.isMediumTest()) { - uploadMultiPartReport(report); - } - } - logSuccess(LoggerFactory.getLogger(getClass())); - } - - private File prepareReport() { - try { - long startTime = System.currentTimeMillis(); - for (ReportPublisher publisher : publishers) { - publisher.publish(writer); - } - long stopTime = System.currentTimeMillis(); - LOG.info("Analysis reports generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir))); - - startTime = System.currentTimeMillis(); - File reportZip = temp.newFile("batch-report", ".zip"); - ZipUtils.zipDir(reportDir, reportZip); - stopTime = System.currentTimeMillis(); - LOG.info("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip))); - return reportZip; - } catch (IOException e) { - throw new IllegalStateException("Unable to prepare batch report", e); - } - } - - @VisibleForTesting - void uploadMultiPartReport(File report) { - LOG.debug("Publish results"); - long startTime = System.currentTimeMillis(); - URL url; - try { - String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); - url = new URL(serverClient.getURL() + "/api/computation/submit_report?projectKey=" + effectiveKey); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Invalid URL", e); - } - HttpRequest request = HttpRequest.post(url); - request.trustAllCerts(); - request.trustAllHosts(); - request.header("User-Agent", String.format("SonarQube %s", server.getVersion())); - request.basic(serverClient.getLogin(), serverClient.getPassword()); - request.part("report", null, "application/octet-stream", report); - if (!request.ok()) { - int responseCode = request.code(); - if (responseCode == 401) { - throw new IllegalStateException(String.format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD)); - } - if (responseCode == 403) { - // SONAR-4397 Details are in response content - throw new IllegalStateException(request.body()); - } - throw new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body())); - } - long stopTime = System.currentTimeMillis(); - LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms"); - } - - @VisibleForTesting - void logSuccess(Logger logger) { - if (analysisMode.isPreview() || analysisMode.isMediumTest()) { - logger.info("ANALYSIS SUCCESSFUL"); - - } else { - String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL); - if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) { - // If server base URL was not configured in Sonar server then is is better to take URL configured on batch side - baseUrl = serverClient.getURL(); - } - if (!baseUrl.endsWith("/")) { - baseUrl += "/"; - } - String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); - String url = baseUrl + "dashboard/index/" + effectiveKey; - logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url); - logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report."); - } - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java index 07aa03fccbe..2043fcd7f01 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java @@ -19,13 +19,162 @@ */ package org.sonar.batch.report; +import com.github.kevinsawicki.http.HttpRequest; +import com.google.common.annotations.VisibleForTesting; +import org.apache.commons.io.FileUtils; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.StartingException; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.StoppingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchComponent; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.config.Settings; +import org.sonar.api.platform.Server; +import org.sonar.api.utils.TempFolder; +import org.sonar.api.utils.ZipUtils; +import org.sonar.batch.bootstrap.DefaultAnalysisMode; +import org.sonar.batch.bootstrap.ServerClient; import org.sonar.batch.protocol.output.BatchReportWriter; -/** - * Adds a sub-part of data to output report - */ -public interface ReportPublisher { +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +public class ReportPublisher implements BatchComponent, Startable { + + private static final Logger LOG = LoggerFactory.getLogger(ReportPublisher.class); + public static final String KEEP_REPORT_PROP_KEY = "sonar.batch.keepReport"; + + private final ServerClient serverClient; + private final Server server; + private final Settings settings; + private final ProjectReactor projectReactor; + private final DefaultAnalysisMode analysisMode; + private final TempFolder temp; + + private ReportPublisherStep[] publishers; + + private File reportDir; + private BatchReportWriter writer; + + public ReportPublisher(Settings settings, ServerClient serverClient, Server server, + ProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { + this.serverClient = serverClient; + this.server = server; + this.projectReactor = projectReactor; + this.settings = settings; + this.analysisMode = analysisMode; + this.temp = temp; + this.publishers = publishers; + } + + @Override + public void start() throws StartingException { + reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report"); + writer = new BatchReportWriter(reportDir); + } + + @Override + public void stop() throws StoppingException { + if (!settings.getBoolean(KEEP_REPORT_PROP_KEY)) { + FileUtils.deleteQuietly(reportDir); + } else { + LOG.info("Batch report generated in " + reportDir); + } + } + + public File getReportDir() { + return reportDir; + } + + public BatchReportWriter getWriter() { + return writer; + } + + public void execute() { + // If this is a preview analysis then we should not upload reports + if (!analysisMode.isPreview()) { + File report = prepareReport(); + if (!analysisMode.isMediumTest()) { + uploadMultiPartReport(report); + } + } + logSuccess(LoggerFactory.getLogger(getClass())); + } + + private File prepareReport() { + try { + long startTime = System.currentTimeMillis(); + for (ReportPublisherStep publisher : publishers) { + publisher.publish(writer); + } + long stopTime = System.currentTimeMillis(); + LOG.info("Analysis reports generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir))); + + startTime = System.currentTimeMillis(); + File reportZip = temp.newFile("batch-report", ".zip"); + ZipUtils.zipDir(reportDir, reportZip); + stopTime = System.currentTimeMillis(); + LOG.info("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip))); + return reportZip; + } catch (IOException e) { + throw new IllegalStateException("Unable to prepare batch report", e); + } + } + + @VisibleForTesting + void uploadMultiPartReport(File report) { + LOG.debug("Publish results"); + long startTime = System.currentTimeMillis(); + URL url; + try { + String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); + url = new URL(serverClient.getURL() + "/api/computation/submit_report?projectKey=" + effectiveKey); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid URL", e); + } + HttpRequest request = HttpRequest.post(url); + request.trustAllCerts(); + request.trustAllHosts(); + request.header("User-Agent", String.format("SonarQube %s", server.getVersion())); + request.basic(serverClient.getLogin(), serverClient.getPassword()); + request.part("report", null, "application/octet-stream", report); + if (!request.ok()) { + int responseCode = request.code(); + if (responseCode == 401) { + throw new IllegalStateException(String.format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD)); + } + if (responseCode == 403) { + // SONAR-4397 Details are in response content + throw new IllegalStateException(request.body()); + } + throw new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body())); + } + long stopTime = System.currentTimeMillis(); + LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms"); + } - void publish(BatchReportWriter writer); + @VisibleForTesting + void logSuccess(Logger logger) { + if (analysisMode.isPreview() || analysisMode.isMediumTest()) { + logger.info("ANALYSIS SUCCESSFUL"); + } else { + String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL); + if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) { + // If server base URL was not configured in Sonar server then is is better to take URL configured on batch side + baseUrl = serverClient.getURL(); + } + if (!baseUrl.endsWith("/")) { + baseUrl += "/"; + } + String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); + String url = baseUrl + "dashboard/index/" + effectiveKey; + logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url); + logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report."); + } + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisherStep.java index 93b92f3e9ae..eb771b3d009 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/package-info.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisherStep.java @@ -17,7 +17,15 @@ * 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.batch.highlighting; +package org.sonar.batch.report; -import javax.annotation.ParametersAreNonnullByDefault; +import org.sonar.batch.protocol.output.BatchReportWriter; + +/** + * Adds a sub-part of data to output report + */ +public interface ReportPublisherStep { + + void publish(BatchReportWriter writer); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 5df7f4c665d..6e724866c67 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.rule.CheckFactory; import org.sonar.api.checks.NoSonarFilter; import org.sonar.api.platform.ComponentContainer; @@ -37,6 +38,7 @@ import org.sonar.batch.deprecated.DeprecatedSensorContext; import org.sonar.batch.deprecated.ResourceFilters; import org.sonar.batch.deprecated.components.DefaultProjectClasspath; import org.sonar.batch.deprecated.components.DefaultTimeMachine; +import org.sonar.batch.deprecated.perspectives.BatchPerspectives; import org.sonar.batch.events.EventBus; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.issue.IssuableFactory; @@ -63,7 +65,8 @@ import org.sonar.batch.sensor.AnalyzerOptimizer; import org.sonar.batch.sensor.DefaultSensorContext; import org.sonar.batch.sensor.DefaultSensorStorage; import org.sonar.batch.sensor.coverage.CoverageExclusions; -import org.sonar.core.component.ScanPerspectives; +import org.sonar.batch.source.HighlightableBuilder; +import org.sonar.batch.source.SymbolizableBuilder; public class ModuleScanContainer extends ComponentContainer { private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class); @@ -111,7 +114,7 @@ public class ModuleScanContainer extends ComponentContainer { SensorsExecutor.class, InitializersExecutor.class, ProjectInitializer.class, - PublishReportJob.class, + ReportPublisher.class, ComponentsPublisher.class, IssuesPublisher.class, MeasuresPublisher.class, @@ -172,7 +175,10 @@ public class ModuleScanContainer extends ComponentContainer { IgnoreIssuesFilter.class, NoSonarFilter.class, - ScanPerspectives.class); + // Perspectives + BatchPerspectives.class, + HighlightableBuilder.class, + SymbolizableBuilder.class); } private void addDataBaseComponents() { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index aaa63f0d328..30bfafa9793 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -59,8 +59,6 @@ import org.sonar.batch.rule.RulesProvider; import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.batch.source.CodeColorizers; -import org.sonar.batch.source.HighlightableBuilder; -import org.sonar.batch.source.SymbolizableBuilder; import org.sonar.core.component.ScanGraph; import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.workflow.FunctionExecutor; @@ -131,7 +129,6 @@ public class ProjectScanContainer extends ComponentContainer { DefaultFileLinesContextFactory.class, Caches.class, ResourceCache.class, - ComponentDataCache.class, SourceDataFactory.class, // file system @@ -162,8 +159,6 @@ public class ProjectScanContainer extends ComponentContainer { // lang Languages.class, DefaultLanguagesRepository.class, - HighlightableBuilder.class, - SymbolizableBuilder.class, // Differential periods PeriodsDefinition.class, @@ -187,7 +182,7 @@ public class ProjectScanContainer extends ComponentContainer { ProjectSettings.class, // Report - PublishReportJob.class, + ReportPublisher.class, ComponentsPublisher.class, IssuesPublisher.class, MeasuresPublisher.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java index 460a08831b3..a30037a1c00 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java @@ -26,6 +26,7 @@ import org.sonar.api.resources.File; import org.sonar.api.resources.Languages; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.batch.index.ResourceCache; import org.sonar.batch.index.ResourceKeyMigration; import org.sonar.batch.index.ResourcePersister; @@ -43,18 +44,20 @@ public class ComponentIndexer implements BatchComponent { private final ResourceKeyMigration migration; private final Project module; private final ResourcePersister resourcePersister; + private final ResourceCache resourceCache; - public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex, @Nullable ResourceKeyMigration migration, + public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex, ResourceCache resourceCache, @Nullable ResourceKeyMigration migration, @Nullable ResourcePersister resourcePersister) { this.module = module; this.languages = languages; this.sonarIndex = sonarIndex; + this.resourceCache = resourceCache; this.migration = migration; this.resourcePersister = resourcePersister; } - public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex) { - this(module, languages, sonarIndex, null, null); + public ComponentIndexer(Project module, Languages languages, SonarIndex sonarIndex, ResourceCache resourceCache) { + this(module, languages, sonarIndex, resourceCache, null, null); } public void execute(DefaultModuleFileSystem fs) { @@ -74,6 +77,7 @@ public class ComponentIndexer implements BatchComponent { boolean unitTest = InputFile.Type.TEST == inputFile.type(); Resource sonarFile = File.create(inputFile.relativePath(), languages.get(languageKey), unitTest); sonarIndex.index(sonarFile); + resourceCache.get(sonarFile).setInputPath(inputFile); } if (resourcePersister != null) { @@ -81,5 +85,4 @@ public class ComponentIndexer implements BatchComponent { resourcePersister.persist(); } } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java index c5eb6bef9b4..fbc40556475 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java @@ -37,17 +37,8 @@ import org.sonar.batch.util.ProgressReport; import java.io.File; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.*; +import java.util.concurrent.*; /** * Index input files into {@link InputPathCache}. @@ -63,15 +54,13 @@ public class FileIndexer implements BatchComponent { private final boolean isAggregator; private final ExclusionFilters exclusionFilters; private final InputFileBuilderFactory inputFileBuilderFactory; - private final InputPathCache inputPathCache; private ProgressReport progressReport; private ExecutorService executorService; private List<Future<Void>> tasks; public FileIndexer(List<InputFileFilter> filters, ExclusionFilters exclusionFilters, InputFileBuilderFactory inputFileBuilderFactory, - ProjectDefinition def, InputPathCache inputPathCache) { - this.inputPathCache = inputPathCache; + ProjectDefinition def) { this.filters = filters; this.exclusionFilters = exclusionFilters; this.inputFileBuilderFactory = inputFileBuilderFactory; @@ -154,12 +143,11 @@ public class FileIndexer implements BatchComponent { tasks.add(executorService.submit(new Callable<Void>() { @Override public Void call() { - InputFileMetadata metadata = inputFileBuilder.completeAndComputeMetadata(inputFile, type); - if (metadata != null && accept(inputFile)) { - fs.add(inputFile); - status.markAsIndexed(inputFile); - inputPathCache.put(inputFile.moduleKey(), inputFile.relativePath(), metadata); - File parentDir = inputFile.file().getParentFile(); + DeprecatedDefaultInputFile completedInputFile = inputFileBuilder.completeAndComputeMetadata(inputFile, type); + if (completedInputFile != null && accept(completedInputFile)) { + fs.add(completedInputFile); + status.markAsIndexed(completedInputFile); + File parentDir = completedInputFile.file().getParentFile(); String relativePath = new PathResolver().relativePath(fs.baseDir(), parentDir); if (relativePath != null) { DefaultInputDir inputDir = new DefaultInputDir(fs.moduleKey(), relativePath); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java index 2077e91d578..e58d5625296 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilder.java @@ -25,6 +25,7 @@ import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.config.Settings; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.DefaultAnalysisMode; @@ -92,7 +93,7 @@ class InputFileBuilder { * Optimization to not compute InputFile metadata if the file is excluded from analysis. */ @CheckForNull - InputFileMetadata completeAndComputeMetadata(DeprecatedDefaultInputFile inputFile, InputFile.Type type) { + DeprecatedDefaultInputFile completeAndComputeMetadata(DeprecatedDefaultInputFile inputFile, InputFile.Type type) { inputFile.setType(type); inputFile.setModuleBaseDir(fs.baseDir().toPath()); inputFile.setCharset(fs.encoding()); @@ -103,22 +104,13 @@ class InputFileBuilder { } inputFile.setLanguage(lang); - InputFileMetadata result = new InputFileMetadata(); + inputFile.initMetadata(fileMetadata.readMetadata(inputFile.file(), fs.encoding())); - FileMetadata.Metadata metadata = fileMetadata.read(inputFile.file(), fs.encoding()); - inputFile.setLines(metadata.lines); - inputFile.setLastValidOffset(metadata.lastValidOffset); - - result.setNonBlankLines(metadata.nonBlankLines); - result.setHash(metadata.hash); - result.setOriginalLineOffsets(metadata.originalLineOffsets); - result.setEmpty(metadata.empty); - - inputFile.setStatus(statusDetection.status(inputFile.moduleKey(), inputFile.relativePath(), metadata.hash)); + inputFile.setStatus(statusDetection.status(inputFile.moduleKey(), inputFile.relativePath(), inputFile.hash())); if (analysisMode.isIncremental() && inputFile.status() == InputFile.Status.SAME) { return null; } - return result; + return inputFile; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java index c173975fc53..74685bd7955 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactory.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.scan.filesystem; +import org.sonar.api.batch.fs.internal.FileMetadata; + import org.sonar.api.BatchComponent; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.config.Settings; diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileMetadata.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileMetadata.java deleted file mode 100644 index f37c672d101..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileMetadata.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.scan.filesystem; - -import java.io.Serializable; - -/** - * Additional input file metadata that are stored in a disk storage to save memory - */ -public class InputFileMetadata implements Serializable { - - private String hash; - private int nonBlankLines; - private int[] originalLineOffsets; - private boolean empty; - - /** - * Digest hash of the file. - */ - public String hash() { - return hash; - } - - public int nonBlankLines() { - return nonBlankLines; - } - - public int[] originalLineOffsets() { - return originalLineOffsets; - } - - public InputFileMetadata setHash(String hash) { - this.hash = hash; - return this; - } - - public InputFileMetadata setNonBlankLines(int nonBlankLines) { - this.nonBlankLines = nonBlankLines; - return this; - } - - public InputFileMetadata setOriginalLineOffsets(int[] originalLineOffsets) { - this.originalLineOffsets = originalLineOffsets; - return this; - } - - public boolean isEmpty() { - return this.empty; - } - - public InputFileMetadata setEmpty(boolean empty) { - this.empty = empty; - return this; - } - -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java index 22cbe41124f..92423f3a25d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java @@ -25,7 +25,6 @@ import org.sonar.api.BatchComponent; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputPath; -import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.batch.index.BatchResource; import javax.annotation.CheckForNull; @@ -40,7 +39,6 @@ public class InputPathCache implements BatchComponent { private final Map<String, SortedMap<String, InputFile>> inputFileCache = new LinkedHashMap<>(); private final Map<String, SortedMap<String, InputDir>> inputDirCache = new LinkedHashMap<>(); - private final Map<String, Map<String, InputFileMetadata>> inputFileMetadataCache = new LinkedHashMap<>(); public Iterable<InputFile> allFiles() { return Iterables.concat(Iterables.transform(inputFileCache.values(), new Function<Map<String, InputFile>, Collection<InputFile>>() { @@ -77,7 +75,6 @@ public class InputPathCache implements BatchComponent { public InputPathCache removeModule(String moduleKey) { inputFileCache.remove(moduleKey); inputDirCache.remove(moduleKey); - inputFileMetadataCache.remove(moduleKey); return this; } @@ -85,9 +82,6 @@ public class InputPathCache implements BatchComponent { if (inputFileCache.containsKey(moduleKey)) { inputFileCache.get(moduleKey).remove(inputFile.relativePath()); } - if (inputFileMetadataCache.containsKey(moduleKey)) { - inputFileMetadataCache.get(moduleKey).remove(inputFile.relativePath()); - } return this; } @@ -106,14 +100,6 @@ public class InputPathCache implements BatchComponent { return this; } - public synchronized InputPathCache put(String moduleKey, String relativePath, InputFileMetadata metadata) { - if (!inputFileMetadataCache.containsKey(moduleKey)) { - inputFileMetadataCache.put(moduleKey, new HashMap<String, InputFileMetadata>()); - } - inputFileMetadataCache.get(moduleKey).put(relativePath, metadata); - return this; - } - public InputPathCache put(String moduleKey, InputDir inputDir) { if (!inputDirCache.containsKey(moduleKey)) { inputDirCache.put(moduleKey, new TreeMap<String, InputDir>()); @@ -131,18 +117,6 @@ public class InputPathCache implements BatchComponent { } @CheckForNull - public InputFileMetadata getFileMetadata(String moduleKey, String relativePath) { - if (inputFileMetadataCache.containsKey(moduleKey)) { - return inputFileMetadataCache.get(moduleKey).get(relativePath); - } - return null; - } - - public InputFileMetadata getFileMetadata(DefaultInputFile inputFile) { - return getFileMetadata(inputFile.moduleKey(), inputFile.relativePath()); - } - - @CheckForNull public InputDir getDir(String moduleKey, String relativePath) { if (inputDirCache.containsKey(moduleKey)) { return inputDirCache.get(moduleKey).get(relativePath); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java index 8795742ba5b..54fd33a6f40 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java @@ -33,8 +33,7 @@ import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.batch.index.ResourceCache; import org.sonar.batch.protocol.input.FileData; import org.sonar.batch.protocol.input.ProjectRepositories; -import org.sonar.batch.report.PublishReportJob; -import org.sonar.batch.scan.filesystem.InputFileMetadata; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.scan.filesystem.InputPathCache; import java.util.LinkedList; @@ -48,18 +47,16 @@ public final class ScmSensor implements Sensor { private final ScmConfiguration configuration; private final FileSystem fs; private final ProjectRepositories projectReferentials; - private final InputPathCache inputPathCache; private final ResourceCache resourceCache; - private final PublishReportJob publishReportJob; + private final ReportPublisher publishReportJob; public ScmSensor(ProjectDefinition projectDefinition, ScmConfiguration configuration, ProjectRepositories projectReferentials, FileSystem fs, InputPathCache inputPathCache, ResourceCache resourceCache, - PublishReportJob publishReportJob) { + ReportPublisher publishReportJob) { this.projectDefinition = projectDefinition; this.configuration = configuration; this.projectReferentials = projectReferentials; this.fs = fs; - this.inputPathCache = inputPathCache; this.resourceCache = resourceCache; this.publishReportJob = publishReportJob; } @@ -96,7 +93,7 @@ public final class ScmSensor implements Sensor { LOG.warn("Forced reloading of SCM data for all files."); } List<InputFile> filesToBlame = new LinkedList<InputFile>(); - for (InputFile f : inputPathCache.allFiles()) { + for (InputFile f : fs.inputFiles(fs.predicates().all())) { if (configuration.forceReloadAll()) { addIfNotEmpty(filesToBlame, (DefaultInputFile) f); } else { @@ -110,8 +107,7 @@ public final class ScmSensor implements Sensor { } private void addIfNotEmpty(List<InputFile> filesToBlame, DefaultInputFile f) { - InputFileMetadata metadata = inputPathCache.getFileMetadata(f); - if (!metadata.isEmpty()) { + if (!f.isEmpty()) { filesToBlame.add(f); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java index cf2930a72fe..724c900176e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java +++ b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java @@ -19,73 +19,74 @@ */ package org.sonar.batch.sensor; +import com.google.common.base.Function; import com.google.common.base.Preconditions; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.InputDir; +import com.google.common.collect.Iterables; +import org.sonar.api.batch.fs.*; import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.duplication.Duplication; import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication; import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.issue.Issue.Severity; import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.design.Dependency; -import org.sonar.api.issue.Issuable; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.measures.Formula; import org.sonar.api.measures.Metric; import org.sonar.api.measures.PersistenceMode; import org.sonar.api.measures.SumChildDistributionFormula; -import org.sonar.api.resources.Directory; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.Scopes; +import org.sonar.api.resources.*; import org.sonar.api.rule.RuleKey; +import org.sonar.api.source.Symbol; import org.sonar.batch.duplication.DuplicationCache; -import org.sonar.batch.highlighting.SyntaxHighlightingData; import org.sonar.batch.index.BatchResource; -import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.issue.ModuleIssues; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.batch.protocol.output.BatchReport.Range; +import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule; +import org.sonar.batch.protocol.output.BatchReportWriter; +import org.sonar.batch.report.BatchReportUtils; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.sensor.coverage.CoverageExclusions; +import org.sonar.batch.source.DefaultSymbol; import org.sonar.core.component.ComponentKeys; -import org.sonar.core.source.SnapshotDataTypes; + +import java.util.Map; +import java.util.Set; public class DefaultSensorStorage implements SensorStorage { private static final String USES = "USES"; private final MetricFinder metricFinder; private final Project project; - private final ResourcePerspectives perspectives; + private final ModuleIssues moduleIssues; private final DefaultIndex sonarIndex; private final CoverageExclusions coverageExclusions; private final DuplicationCache duplicationCache; private final ResourceCache resourceCache; - private final ComponentDataCache componentDataCache; + private final ReportPublisher reportPublisher; - public DefaultSensorStorage(MetricFinder metricFinder, Project project, - ResourcePerspectives perspectives, - Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache, - DuplicationCache duplicationCache, DefaultIndex sonarIndex, CoverageExclusions coverageExclusions, - ResourceCache resourceCache) { + public DefaultSensorStorage(MetricFinder metricFinder, Project project, ModuleIssues moduleIssues, + Settings settings, FileSystem fs, ActiveRules activeRules, DuplicationCache duplicationCache, DefaultIndex sonarIndex, + CoverageExclusions coverageExclusions, ResourceCache resourceCache, ReportPublisher reportPublisher) { this.metricFinder = metricFinder; this.project = project; - this.perspectives = perspectives; - this.componentDataCache = componentDataCache; + this.moduleIssues = moduleIssues; this.sonarIndex = sonarIndex; this.coverageExclusions = coverageExclusions; this.duplicationCache = duplicationCache; this.resourceCache = resourceCache; + this.reportPublisher = reportPublisher; } private Metric findMetricOrFail(String metricKey) { @@ -161,11 +162,7 @@ public class DefaultSensorStorage implements SensorStorage { } else { r = project; } - Issuable issuable = perspectives.as(Issuable.class, r); - if (issuable == null) { - return; - } - issuable.addIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue)); + moduleIssues.initAndAddIssue(toDefaultIssue(project.getKey(), ComponentKeys.createEffectiveKey(project, r), issue)); } public static DefaultIssue toDefaultIssue(String projectKey, String componentKey, Issue issue) { @@ -246,7 +243,56 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(DefaultHighlighting highlighting) { - String componentKey = ((DefaultInputFile) highlighting.inputFile()).key(); - componentDataCache.setData(componentKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING, new SyntaxHighlightingData(highlighting.getSyntaxHighlightingRuleSet())); + BatchReportWriter writer = reportPublisher.getWriter(); + DefaultInputFile inputFile = (DefaultInputFile) highlighting.inputFile(); + writer.writeComponentSyntaxHighlighting(resourceCache.get(inputFile).batchId(), + Iterables.transform(highlighting.getSyntaxHighlightingRuleSet(), new Function<SyntaxHighlightingRule, HighlightingRule>() { + private HighlightingRule.Builder builder = HighlightingRule.newBuilder(); + private Range.Builder rangeBuilder = Range.newBuilder(); + + @Override + public HighlightingRule apply(SyntaxHighlightingRule input) { + builder.clear(); + rangeBuilder.clear(); + builder.setRange(rangeBuilder.setStartLine(input.range().start().line()) + .setStartOffset(input.range().start().lineOffset()) + .setEndLine(input.range().end().line()) + .setEndOffset(input.range().end().lineOffset()) + .build()); + builder.setType(BatchReportUtils.toProtocolType(input.getTextType())); + return builder.build(); + } + + })); + } + + public void store(DefaultInputFile inputFile, Map<Symbol, Set<TextRange>> referencesBySymbol) { + BatchReportWriter writer = reportPublisher.getWriter(); + writer.writeComponentSymbols(resourceCache.get(inputFile).batchId(), + Iterables.transform(referencesBySymbol.entrySet(), new Function<Map.Entry<Symbol, Set<TextRange>>, BatchReport.Symbols.Symbol>() { + private BatchReport.Symbols.Symbol.Builder builder = BatchReport.Symbols.Symbol.newBuilder(); + private Range.Builder rangeBuilder = Range.newBuilder(); + + @Override + public BatchReport.Symbols.Symbol apply(Map.Entry<Symbol, Set<TextRange>> input) { + builder.clear(); + rangeBuilder.clear(); + DefaultSymbol symbol = (DefaultSymbol) input.getKey(); + builder.setDeclaration(rangeBuilder.setStartLine(symbol.range().start().line()) + .setStartOffset(symbol.range().start().lineOffset()) + .setEndLine(symbol.range().end().line()) + .setEndOffset(symbol.range().end().lineOffset()) + .build()); + for (TextRange reference : input.getValue()) { + builder.addReference(rangeBuilder.setStartLine(reference.start().line()) + .setStartOffset(reference.start().lineOffset()) + .setEndLine(reference.end().line()) + .setEndOffset(reference.end().lineOffset()) + .build()); + } + return builder.build(); + } + + })); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizerSensor.java b/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizerSensor.java new file mode 100644 index 00000000000..ea2314398c5 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizerSensor.java @@ -0,0 +1,73 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.source; + +import org.sonar.api.batch.Phase; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule; +import org.sonar.batch.protocol.output.BatchReportReader; +import org.sonar.batch.report.ReportPublisher; +import org.sonar.colorizer.CodeColorizer; + +import java.util.List; + +/** + * Execute deprecated {@link CodeColorizer} if necessary. + */ +@Phase(name = Phase.Name.POST) +public final class CodeColorizerSensor implements Sensor { + + private final ReportPublisher reportPublisher; + private final ResourceCache resourceCache; + private final CodeColorizers codeColorizers; + + public CodeColorizerSensor(ReportPublisher reportPublisher, ResourceCache resourceCache, CodeColorizers codeColorizers) { + this.reportPublisher = reportPublisher; + this.resourceCache = resourceCache; + this.codeColorizers = codeColorizers; + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor.name("Code Colorizer Sensor") + .disabledInPreview(); + } + + @Override + public void execute(final SensorContext context) { + FileSystem fs = context.fileSystem(); + for (InputFile f : fs.inputFiles(fs.predicates().all())) { + BatchReportReader reader = new BatchReportReader(reportPublisher.getReportDir()); + int batchId = resourceCache.get(f).batchId(); + List<HighlightingRule> highlightingRules = reader.readComponentSyntaxHighlighting(batchId); + String language = f.language(); + if (!highlightingRules.isEmpty() || language == null) { + continue; + } + codeColorizers.toSyntaxHighlighting(f.file(), fs.encoding(), language, context.newHighlighting().onFile(f)); + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizers.java b/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizers.java index 4b99191538a..9f38c69c63f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizers.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizers.java @@ -25,18 +25,14 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; +import org.sonar.api.batch.sensor.highlighting.NewHighlighting; import org.sonar.api.web.CodeColorizerFormat; -import org.sonar.batch.highlighting.SyntaxHighlightingData; import org.sonar.colorizer.CodeColorizer; import org.sonar.colorizer.Tokenizer; import javax.annotation.CheckForNull; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.*; import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; @@ -68,7 +64,7 @@ public class CodeColorizers implements BatchComponent { } @CheckForNull - public SyntaxHighlightingData toSyntaxHighlighting(File file, Charset charset, String language) { + public void toSyntaxHighlighting(File file, Charset charset, String language, NewHighlighting highlighting) { CodeColorizerFormat format = byLang.get(language); List<Tokenizer> tokenizers; if (format == null) { @@ -77,13 +73,13 @@ public class CodeColorizers implements BatchComponent { if ("java".equals(language)) { tokenizers = CodeColorizer.Format.JAVA.getTokenizers(); } else { - return null; + return; } } else { tokenizers = format.getTokenizers(); } try (Reader reader = new BufferedReader(new InputStreamReader(new BOMInputStream(new FileInputStream(file)), charset))) { - return new HighlightingRenderer().render(reader, tokenizers); + new HighlightingRenderer().render(reader, tokenizers, highlighting); } catch (Exception e) { throw new IllegalStateException("Unable to read source file for colorization", e); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java index bef343b872a..741346dd99b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultHighlightable.java @@ -19,64 +19,57 @@ */ package org.sonar.batch.source; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.component.Component; import org.sonar.api.source.Highlightable; -import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder; -import org.sonar.batch.index.ComponentDataCache; -import org.sonar.core.source.SnapshotDataTypes; +import org.sonar.batch.deprecated.InputFileComponent; /** * @since 3.6 */ public class DefaultHighlightable implements Highlightable { - private final Component component; - private final ComponentDataCache cache; - private final SyntaxHighlightingDataBuilder builder; + private final DefaultInputFile inputFile; + private final SensorStorage sensorStorage; - public DefaultHighlightable(Component component, ComponentDataCache cache) { - this.component = component; - this.cache = cache; - this.builder = new SyntaxHighlightingDataBuilder(); + public DefaultHighlightable(DefaultInputFile inputFile, SensorStorage sensorStorage) { + this.inputFile = inputFile; + this.sensorStorage = sensorStorage; } @Override public HighlightingBuilder newHighlighting() { - return new DefaultHighlightingBuilder(component.key(), cache, builder); + DefaultHighlighting defaultHighlighting = new DefaultHighlighting(sensorStorage); + defaultHighlighting.onFile(inputFile); + return new DefaultHighlightingBuilder(defaultHighlighting); } @Override public Component component() { - return component; - } - - public SyntaxHighlightingDataBuilder getHighlightingRules() { - return builder; + return new InputFileComponent(inputFile); } private static class DefaultHighlightingBuilder implements HighlightingBuilder { - private final SyntaxHighlightingDataBuilder builder; - private String componentKey; - private ComponentDataCache cache; + private final DefaultHighlighting defaultHighlighting; - public DefaultHighlightingBuilder(String componentKey, ComponentDataCache cache, SyntaxHighlightingDataBuilder builder) { - this.componentKey = componentKey; - this.cache = cache; - this.builder = builder; + public DefaultHighlightingBuilder(DefaultHighlighting defaultHighlighting) { + this.defaultHighlighting = defaultHighlighting; } @Override public HighlightingBuilder highlight(int startOffset, int endOffset, String typeOfText) { TypeOfText type = org.sonar.api.batch.sensor.highlighting.TypeOfText.forCssClass(typeOfText); - builder.registerHighlightingRule(startOffset, endOffset, type); + defaultHighlighting.highlight(startOffset, endOffset, type); return this; } @Override public void done() { - cache.setData(componentKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING, builder.build()); + defaultHighlighting.save(); } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java index 0bca8bd9997..2ca34b67a9a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbol.java @@ -21,38 +21,47 @@ package org.sonar.batch.source; import com.google.common.base.Objects; +import org.sonar.api.batch.fs.TextRange; import java.io.Serializable; public class DefaultSymbol implements org.sonar.api.source.Symbol, Serializable { - private final int declarationStartOffset; - private final int declarationEndOffset; + private TextRange range; + private int length; - public DefaultSymbol(int startOffset, int endOffset) { - this.declarationStartOffset = startOffset; - this.declarationEndOffset = endOffset; + public DefaultSymbol(TextRange range, int length) { + this.range = range; + this.length = length; } @Override public int getDeclarationStartOffset() { - return declarationStartOffset; + throw new UnsupportedOperationException("getDeclarationStartOffset"); } @Override public int getDeclarationEndOffset() { - return declarationEndOffset; + throw new UnsupportedOperationException("getDeclarationEndOffset"); } @Override public String getFullyQualifiedName() { - return null; + throw new UnsupportedOperationException("getFullyQualifiedName"); + } + + public TextRange range() { + return range; + } + + public int getLength() { + return length; } @Override public String toString() { return Objects.toStringHelper("Symbol") - .add("offset", String.format("%d-%d", declarationStartOffset, declarationEndOffset)) + .add("range", range) .toString(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java index 18b3e06df6b..2ab9999bf1b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolTable.java @@ -20,25 +20,22 @@ package org.sonar.batch.source; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.source.Symbol; import org.sonar.api.source.Symbolizable; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; public class DefaultSymbolTable implements Symbolizable.SymbolTable { - private Map<Symbol, Set<Integer>> referencesBySymbol; + private Map<Symbol, Set<TextRange>> referencesBySymbol; - private DefaultSymbolTable(Map<Symbol, Set<Integer>> referencesBySymbol) { + private DefaultSymbolTable(Map<Symbol, Set<TextRange>> referencesBySymbol) { this.referencesBySymbol = referencesBySymbol; } - public Map<Symbol, Set<Integer>> getReferencesBySymbol() { + public Map<Symbol, Set<TextRange>> getReferencesBySymbol() { return referencesBySymbol; } @@ -53,22 +50,28 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { @Override public List<Integer> references(Symbol symbol) { - return new ArrayList<Integer>(referencesBySymbol.get(symbol)); + throw new UnsupportedOperationException("references"); } public static class Builder implements Symbolizable.SymbolTableBuilder { - private final Map<Symbol, Set<Integer>> referencesBySymbol = new LinkedHashMap<Symbol, Set<Integer>>(); - private final String componentKey; + private final Map<Symbol, Set<TextRange>> referencesBySymbol = new LinkedHashMap<Symbol, Set<TextRange>>(); + private final DefaultInputFile inputFile; - public Builder(String componentKey) { - this.componentKey = componentKey; + public Builder(DefaultInputFile inputFile) { + this.inputFile = inputFile; } @Override public Symbol newSymbol(int fromOffset, int toOffset) { - Symbol symbol = new DefaultSymbol(fromOffset, toOffset); - referencesBySymbol.put(symbol, new TreeSet<Integer>()); + TextRange declarationRange = inputFile.newRange(fromOffset, toOffset); + DefaultSymbol symbol = new DefaultSymbol(declarationRange, toOffset - fromOffset); + referencesBySymbol.put(symbol, new TreeSet<TextRange>(new Comparator<TextRange>() { + @Override + public int compare(TextRange o1, TextRange o2) { + return o1.start().compareTo(o2.start()); + } + })); return symbol; } @@ -77,10 +80,12 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { if (!referencesBySymbol.containsKey(symbol)) { throw new UnsupportedOperationException("Cannot add reference to a symbol in another file"); } - if (fromOffset >= symbol.getDeclarationStartOffset() && fromOffset < symbol.getDeclarationEndOffset()) { - throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol + " in " + componentKey); + TextRange referenceRange = inputFile.newRange(fromOffset, fromOffset + ((DefaultSymbol) symbol).getLength()); + + if (referenceRange.overlap(((DefaultSymbol) symbol).range())) { + throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol + " in " + inputFile.key()); } - referencesBySymbol.get(symbol).add(fromOffset); + referencesBySymbol.get(symbol).add(referenceRange); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java index 8a6f6421408..037913f06cd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/DefaultSymbolizable.java @@ -20,35 +20,34 @@ package org.sonar.batch.source; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.component.Component; import org.sonar.api.source.Symbolizable; -import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.symbol.SymbolData; -import org.sonar.core.source.SnapshotDataTypes; +import org.sonar.batch.deprecated.InputFileComponent; +import org.sonar.batch.sensor.DefaultSensorStorage; public class DefaultSymbolizable implements Symbolizable { - private final ComponentDataCache cache; - private final Component component; + private final DefaultInputFile inputFile; + private final DefaultSensorStorage sensorStorage; - public DefaultSymbolizable(ComponentDataCache cache, Component component) { - this.cache = cache; - this.component = component; + public DefaultSymbolizable(DefaultInputFile inputFile, DefaultSensorStorage sensorStorage) { + this.inputFile = inputFile; + this.sensorStorage = sensorStorage; } @Override public Component component() { - return component; + return new InputFileComponent(inputFile); } @Override public SymbolTableBuilder newSymbolTableBuilder() { - return new DefaultSymbolTable.Builder(component.key()); + return new DefaultSymbolTable.Builder(inputFile); } @Override public void setSymbolTable(SymbolTable symbolTable) { - SymbolData symbolData = new SymbolData(((DefaultSymbolTable) symbolTable).getReferencesBySymbol()); - cache.setData(component().key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING, symbolData); + sensorStorage.store(inputFile, ((DefaultSymbolTable) symbolTable).getReferencesBySymbol()); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java index d20fbebb147..fa9326a1604 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightableBuilder.java @@ -20,10 +20,14 @@ package org.sonar.batch.source; import com.google.common.collect.ImmutableSet; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.component.Component; import org.sonar.api.resources.Qualifiers; import org.sonar.api.source.Highlightable; -import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.component.PerspectiveBuilder; import org.sonar.core.component.ResourceComponent; @@ -31,27 +35,30 @@ import javax.annotation.CheckForNull; import java.util.Set; -/** - * @since 3.6 - * @deprecated since 4.5 no more used in batch 2.0 - */ -@Deprecated public class HighlightableBuilder extends PerspectiveBuilder<Highlightable> { private static final Set<String> SUPPORTED_QUALIFIERS = ImmutableSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE); - private final ComponentDataCache cache; + private final ResourceCache cache; + private final SensorStorage sensorStorage; - public HighlightableBuilder(ComponentDataCache cache) { + public HighlightableBuilder(ResourceCache cache, SensorStorage sensorStorage) { super(Highlightable.class); this.cache = cache; + this.sensorStorage = sensorStorage; } @CheckForNull @Override - protected Highlightable loadPerspective(Class<Highlightable> perspectiveClass, Component component) { + public Highlightable loadPerspective(Class<Highlightable> perspectiveClass, Component component) { boolean supported = SUPPORTED_QUALIFIERS.contains(component.qualifier()); if (supported && component instanceof ResourceComponent) { - return new DefaultHighlightable(component, cache); + BatchResource batchComponent = cache.get(component.key()); + if (batchComponent != null) { + InputFile path = (InputFile) batchComponent.inputPath(); + if (path != null) { + return new DefaultHighlightable((DefaultInputFile) path, sensorStorage); + } + } } return null; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingCodeBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingCodeBuilder.java index 8da5515fdaa..687f2fecc2d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingCodeBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingCodeBuilder.java @@ -21,9 +21,8 @@ package org.sonar.batch.source; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.batch.sensor.highlighting.NewHighlighting; import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import org.sonar.batch.highlighting.SyntaxHighlightingData; -import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder; import org.sonar.colorizer.HtmlCodeBuilder; import java.util.regex.Matcher; @@ -33,12 +32,16 @@ public class HighlightingCodeBuilder extends HtmlCodeBuilder { private static final Logger LOG = LoggerFactory.getLogger(HighlightingCodeBuilder.class); - private SyntaxHighlightingDataBuilder highlightingBuilder = new SyntaxHighlightingDataBuilder(); private int currentOffset = 0; private static final Pattern START_TAG_PATTERN = Pattern.compile("<span class=\"(.+)\">"); private static final Pattern END_TAG_PATTERN = Pattern.compile("</span>"); private int startOffset = -1; private String cssClass; + private final NewHighlighting highlighting; + + public HighlightingCodeBuilder(NewHighlighting highlighting) { + this.highlighting = highlighting; + } @Override public Appendable append(CharSequence csq) { @@ -67,7 +70,7 @@ public class HighlightingCodeBuilder extends HtmlCodeBuilder { } else { Matcher endMatcher = END_TAG_PATTERN.matcher(htmlTag); if (endMatcher.matches()) { - highlightingBuilder.registerHighlightingRule(startOffset, currentOffset, TypeOfText.forCssClass(cssClass)); + highlighting.highlight(startOffset, currentOffset, TypeOfText.forCssClass(cssClass)); startOffset = -1; } else { LOG.warn("Expected to match highlighting end html tag but was: " + htmlTag); @@ -85,8 +88,4 @@ public class HighlightingCodeBuilder extends HtmlCodeBuilder { throw new UnsupportedOperationException(); } - public SyntaxHighlightingData getHighlightingData() { - return highlightingBuilder.build(); - } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingRenderer.java b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingRenderer.java index e2a5faa592e..6858d6de1f0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingRenderer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/HighlightingRenderer.java @@ -19,7 +19,7 @@ */ package org.sonar.batch.source; -import org.sonar.batch.highlighting.SyntaxHighlightingData; +import org.sonar.api.batch.sensor.highlighting.NewHighlighting; import org.sonar.channel.Channel; import org.sonar.channel.CodeReader; import org.sonar.colorizer.HtmlCodeBuilder; @@ -31,14 +31,13 @@ import java.util.List; public class HighlightingRenderer { - public SyntaxHighlightingData render(Reader code, List<? extends Channel<HtmlCodeBuilder>> tokenizers) { + public void render(Reader code, List<? extends Channel<HtmlCodeBuilder>> tokenizers, NewHighlighting highlighting) { List<Channel<HtmlCodeBuilder>> allTokenizers = new ArrayList<Channel<HtmlCodeBuilder>>(); - HighlightingCodeBuilder codeBuilder = new HighlightingCodeBuilder(); + HighlightingCodeBuilder codeBuilder = new HighlightingCodeBuilder(highlighting); allTokenizers.addAll(tokenizers); new TokenizerDispatcher(allTokenizers).colorize(new CodeReader(code), codeBuilder); - - return codeBuilder.getHighlightingData(); + highlighting.save(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java b/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java index 5851824f169..a38d5c2df4f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/LinesSensor.java @@ -29,18 +29,10 @@ import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; -import org.sonar.batch.scan.filesystem.InputFileMetadata; -import org.sonar.batch.scan.filesystem.InputPathCache; @Phase(name = Phase.Name.PRE) public final class LinesSensor implements Sensor { - private final InputPathCache inputPathCache; - - public LinesSensor(InputPathCache inputPathCache) { - this.inputPathCache = inputPathCache; - } - @Override public void describe(SensorDescriptor descriptor) { descriptor.name("Lines Sensor"); @@ -58,11 +50,10 @@ public final class LinesSensor implements Sensor { .save(); if (f.language() == null) { // As an approximation for files with no language plugin we consider every non blank line as ncloc - InputFileMetadata metadata = inputPathCache.getFileMetadata((DefaultInputFile) f); ((DefaultMeasure<Integer>) context.<Integer>newMeasure() .onFile(f) .forMetric(CoreMetrics.NCLOC) - .withValue(metadata.nonBlankLines())) + .withValue(((DefaultInputFile) f).nonBlankLines())) .save(); // No test and no coverage on those files ((DefaultMeasure<Integer>) context.<Integer>newMeasure() diff --git a/sonar-batch/src/main/java/org/sonar/batch/source/SymbolizableBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/source/SymbolizableBuilder.java index 915dc285fa6..212c31d87b4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/source/SymbolizableBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/source/SymbolizableBuilder.java @@ -20,22 +20,47 @@ package org.sonar.batch.source; +import com.google.common.collect.ImmutableSet; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.component.Component; +import org.sonar.api.resources.Qualifiers; import org.sonar.api.source.Symbolizable; -import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.sensor.DefaultSensorStorage; import org.sonar.core.component.PerspectiveBuilder; +import org.sonar.core.component.ResourceComponent; + +import javax.annotation.CheckForNull; + +import java.util.Set; public class SymbolizableBuilder extends PerspectiveBuilder<Symbolizable> { - private final ComponentDataCache cache; + private static final Set<String> SUPPORTED_QUALIFIERS = ImmutableSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE); + private final ResourceCache cache; + private final DefaultSensorStorage sensorStorage; - public SymbolizableBuilder(ComponentDataCache cache) { + public SymbolizableBuilder(ResourceCache cache, DefaultSensorStorage sensorStorage) { super(Symbolizable.class); this.cache = cache; + this.sensorStorage = sensorStorage; } + @CheckForNull @Override - protected Symbolizable loadPerspective(Class<Symbolizable> perspectiveClass, Component component) { - return new DefaultSymbolizable(cache, component); + public Symbolizable loadPerspective(Class<Symbolizable> perspectiveClass, Component component) { + boolean supported = SUPPORTED_QUALIFIERS.contains(component.qualifier()); + if (supported && component instanceof ResourceComponent) { + BatchResource batchComponent = cache.get(component.key()); + if (batchComponent != null) { + InputFile path = (InputFile) batchComponent.inputPath(); + if (path != null) { + return new DefaultSymbolizable((DefaultInputFile) path, sensorStorage); + } + } + } + return null; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java deleted file mode 100644 index fb7b92dc038..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/symbol/DefaultSymbolTableBuilder.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.symbol; - -import org.sonar.api.source.Symbol; -import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.source.DefaultSymbol; -import org.sonar.core.source.SnapshotDataTypes; - -import java.io.Serializable; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -public class DefaultSymbolTableBuilder { - - private final String componentKey; - private final ComponentDataCache cache; - private final Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol = new LinkedHashMap<org.sonar.api.source.Symbol, Set<Integer>>(); - - public DefaultSymbolTableBuilder(String componentKey, ComponentDataCache cache) { - this.componentKey = componentKey; - this.cache = cache; - } - - public Symbol newSymbol(int fromOffset, int toOffset) { - org.sonar.api.source.Symbol symbol = new DefaultSymbol(fromOffset, toOffset); - referencesBySymbol.put(symbol, new TreeSet<Integer>()); - return symbol; - } - - public void newReference(Symbol symbol, int fromOffset) { - if (!referencesBySymbol.containsKey(symbol)) { - throw new UnsupportedOperationException("Cannot add reference to a symbol in another file"); - } - if (fromOffset >= symbol.getDeclarationStartOffset() && fromOffset < symbol.getDeclarationEndOffset()) { - throw new UnsupportedOperationException("Cannot add reference (" + fromOffset + ") overlapping " + symbol); - } - referencesBySymbol.get(symbol).add(fromOffset); - } - - public SymbolData build() { - return new SymbolData(referencesBySymbol); - } - - public void done() { - cache.setData(componentKey, SnapshotDataTypes.SYMBOL_HIGHLIGHTING, build()); - } - - public static class SymbolComparator implements Comparator<Symbol>, Serializable { - @Override - public int compare(Symbol left, Symbol right) { - return left.getDeclarationStartOffset() - right.getDeclarationStartOffset(); - } - } - - public static class ReferenceComparator implements Comparator<Integer>, Serializable { - @Override - public int compare(Integer left, Integer right) { - int result; - if (left != null && right != null) { - result = left - right; - } else { - result = left == null ? -1 : 1; - } - return result; - } - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java b/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java deleted file mode 100644 index 616429242ca..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/symbol/SymbolData.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.symbol; - -import org.sonar.api.source.Symbol; -import org.sonar.batch.index.Data; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -public class SymbolData implements Data { - - public static final String FIELD_SEPARATOR = ","; - public static final String SYMBOL_SEPARATOR = ";"; - - private final Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol; - - public SymbolData(Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol) { - this.referencesBySymbol = referencesBySymbol; - } - - public Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol() { - return referencesBySymbol; - } - - @Override - public String writeString() { - StringBuilder sb = new StringBuilder(); - - for (Symbol symbol : referencesBySymbol.keySet()) { - if (sb.length() > 0) { - sb.append(SYMBOL_SEPARATOR); - } - - sb.append(symbol.getDeclarationStartOffset()) - .append(FIELD_SEPARATOR) - .append(symbol.getDeclarationEndOffset()) - .append(FIELD_SEPARATOR) - .append(symbol.getDeclarationStartOffset()); - Collection<Integer> symbolReferences = referencesBySymbol.get(symbol); - for (Integer symbolReference : symbolReferences) { - sb.append(FIELD_SEPARATOR).append(symbolReference); - } - } - - return sb.toString(); - } - -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java deleted file mode 100644 index b4c4828b204..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilderTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.highlighting; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; - -import java.util.Collection; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.api.batch.sensor.highlighting.TypeOfText.COMMENT; -import static org.sonar.api.batch.sensor.highlighting.TypeOfText.CPP_DOC; -import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD; - -public class SyntaxHighlightingDataBuilderTest { - - private Collection<SyntaxHighlightingRule> highlightingRules; - - @Rule - public ExpectedException throwable = ExpectedException.none(); - - @Before - public void setUpSampleRules() { - - SyntaxHighlightingDataBuilder highlightingDataBuilder = new SyntaxHighlightingDataBuilder(); - highlightingDataBuilder.registerHighlightingRule(0, 10, COMMENT); - highlightingDataBuilder.registerHighlightingRule(10, 12, KEYWORD); - highlightingDataBuilder.registerHighlightingRule(24, 38, KEYWORD); - highlightingDataBuilder.registerHighlightingRule(42, 50, KEYWORD); - highlightingDataBuilder.registerHighlightingRule(24, 65, CPP_DOC); - highlightingDataBuilder.registerHighlightingRule(12, 20, COMMENT); - - highlightingRules = highlightingDataBuilder.getSyntaxHighlightingRuleSet(); - } - - @Test - public void should_register_highlighting_rule() throws Exception { - assertThat(highlightingRules).hasSize(6); - } - - @Test - public void should_order_by_start_then_end_offset() throws Exception { - assertThat(highlightingRules).extracting("startPosition").containsOnly(0, 10, 12, 24, 24, 42); - assertThat(highlightingRules).extracting("endPosition").containsOnly(10, 12, 20, 38, 65, 50); - assertThat(highlightingRules).extracting("textType").containsOnly(COMMENT, KEYWORD, COMMENT, KEYWORD, CPP_DOC, KEYWORD); - } - - @Test - public void should_suport_overlapping() throws Exception { - SyntaxHighlightingDataBuilder builder = new SyntaxHighlightingDataBuilder(); - builder.registerHighlightingRule(0, 15, KEYWORD); - builder.registerHighlightingRule(8, 12, CPP_DOC); - builder.build(); - } - - @Test - public void should_prevent_boudaries_overlapping() throws Exception { - throwable.expect(IllegalStateException.class); - throwable.expectMessage("Cannot register highlighting rule for characters from 8 to 15 as it overlaps at least one existing rule"); - - SyntaxHighlightingDataBuilder builder = new SyntaxHighlightingDataBuilder(); - builder.registerHighlightingRule(0, 10, KEYWORD); - builder.registerHighlightingRule(8, 15, KEYWORD); - builder.build(); - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java b/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java deleted file mode 100644 index dcebfe2893e..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/highlighting/SyntaxHighlightingDataTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.highlighting; - -import com.google.common.collect.Lists; -import org.junit.Test; -import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.api.batch.sensor.highlighting.TypeOfText.COMMENT; -import static org.sonar.api.batch.sensor.highlighting.TypeOfText.CPP_DOC; -import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD; - -public class SyntaxHighlightingDataTest { - - @Test - public void should_serialize_rules_to_string() throws Exception { - - List<SyntaxHighlightingRule> orderedHighlightingRules = Lists.newArrayList( - SyntaxHighlightingRule.create(0, 10, COMMENT), - SyntaxHighlightingRule.create(10, 12, KEYWORD), - SyntaxHighlightingRule.create(12, 20, COMMENT), - SyntaxHighlightingRule.create(24, 38, KEYWORD), - SyntaxHighlightingRule.create(24, 65, CPP_DOC), - SyntaxHighlightingRule.create(42, 50, KEYWORD) - ); - - String serializedRules = new SyntaxHighlightingData(orderedHighlightingRules).writeString(); - assertThat(serializedRules).isEqualTo("0,10,cd;10,12,k;12,20,cd;24,38,k;24,65,cppd;42,50,k"); - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java deleted file mode 100644 index 5fa9587f424..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/index/ComponentDataCacheTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.index; - -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ComponentDataCacheTest { - - @ClassRule - public static TemporaryFolder temp = new TemporaryFolder(); - - Caches caches; - - @Before - public void start() throws Exception { - caches = CachesTest.createCacheOnTemp(temp); - caches.start(); - } - - @After - public void stop() { - caches.stop(); - } - - @Test - public void should_get_and_set_string_data() { - ComponentDataCache cache = new ComponentDataCache(caches); - cache.setStringData("org/struts/Action.java", "SYNTAX", "1:foo;3:bar"); - assertThat(cache.getStringData("org/struts/Action.java", "SYNTAX")).isEqualTo("1:foo;3:bar"); - assertThat(cache.getStringData("org/struts/Action.java", "OTHER")).isNull(); - assertThat(cache.getStringData("Other.java", "SYNTAX")).isNull(); - assertThat(cache.getStringData("Other.java", "OTHER")).isNull(); - } - - @Test - public void should_get_and_set_data() { - ComponentDataCache cache = new ComponentDataCache(caches); - cache.setData("org/struts/Action.java", "COUNT", new LongData(1234L)); - LongData count = cache.getData("org/struts/Action.java", "COUNT"); - assertThat(count.data()).isEqualTo(1234L); - } - - static class LongData implements Data { - - private long data; - - LongData() { - } - - LongData(long data) { - this.data = data; - } - - public long data() { - return data; - } - - @Override - public String writeString() { - return String.valueOf(data); - } - - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java index a37bef0da56..50a2554aa37 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java @@ -30,23 +30,18 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.duplication.Duplication; import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication; -import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; -import org.sonar.api.source.Symbol; import org.sonar.batch.duplication.DuplicationCache; -import org.sonar.batch.highlighting.SyntaxHighlightingData; -import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder; +import org.sonar.batch.protocol.Constants.HighlightingType; +import org.sonar.batch.protocol.output.*; +import org.sonar.batch.protocol.output.BatchReport.Range; import org.sonar.batch.protocol.output.BatchReport.Scm; import org.sonar.batch.protocol.output.BatchReport.Scm.Changeset; -import org.sonar.batch.protocol.output.BatchReportWriter; -import org.sonar.batch.report.PublishReportJob; -import org.sonar.batch.scan.filesystem.InputFileMetadata; +import org.sonar.batch.protocol.output.BatchReport.SyntaxHighlighting.HighlightingRule; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.scan.measure.MeasureCache; -import org.sonar.batch.source.CodeColorizers; -import org.sonar.batch.symbol.DefaultSymbolTableBuilder; -import org.sonar.core.source.SnapshotDataTypes; import org.sonar.server.source.db.FileSourceDb; import java.io.File; @@ -64,24 +59,23 @@ public class SourceDataFactoryTest { public TemporaryFolder temp = new TemporaryFolder(); private MeasureCache measureCache = mock(MeasureCache.class); - private ComponentDataCache componentDataCache = mock(ComponentDataCache.class); private DuplicationCache duplicationCache = mock(DuplicationCache.class); - private CodeColorizers colorizers = mock(CodeColorizers.class); private DefaultInputFile inputFile; - private InputFileMetadata metadata; private SourceDataFactory sut; private FileSourceDb.Data.Builder output; private File reportDir; + private BatchReportWriter batchReportWriter; @Before public void setUp() throws Exception { - PublishReportJob publishReportJob = mock(PublishReportJob.class); + ReportPublisher reportPublisher = mock(ReportPublisher.class); reportDir = temp.newFolder(); - when(publishReportJob.getReportDir()).thenReturn(reportDir); + batchReportWriter = new BatchReportWriter(reportDir); + when(reportPublisher.getReportDir()).thenReturn(reportDir); ResourceCache resourceCache = new ResourceCache(); resourceCache.add(org.sonar.api.resources.File.create("src/Foo.java").setEffectiveKey("module_key:src/Foo.java"), null); when(measureCache.byMetric(anyString(), anyString())).thenReturn(Collections.<Measure>emptyList()); - sut = new SourceDataFactory(measureCache, componentDataCache, duplicationCache, colorizers, publishReportJob, resourceCache); + sut = new SourceDataFactory(measureCache, duplicationCache, reportPublisher, resourceCache); // generate a file with 3 lines File baseDir = temp.newFolder(); DefaultFileSystem fs = new DefaultFileSystem(baseDir.toPath()); @@ -89,7 +83,6 @@ public class SourceDataFactoryTest { .setLines(3) .setCharset(Charsets.UTF_8); fs.add(inputFile); - metadata = new InputFileMetadata(); FileUtils.write(inputFile.file(), "one\ntwo\nthree\n"); output = sut.createForSource(inputFile); when(duplicationCache.byComponent(anyString())).thenReturn(Collections.<DefaultDuplication>emptyList()); @@ -106,7 +99,7 @@ public class SourceDataFactoryTest { @Test public void consolidateData() throws Exception { - byte[] bytes = sut.consolidateData(inputFile, metadata); + byte[] bytes = sut.consolidateData(inputFile); assertThat(bytes).isNotEmpty(); } @@ -247,9 +240,7 @@ public class SourceDataFactoryTest { @Test public void applyHighlighting_missing() throws Exception { - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(null); - - sut.applyHighlighting(inputFile, metadata, output); + sut.applyHighlighting(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).hasHighlighting()).isFalse(); @@ -259,49 +250,28 @@ public class SourceDataFactoryTest { @Test public void applyHighlighting() throws Exception { - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 4, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 5, TypeOfText.COMMENT) - .registerHighlightingRule(7, 16, TypeOfText.CONSTANT) - .build(); - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); - metadata.setOriginalLineOffsets(new int[] {0, 4, 7}); - - sut.applyHighlighting(inputFile, metadata, output); + batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList( + newRule(1, 0, 1, 4, HighlightingType.ANNOTATION), + newRule(2, 0, 2, 1, HighlightingType.COMMENT), + newRule(3, 1, 3, 9, HighlightingType.CONSTANT))); + inputFile.setOriginalLineOffsets(new int[] {0, 4, 7}); + sut.applyHighlighting(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,4,a"); assertThat(data.getLines(1).getHighlighting()).isEqualTo("0,1,cd"); - assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,9,c"); - } - - @Test - public void applyHighlighting_ignore_bad_line() throws Exception { - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 4, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 5, TypeOfText.COMMENT) - .registerHighlightingRule(7, 25, TypeOfText.CONSTANT) - .build(); - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); - metadata.setOriginalLineOffsets(new int[] {0, 4, 7, 15}); - - sut.applyHighlighting(inputFile, metadata, output); - - FileSourceDb.Data data = output.build(); - assertThat(data.getLinesCount()).isEqualTo(3); + assertThat(data.getLines(2).getHighlighting()).isEqualTo("1,9,c"); } @Test public void applyHighlighting_multiple_lines() throws Exception { - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 9, TypeOfText.COMMENT) - .registerHighlightingRule(10, 16, TypeOfText.CONSTANT) - .build(); - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); - metadata.setOriginalLineOffsets(new int[] {0, 4, 7}); + batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList( + newRule(1, 0, 1, 3, HighlightingType.ANNOTATION), + newRule(2, 0, 3, 2, HighlightingType.COMMENT), + newRule(3, 3, 3, 9, HighlightingType.CONSTANT))); + inputFile.setOriginalLineOffsets(new int[] {0, 4, 7}); - sut.applyHighlighting(inputFile, metadata, output); + sut.applyHighlighting(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a"); @@ -311,16 +281,15 @@ public class SourceDataFactoryTest { @Test public void applyHighlighting_nested_rules() throws Exception { - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 6, TypeOfText.COMMENT) - .registerHighlightingRule(7, 16, TypeOfText.CONSTANT) - .registerHighlightingRule(8, 15, TypeOfText.KEYWORD) - .build(); - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); - metadata.setOriginalLineOffsets(new int[] {0, 4, 7}); + batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList( + newRule(1, 0, 1, 3, HighlightingType.ANNOTATION), + newRule(2, 0, 2, 2, HighlightingType.COMMENT), + newRule(3, 0, 3, 9, HighlightingType.CONSTANT), + newRule(3, 1, 3, 8, HighlightingType.KEYWORD))); + + inputFile.setOriginalLineOffsets(new int[] {0, 4, 7}); - sut.applyHighlighting(inputFile, metadata, output); + sut.applyHighlighting(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a"); @@ -330,16 +299,15 @@ public class SourceDataFactoryTest { @Test public void applyHighlighting_nested_rules_and_multiple_lines() throws Exception { - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 6, TypeOfText.COMMENT) - .registerHighlightingRule(4, 16, TypeOfText.CONSTANT) - .registerHighlightingRule(8, 15, TypeOfText.KEYWORD) - .build(); - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); - metadata.setOriginalLineOffsets(new int[] {0, 4, 7}); + batchReportWriter.writeComponentSyntaxHighlighting(1, Arrays.asList( + newRule(1, 0, 1, 3, HighlightingType.ANNOTATION), + newRule(2, 0, 3, 9, HighlightingType.CONSTANT), + newRule(2, 0, 2, 2, HighlightingType.COMMENT), + newRule(3, 1, 3, 8, HighlightingType.KEYWORD))); - sut.applyHighlighting(inputFile, metadata, output); + inputFile.setOriginalLineOffsets(new int[] {0, 4, 7}); + + sut.applyHighlighting(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a"); @@ -347,11 +315,26 @@ public class SourceDataFactoryTest { assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,9,c;1,8,k"); } + private HighlightingRule newRule(int startLine, int startOffset, int endLine, int endOffset, HighlightingType type) { + return BatchReport.SyntaxHighlighting.HighlightingRule.newBuilder() + .setRange(Range.newBuilder().setStartLine(startLine).setStartOffset(startOffset).setEndLine(endLine).setEndOffset(endOffset).build()) + .setType(type) + .build(); + } + + private BatchReport.Symbols.Symbol newSymbol(int startLine, int startOffset, int endLine, int endOffset, + int startLine1, int startOffset1, int endLine1, int endOffset1, + int startLine2, int startOffset2, int endLine2, int endOffset2) { + return BatchReport.Symbols.Symbol.newBuilder() + .setDeclaration(Range.newBuilder().setStartLine(startLine).setStartOffset(startOffset).setEndLine(endLine).setEndOffset(endOffset).build()) + .addReference(Range.newBuilder().setStartLine(startLine1).setStartOffset(startOffset1).setEndLine(endLine1).setEndOffset(endOffset1).build()) + .addReference(Range.newBuilder().setStartLine(startLine2).setStartOffset(startOffset2).setEndLine(endLine2).setEndOffset(endOffset2).build()) + .build(); + } + @Test public void applySymbolReferences_missing() throws Exception { - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(null); - - sut.applySymbolReferences(inputFile, metadata, output); + sut.applySymbolReferences(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).hasSymbols()).isFalse(); @@ -361,17 +344,17 @@ public class SourceDataFactoryTest { @Test public void applySymbolReferences() throws Exception { - DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(inputFile.key(), null); - Symbol s1 = symbolBuilder.newSymbol(1, 2); - symbolBuilder.newReference(s1, 4); - symbolBuilder.newReference(s1, 11); - Symbol s2 = symbolBuilder.newSymbol(4, 6); - symbolBuilder.newReference(s2, 0); - symbolBuilder.newReference(s2, 7); - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(symbolBuilder.build()); - metadata.setOriginalLineOffsets(new int[] {0, 4, 7}); - - sut.applySymbolReferences(inputFile, metadata, output); + batchReportWriter.writeComponentSymbols(1, Arrays.asList( + newSymbol(1, 1, 1, 2, + 2, 0, 2, 1, + 3, 4, 3, 5), + newSymbol(2, 0, 2, 2, + 1, 0, 1, 2, + 3, 0, 3, 2) + )); + inputFile.setOriginalLineOffsets(new int[] {0, 4, 7}); + + sut.applySymbolReferences(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).getSymbols()).isEqualTo("1,2,1;0,2,2"); @@ -381,17 +364,18 @@ public class SourceDataFactoryTest { @Test public void applySymbolReferences_declaration_order_is_not_important() throws Exception { - DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(inputFile.key(), null); - Symbol s2 = symbolBuilder.newSymbol(4, 6); - symbolBuilder.newReference(s2, 7); - symbolBuilder.newReference(s2, 0); - Symbol s1 = symbolBuilder.newSymbol(1, 2); - symbolBuilder.newReference(s1, 11); - symbolBuilder.newReference(s1, 4); - when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(symbolBuilder.build()); - metadata.setOriginalLineOffsets(new int[] {0, 4, 7}); - - sut.applySymbolReferences(inputFile, metadata, output); + batchReportWriter.writeComponentSymbols(1, Arrays.asList( + newSymbol(2, 0, 2, 2, + 1, 0, 1, 2, + 3, 0, 3, 2), + newSymbol(1, 1, 1, 2, + 2, 0, 2, 1, + 3, 4, 3, 5) + + )); + inputFile.setOriginalLineOffsets(new int[] {0, 4, 7}); + + sut.applySymbolReferences(inputFile, output); FileSourceDb.Data data = output.build(); assertThat(data.getLines(0).getSymbols()).isEqualTo("1,2,1;0,2,2"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java index e4dc6d3099f..a0021e72025 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java @@ -44,7 +44,6 @@ import org.sonar.api.rules.RuleFinder; import org.sonar.api.utils.Duration; import org.sonar.api.utils.System2; import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.scan.filesystem.InputFileMetadata; import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.db.IssueChangeDto; @@ -60,19 +59,8 @@ import java.util.List; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyCollection; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.RETURNS_MOCKS; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.*; public class IssueTrackingDecoratorTest { @@ -132,7 +120,6 @@ public class IssueTrackingDecoratorTest { List<ServerIssue> dbIssues = Collections.emptyList(); when(initialOpenIssues.selectAndRemoveIssues("struts:Action.java")).thenReturn(dbIssues); when(inputPathCache.getFile("foo", "Action.java")).thenReturn(mock(DefaultInputFile.class)); - when(inputPathCache.getFileMetadata("foo", "Action.java")).thenReturn(new InputFileMetadata()); decorator.doDecorate(file); // Apply filters, track, apply transitions, notify extensions then update cache @@ -161,7 +148,6 @@ public class IssueTrackingDecoratorTest { when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult); when(inputPathCache.getFile("foo", "Action.java")).thenReturn(mock(DefaultInputFile.class)); - when(inputPathCache.getFileMetadata("foo", "Action.java")).thenReturn(new InputFileMetadata()); decorator.doDecorate(file); diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java index 42f7bfc8330..8b42d6a5b67 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java @@ -71,7 +71,7 @@ public class HighlightingMediumTest { File xooFile = new File(srcDir, "sample.xoo"); File xoohighlightingFile = new File(srcDir, "sample.xoo.highlighting"); - FileUtils.write(xooFile, "Sample xoo\ncontent"); + FileUtils.write(xooFile, "Sample xoo\ncontent plop"); FileUtils.write(xoohighlightingFile, "0:10:s\n11:18:k"); TaskResult result = tester.newTask() @@ -87,10 +87,10 @@ public class HighlightingMediumTest { .start(); InputFile file = result.inputFile("src/sample.xoo"); - assertThat(result.highlightingTypeFor(file, 0)).containsExactly(TypeOfText.STRING); - assertThat(result.highlightingTypeFor(file, 9)).containsExactly(TypeOfText.STRING); - assertThat(result.highlightingTypeFor(file, 10)).isEmpty(); - assertThat(result.highlightingTypeFor(file, 11)).containsExactly(TypeOfText.KEYWORD); + assertThat(result.highlightingTypeFor(file, 1, 0)).containsExactly(TypeOfText.STRING); + assertThat(result.highlightingTypeFor(file, 1, 9)).containsExactly(TypeOfText.STRING); + assertThat(result.highlightingTypeFor(file, 2, 0)).containsExactly(TypeOfText.KEYWORD); + assertThat(result.highlightingTypeFor(file, 2, 8)).isEmpty(); } @@ -126,7 +126,7 @@ public class HighlightingMediumTest { System.out.println("Duration: " + (System.currentTimeMillis() - start)); InputFile file = result.inputFile("src/sample.xoo"); - assertThat(result.highlightingTypeFor(file, 0)).containsExactly(TypeOfText.STRING); + assertThat(result.highlightingTypeFor(file, 1, 0)).containsExactly(TypeOfText.STRING); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java index a231c93f69b..b8ca587d1b9 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java @@ -28,6 +28,7 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.batch.protocol.output.BatchReport.Range; import org.sonar.xoo.XooPlugin; import java.io.File; @@ -65,6 +66,7 @@ public class SymbolMediumTest { File xooFile = new File(srcDir, "sample.xoo"); File xooSymbolFile = new File(srcDir, "sample.xoo.symbol"); FileUtils.write(xooFile, "Sample xoo\ncontent\nanother xoo"); + // Highlight xoo symbol FileUtils.write(xooSymbolFile, "7,10,27"); TaskResult result = tester.newTask() @@ -80,7 +82,7 @@ public class SymbolMediumTest { .start(); InputFile file = result.inputFile("src/sample.xoo"); - assertThat(result.symbolReferencesFor(file, 7, 10)).containsOnly(27); + assertThat(result.symbolReferencesFor(file, 1, 7)).containsOnly(Range.newBuilder().setStartLine(3).setStartOffset(8).setEndLine(3).setEndOffset(11).build()); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/PublishReportJobTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java index a636a5386df..5b9221dc92c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/report/PublishReportJobTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/report/ReportPublisherTest.java @@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class PublishReportJobTest extends AbstractDbUnitTestCase { +public class ReportPublisherTest extends AbstractDbUnitTestCase { private DefaultAnalysisMode mode; @@ -56,7 +56,7 @@ public class PublishReportJobTest extends AbstractDbUnitTestCase { public void should_log_successful_analysis() throws Exception { Settings settings = new Settings(); settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/"); - PublishReportJob job = new PublishReportJob(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisher[0]); + ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); Logger logger = mock(Logger.class); job.logSuccess(logger); @@ -69,7 +69,7 @@ public class PublishReportJobTest extends AbstractDbUnitTestCase { public void should_log_successful_preview_analysis() throws Exception { Settings settings = new Settings(); when(mode.isPreview()).thenReturn(true); - PublishReportJob job = new PublishReportJob(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisher[0]); + ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); Logger logger = mock(Logger.class); job.logSuccess(logger); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java index 5960fab5c2d..c62ccb0ae8c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ComponentIndexerTest.java @@ -30,15 +30,14 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; -import org.sonar.api.resources.AbstractLanguage; -import org.sonar.api.resources.Java; -import org.sonar.api.resources.Languages; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.*; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; import java.io.File; import java.io.IOException; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -97,7 +96,9 @@ public class ComponentIndexerTest { } private ComponentIndexer createIndexer(Languages languages) { - return new ComponentIndexer(project, languages, sonarIndex); + ResourceCache resourceCache = mock(ResourceCache.class); + when(resourceCache.get(any(Resource.class))).thenReturn(new BatchResource(1, org.sonar.api.resources.File.create("foo.php"), null)); + return new ComponentIndexer(project, languages, sonarIndex, resourceCache); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java index 2b5b1f4bcca..0c83abfa01e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderFactoryTest.java @@ -22,6 +22,7 @@ package org.sonar.batch.scan.filesystem; import org.junit.Test; import org.mockito.Mockito; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.config.Settings; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.DefaultAnalysisMode; @@ -39,7 +40,7 @@ public class InputFileBuilderFactoryTest { DefaultAnalysisMode analysisMode = mock(DefaultAnalysisMode.class); InputFileBuilderFactory factory = new InputFileBuilderFactory(ProjectDefinition.create().setKey("struts"), pathResolver, langDetectionFactory, - statusDetectionFactory, analysisMode, new Settings(), new FileMetadata(analysisMode)); + statusDetectionFactory, analysisMode, new Settings(), new FileMetadata()); InputFileBuilder builder = factory.create(fs); assertThat(builder.langDetection()).isNotNull(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java index df058f13c8f..2438c3de381 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileBuilderTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.config.Settings; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.PathUtils; @@ -66,7 +67,7 @@ public class InputFileBuilderTest { .thenReturn(InputFile.Status.ADDED); InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), - langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata(analysisMode)); + langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata()); DeprecatedDefaultInputFile inputFile = builder.create(srcFile); builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN); @@ -89,7 +90,7 @@ public class InputFileBuilderTest { when(fs.baseDir()).thenReturn(basedir); InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), - langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata(analysisMode)); + langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata()); DeprecatedDefaultInputFile inputFile = builder.create(srcFile); assertThat(inputFile).isNull(); @@ -109,11 +110,11 @@ public class InputFileBuilderTest { when(langDetection.language(any(InputFile.class))).thenReturn(null); InputFileBuilder builder = new InputFileBuilder("struts", new PathResolver(), - langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata(analysisMode)); + langDetection, statusDetection, fs, analysisMode, new Settings(), new FileMetadata()); DeprecatedDefaultInputFile inputFile = builder.create(srcFile); - InputFileMetadata metadata = builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN); + inputFile = builder.completeAndComputeMetadata(inputFile, InputFile.Type.MAIN); - assertThat(metadata).isNull(); + assertThat(inputFile).isNull(); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java index 7c943e06f66..f14e2a9b957 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java @@ -79,20 +79,4 @@ public class InputPathCacheTest { assertThat(cache.allFiles()).hasSize(1); } - @Test - public void should_add_input_file_metadata() throws Exception { - InputPathCache cache = new InputPathCache(); - cache.put("struts-core", "src/main/java/Bar.java", new InputFileMetadata() - .setHash("xyz") - .setNonBlankLines(2) - .setEmpty(true) - .setOriginalLineOffsets(new int[] {0, 4})); - - InputFileMetadata loadedFileMetadata = cache.getFileMetadata("struts-core", "src/main/java/Bar.java"); - assertThat(loadedFileMetadata.originalLineOffsets()).containsOnly(0, 4); - assertThat(loadedFileMetadata.hash()).isEqualTo("xyz"); - assertThat(loadedFileMetadata.nonBlankLines()).isEqualTo(2); - assertThat(loadedFileMetadata.isEmpty()).isTrue(); - - } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java index b65d7e8050c..60f04a9f626 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java @@ -38,11 +38,8 @@ import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency; import org.sonar.api.batch.sensor.issue.Issue.Severity; import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.config.Settings; import org.sonar.api.design.Dependency; -import org.sonar.api.issue.Issuable; -import org.sonar.api.issue.Issue; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.PersistenceMode; @@ -52,9 +49,10 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.rule.RuleKey; import org.sonar.batch.duplication.DuplicationCache; -import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.issue.ModuleIssues; +import org.sonar.batch.report.ReportPublisher; import org.sonar.batch.sensor.coverage.CoverageExclusions; import static org.assertj.core.api.Assertions.assertThat; @@ -77,7 +75,7 @@ public class DefaultSensorStorageTest { private DefaultFileSystem fs; private DefaultSensorStorage sensorStorage; private Settings settings; - private ResourcePerspectives resourcePerspectives; + private ModuleIssues moduleIssues; private Project project; private DefaultIndex sonarIndex; @@ -91,15 +89,14 @@ public class DefaultSensorStorageTest { when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC); when(metricFinder.findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION); settings = new Settings(); - resourcePerspectives = mock(ResourcePerspectives.class); - ComponentDataCache componentDataCache = mock(ComponentDataCache.class); + moduleIssues = mock(ModuleIssues.class); project = new Project("myProject"); sonarIndex = mock(DefaultIndex.class); CoverageExclusions coverageExclusions = mock(CoverageExclusions.class); when(coverageExclusions.accept(any(Resource.class), any(Measure.class))).thenReturn(true); resourceCache = new ResourceCache(); sensorStorage = new DefaultSensorStorage(metricFinder, project, - resourcePerspectives, settings, fs, activeRules, componentDataCache, mock(DuplicationCache.class), sonarIndex, coverageExclusions, resourceCache); + moduleIssues, settings, fs, activeRules, mock(DuplicationCache.class), sonarIndex, coverageExclusions, resourceCache, mock(ReportPublisher.class)); } @Test @@ -178,12 +175,7 @@ public class DefaultSensorStorageTest { public void shouldAddIssueOnFile() { InputFile file = new DefaultInputFile("foo", "src/Foo.php").setLines(4); - ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class); - - Issuable issuable = mock(Issuable.class); - when(resourcePerspectives.as(Issuable.class, File.create("src/Foo.php"))).thenReturn(issuable); - - when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true); + ArgumentCaptor<org.sonar.api.issue.internal.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.issue.internal.DefaultIssue.class); sensorStorage.store(new DefaultIssue() .onFile(file) @@ -192,7 +184,9 @@ public class DefaultSensorStorageTest { .atLine(3) .effortToFix(10.0)); - Issue issue = argumentCaptor.getValue(); + verify(moduleIssues).initAndAddIssue(argumentCaptor.capture()); + + org.sonar.api.issue.internal.DefaultIssue issue = argumentCaptor.getValue(); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar")); assertThat(issue.message()).isEqualTo("Foo"); assertThat(issue.line()).isEqualTo(3); @@ -204,12 +198,7 @@ public class DefaultSensorStorageTest { public void shouldAddIssueOnDirectory() { InputDir dir = new DefaultInputDir("foo", "src"); - ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class); - - Issuable issuable = mock(Issuable.class); - when(resourcePerspectives.as(Issuable.class, Directory.create("src"))).thenReturn(issuable); - - when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true); + ArgumentCaptor<org.sonar.api.issue.internal.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.issue.internal.DefaultIssue.class); sensorStorage.store(new DefaultIssue() .onDir(dir) @@ -217,7 +206,9 @@ public class DefaultSensorStorageTest { .message("Foo") .effortToFix(10.0)); - Issue issue = argumentCaptor.getValue(); + verify(moduleIssues).initAndAddIssue(argumentCaptor.capture()); + + org.sonar.api.issue.internal.DefaultIssue issue = argumentCaptor.getValue(); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar")); assertThat(issue.message()).isEqualTo("Foo"); assertThat(issue.line()).isNull(); @@ -227,12 +218,7 @@ public class DefaultSensorStorageTest { @Test public void shouldAddIssueOnProject() { - ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class); - - Issuable issuable = mock(Issuable.class); - when(resourcePerspectives.as(Issuable.class, (Resource) project)).thenReturn(issuable); - - when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true); + ArgumentCaptor<org.sonar.api.issue.internal.DefaultIssue> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.issue.internal.DefaultIssue.class); sensorStorage.store(new DefaultIssue() .onProject() @@ -241,7 +227,9 @@ public class DefaultSensorStorageTest { .overrideSeverity(Severity.BLOCKER) .effortToFix(10.0)); - Issue issue = argumentCaptor.getValue(); + verify(moduleIssues).initAndAddIssue(argumentCaptor.capture()); + + org.sonar.api.issue.internal.DefaultIssue issue = argumentCaptor.getValue(); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar")); assertThat(issue.message()).isEqualTo("Foo"); assertThat(issue.line()).isNull(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/CodeColorizersTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/CodeColorizersTest.java index a7486ad376b..a6568661633 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/CodeColorizersTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/CodeColorizersTest.java @@ -25,38 +25,45 @@ import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.sensor.highlighting.NewHighlighting; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.web.CodeColorizerFormat; -import org.sonar.batch.highlighting.SyntaxHighlightingData; -import org.sonar.colorizer.CDocTokenizer; -import org.sonar.colorizer.CppDocTokenizer; -import org.sonar.colorizer.JavadocTokenizer; -import org.sonar.colorizer.KeywordsTokenizer; -import org.sonar.colorizer.StringTokenizer; -import org.sonar.colorizer.Tokenizer; +import org.sonar.colorizer.*; import java.io.File; import java.util.Arrays; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class CodeColorizersTest { - private static final String HIGHLIGHTING_JS = "0,4,cppd;5,11,cppd;12,15,cppd;16,19,k;29,37,k;65,69,k;85,93,cd;98,102,k;112,114,s;120,124,k"; - private static final String HIGHLIGHTING_JAVA = "0,4,j;5,11,j;12,15,j;16,22,k;23,28,k;43,50,k;51,54,k;67,78,a;81,87,k;88,92,k;97,100,k;142,146,k;162,170,cd"; @Rule public TemporaryFolder temp = new TemporaryFolder(); @Test public void testConvertToHighlighting() throws Exception { CodeColorizers codeColorizers = new CodeColorizers(Arrays.<CodeColorizerFormat>asList(new JavaScriptColorizerFormat())); - File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI()); + NewHighlighting highlighting = mock(NewHighlighting.class); - SyntaxHighlightingData syntaxHighlighting = codeColorizers.toSyntaxHighlighting(jsFile, Charsets.UTF_8, "js"); + codeColorizers.toSyntaxHighlighting(jsFile, Charsets.UTF_8, "js", highlighting); - assertThat(syntaxHighlighting.writeString()).isEqualTo(HIGHLIGHTING_JS); + verifyForJs(highlighting); + } + private void verifyForJs(NewHighlighting highlighting) { + verify(highlighting).highlight(0, 4, TypeOfText.CPP_DOC); + verify(highlighting).highlight(5, 11, TypeOfText.CPP_DOC); + verify(highlighting).highlight(12, 15, TypeOfText.CPP_DOC); + verify(highlighting).highlight(16, 19, TypeOfText.KEYWORD); + verify(highlighting).highlight(29, 37, TypeOfText.KEYWORD); + verify(highlighting).highlight(65, 69, TypeOfText.KEYWORD); + verify(highlighting).highlight(85, 93, TypeOfText.COMMENT); + verify(highlighting).highlight(98, 102, TypeOfText.KEYWORD); + verify(highlighting).highlight(112, 114, TypeOfText.STRING); + verify(highlighting).highlight(120, 124, TypeOfText.KEYWORD); } @Test @@ -68,9 +75,10 @@ public class CodeColorizersTest { File jsFile = new File(this.getClass().getResource("CodeColorizersTest/Person.js").toURI()); FileUtils.write(fileWithBom, FileUtils.readFileToString(jsFile), "UTF-8", true); - SyntaxHighlightingData syntaxHighlighting = codeColorizers.toSyntaxHighlighting(fileWithBom, Charsets.UTF_8, "js"); + NewHighlighting highlighting = mock(NewHighlighting.class); + codeColorizers.toSyntaxHighlighting(fileWithBom, Charsets.UTF_8, "js", highlighting); - assertThat(syntaxHighlighting.writeString()).isEqualTo(HIGHLIGHTING_JS); + verifyForJs(highlighting); } @Test @@ -79,9 +87,22 @@ public class CodeColorizersTest { File javaFile = new File(this.getClass().getResource("CodeColorizersTest/Person.java").toURI()); - SyntaxHighlightingData syntaxHighlighting = codeColorizers.toSyntaxHighlighting(javaFile, Charsets.UTF_8, "java"); - - assertThat(syntaxHighlighting.writeString()).isEqualTo(HIGHLIGHTING_JAVA); + NewHighlighting highlighting = mock(NewHighlighting.class); + codeColorizers.toSyntaxHighlighting(javaFile, Charsets.UTF_8, "java", highlighting); + + verify(highlighting).highlight(0, 4, TypeOfText.STRUCTURED_COMMENT); + verify(highlighting).highlight(5, 11, TypeOfText.STRUCTURED_COMMENT); + verify(highlighting).highlight(12, 15, TypeOfText.STRUCTURED_COMMENT); + verify(highlighting).highlight(16, 22, TypeOfText.KEYWORD); + verify(highlighting).highlight(23, 28, TypeOfText.KEYWORD); + verify(highlighting).highlight(43, 50, TypeOfText.KEYWORD); + verify(highlighting).highlight(51, 54, TypeOfText.KEYWORD); + verify(highlighting).highlight(67, 78, TypeOfText.ANNOTATION); + verify(highlighting).highlight(81, 87, TypeOfText.KEYWORD); + verify(highlighting).highlight(88, 92, TypeOfText.KEYWORD); + verify(highlighting).highlight(97, 100, TypeOfText.KEYWORD); + verify(highlighting).highlight(142, 146, TypeOfText.KEYWORD); + verify(highlighting).highlight(162, 170, TypeOfText.COMMENT); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java index a14e4fe91d1..7b3d3987bf3 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultHighlightableTest.java @@ -23,16 +23,16 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; -import org.sonar.api.component.Component; -import org.sonar.batch.highlighting.SyntaxHighlightingData; -import org.sonar.batch.index.ComponentDataCache; -import org.sonar.core.source.SnapshotDataTypes; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; +import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.internal.SensorStorage; + +import java.io.StringReader; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; public class DefaultHighlightableTest { @@ -41,27 +41,15 @@ public class DefaultHighlightableTest { @Test public void should_store_highlighting_rules() throws Exception { - DefaultHighlightable highlightablePerspective = new DefaultHighlightable(mock(Component.class), null); - highlightablePerspective.newHighlighting().highlight(0, 10, "k").highlight(20, 30, "cppd"); - - assertThat(highlightablePerspective.getHighlightingRules().getSyntaxHighlightingRuleSet()).hasSize(2); + SensorStorage sensorStorage = mock(SensorStorage.class); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php") + .initMetadata(new FileMetadata().readMetadata(new StringReader("azerty\nbla bla"))); + DefaultHighlightable highlightablePerspective = new DefaultHighlightable(inputFile, sensorStorage); + highlightablePerspective.newHighlighting().highlight(0, 6, "k").highlight(7, 10, "cppd").done(); + + ArgumentCaptor<DefaultHighlighting> argCaptor = ArgumentCaptor.forClass(DefaultHighlighting.class); + verify(sensorStorage).store(argCaptor.capture()); + assertThat(argCaptor.getValue().getSyntaxHighlightingRuleSet()).hasSize(2); } - @Test - public void should_apply_registered_highlighting() throws Exception { - Component component = mock(Component.class); - when(component.key()).thenReturn("myComponent"); - - ComponentDataCache cache = mock(ComponentDataCache.class); - - DefaultHighlightable highlightable = new DefaultHighlightable(component, cache); - highlightable.newHighlighting() - .highlight(0, 10, "k") - .highlight(20, 30, "cppd") - .done(); - - ArgumentCaptor<SyntaxHighlightingData> argCaptor = ArgumentCaptor.forClass(SyntaxHighlightingData.class); - verify(cache).setData(eq("myComponent"), eq(SnapshotDataTypes.SYNTAX_HIGHLIGHTING), argCaptor.capture()); - assertThat(argCaptor.getValue().writeString()).isEqualTo("0,10,k;20,30,cppd"); - } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java index c23fcd2831d..a0489a7c209 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolTableTest.java @@ -20,22 +20,36 @@ package org.sonar.batch.source; +import com.google.common.base.Strings; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.source.Symbol; import org.sonar.api.source.Symbolizable; +import java.io.StringReader; + import static org.assertj.core.api.Assertions.assertThat; public class DefaultSymbolTableTest { @Rule public ExpectedException throwable = ExpectedException.none(); + private DefaultInputFile inputFile; + + @Before + public void prepare() { + inputFile = new DefaultInputFile("foo", "src/Foo.php") + .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20)))); + } @Test public void should_order_symbol_and_references() throws Exception { - Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo"); + + Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20); symbolTableBuilder.newReference(firstSymbol, 32); Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92); @@ -45,25 +59,22 @@ public class DefaultSymbolTableTest { Symbolizable.SymbolTable symbolTable = symbolTableBuilder.build(); assertThat(symbolTable.symbols()).containsExactly(firstSymbol, secondSymbol, thirdSymbol); - assertThat(symbolTable.references(firstSymbol)).containsExactly(32); - assertThat(symbolTable.references(secondSymbol)).containsExactly(124); - assertThat(symbolTable.references(thirdSymbol)).containsExactly(70); } @Test public void should_reject_reference_conflicting_with_declaration() throws Exception { throwable.expect(UnsupportedOperationException.class); - Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo"); + Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); Symbol symbol = symbolTableBuilder.newSymbol(10, 20); symbolTableBuilder.newReference(symbol, 15); } @Test public void test_toString() throws Exception { - Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder("foo"); + Symbolizable.SymbolTableBuilder symbolTableBuilder = new DefaultSymbolTable.Builder(inputFile); Symbol symbol = symbolTableBuilder.newSymbol(10, 20); - assertThat(symbol.toString()).isEqualTo("Symbol{offset=10-20}"); + assertThat(symbol.toString()).isEqualTo("Symbol{range=Range[from [line=2, lineOffset=3] to [line=3, lineOffset=6]]}"); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java index 940c47ed46e..d3b1018ace5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/DefaultSymbolizableTest.java @@ -20,31 +20,33 @@ package org.sonar.batch.source; +import com.google.common.base.Strings; import org.junit.Test; -import org.sonar.api.component.Component; +import org.mockito.ArgumentCaptor; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.source.Symbol; import org.sonar.api.source.Symbolizable; -import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.symbol.SymbolData; -import org.sonar.core.source.SnapshotDataTypes; +import org.sonar.batch.sensor.DefaultSensorStorage; -import static org.mockito.Matchers.any; +import java.io.StringReader; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; public class DefaultSymbolizableTest { @Test public void should_update_cache_when_done() throws Exception { - Component component = mock(Component.class); - when(component.key()).thenReturn("myComponent"); - - ComponentDataCache cache = mock(ComponentDataCache.class); + DefaultSensorStorage sensorStorage = mock(DefaultSensorStorage.class); + DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php") + .initMetadata(new FileMetadata().readMetadata(new StringReader(Strings.repeat("azerty\n", 20)))); - DefaultSymbolizable symbolPerspective = new DefaultSymbolizable(cache, component); + DefaultSymbolizable symbolPerspective = new DefaultSymbolizable(inputFile, sensorStorage); Symbolizable.SymbolTableBuilder symbolTableBuilder = symbolPerspective.newSymbolTableBuilder(); Symbol firstSymbol = symbolTableBuilder.newSymbol(4, 8); symbolTableBuilder.newReference(firstSymbol, 12); @@ -57,6 +59,9 @@ public class DefaultSymbolizableTest { symbolPerspective.setSymbolTable(symbolTable); - verify(cache).setData(eq("myComponent"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), any(SymbolData.class)); + ArgumentCaptor<Map> argCaptor = ArgumentCaptor.forClass(Map.class); + verify(sensorStorage).store(eq(inputFile), argCaptor.capture()); + // Map<Symbol, Set<TextRange>> + assertThat(argCaptor.getValue().keySet()).hasSize(2); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java index e2adf2dd0db..893957a80a5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/HighlightableBuilderTest.java @@ -20,38 +20,42 @@ package org.sonar.batch.source; import org.junit.Test; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.component.Component; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.source.Highlightable; -import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.component.ResourceComponent; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class HighlightableBuilderTest { - ComponentDataCache cache = mock(ComponentDataCache.class); - @Test public void should_load_default_perspective() throws Exception { Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); Component component = new ResourceComponent(file); - HighlightableBuilder builder = new HighlightableBuilder(cache); + ResourceCache resourceCache = mock(ResourceCache.class); + when(resourceCache.get(file.getEffectiveKey())).thenReturn(new BatchResource(1, file, null).setInputPath(new DefaultInputFile("myproject", "path/to/foo.c"))); + HighlightableBuilder builder = new HighlightableBuilder(resourceCache, mock(SensorStorage.class)); Highlightable perspective = builder.loadPerspective(Highlightable.class, component); assertThat(perspective).isNotNull().isInstanceOf(DefaultHighlightable.class); - assertThat(perspective.component()).isSameAs(component); + assertThat(perspective.component().key()).isEqualTo(component.key()); } @Test public void project_should_not_be_highlightable() { Component component = new ResourceComponent(new Project("struts").setEffectiveKey("org.struts")); - HighlightableBuilder builder = new HighlightableBuilder(cache); + HighlightableBuilder builder = new HighlightableBuilder(mock(ResourceCache.class), mock(SensorStorage.class)); Highlightable perspective = builder.loadPerspective(Highlightable.class, component); assertThat(perspective).isNull(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java index f2138c9a721..16c4fe669a7 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/source/SymbolizableBuilderTest.java @@ -21,26 +21,46 @@ package org.sonar.batch.source; import org.junit.Test; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.component.Component; import org.sonar.api.component.Perspective; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; import org.sonar.api.source.Symbolizable; -import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.sensor.DefaultSensorStorage; +import org.sonar.core.component.ResourceComponent; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class SymbolizableBuilderTest { - ComponentDataCache dataCache = mock(ComponentDataCache.class); - @Test public void should_load_perspective() throws Exception { - Component component = mock(Component.class); + Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); + Component component = new ResourceComponent(file); + + ResourceCache resourceCache = mock(ResourceCache.class); + when(resourceCache.get(file.getEffectiveKey())).thenReturn(new BatchResource(1, file, null).setInputPath(new DefaultInputFile("myproject", "path/to/foo.c"))); - SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(dataCache); + SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(resourceCache, mock(DefaultSensorStorage.class)); Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, component); assertThat(perspective).isInstanceOf(Symbolizable.class); - assertThat(perspective.component()).isEqualTo(component); + assertThat(perspective.component().key()).isEqualTo(component.key()); + } + + @Test + public void project_should_not_be_highlightable() { + Component component = new ResourceComponent(new Project("struts").setEffectiveKey("org.struts")); + + SymbolizableBuilder builder = new SymbolizableBuilder(mock(ResourceCache.class), mock(DefaultSensorStorage.class)); + Perspective perspective = builder.loadPerspective(Symbolizable.class, component); + + assertThat(perspective).isNull(); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java b/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java deleted file mode 100644 index 05961573725..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/symbol/DefaultSymbolTableBuilderTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.symbol; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.mockito.ArgumentCaptor; -import org.sonar.api.source.Symbol; -import org.sonar.batch.index.ComponentDataCache; -import org.sonar.core.source.SnapshotDataTypes; - -import java.util.ArrayList; -import java.util.Map; -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class DefaultSymbolTableBuilderTest { - - @Rule - public ExpectedException throwable = ExpectedException.none(); - - @Test - public void should_write_symbol_and_references() throws Exception { - ComponentDataCache componentDataCache = mock(ComponentDataCache.class); - DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache); - Symbol firstSymbol = symbolTableBuilder.newSymbol(10, 20); - symbolTableBuilder.newReference(firstSymbol, 32); - Symbol secondSymbol = symbolTableBuilder.newSymbol(84, 92); - symbolTableBuilder.newReference(secondSymbol, 124); - Symbol thirdSymbol = symbolTableBuilder.newSymbol(55, 62); - symbolTableBuilder.newReference(thirdSymbol, 70); - symbolTableBuilder.done(); - - ArgumentCaptor<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class); - verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture()); - - Map<org.sonar.api.source.Symbol, Set<Integer>> referencesBySymbol = argCaptor.getValue().referencesBySymbol(); - - assertThat(new ArrayList<Symbol>(referencesBySymbol.keySet())).containsExactly(firstSymbol, secondSymbol, thirdSymbol); - assertThat(new ArrayList<Integer>(referencesBySymbol.get(firstSymbol))).containsExactly(32); - assertThat(new ArrayList<Integer>(referencesBySymbol.get(secondSymbol))).containsExactly(124); - assertThat(new ArrayList<Integer>(referencesBySymbol.get(thirdSymbol))).containsExactly(70); - - assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10,32;84,92,84,124;55,62,55,70"); - } - - @Test - public void should_serialize_unused_symbol() throws Exception { - - ComponentDataCache componentDataCache = mock(ComponentDataCache.class); - DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache); - symbolTableBuilder.newSymbol(10, 20); - symbolTableBuilder.done(); - - ArgumentCaptor<SymbolData> argCaptor = ArgumentCaptor.forClass(SymbolData.class); - verify(componentDataCache).setData(eq("foo"), eq(SnapshotDataTypes.SYMBOL_HIGHLIGHTING), argCaptor.capture()); - - assertThat(argCaptor.getValue().writeString()).isEqualTo("10,20,10"); - } - - @Test - public void should_reject_reference_conflicting_with_declaration() throws Exception { - throwable.expect(UnsupportedOperationException.class); - - ComponentDataCache componentDataCache = mock(ComponentDataCache.class); - DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache); - Symbol symbol = symbolTableBuilder.newSymbol(10, 20); - symbolTableBuilder.newReference(symbol, 15); - } - - @Test - public void should_reject_reference_from_another_file() throws Exception { - throwable.expect(UnsupportedOperationException.class); - - ComponentDataCache componentDataCache = mock(ComponentDataCache.class); - DefaultSymbolTableBuilder symbolTableBuilder = new DefaultSymbolTableBuilder("foo", componentDataCache); - Symbol symbol = symbolTableBuilder.newSymbol(10, 20); - - DefaultSymbolTableBuilder symbolTableBuilder2 = new DefaultSymbolTableBuilder("foo2", componentDataCache); - Symbol symbol2 = symbolTableBuilder2.newSymbol(30, 40); - - symbolTableBuilder.newReference(symbol2, 15); - } - -} diff --git a/sonar-core/src/main/java/org/sonar/core/component/GraphPerspectiveBuilder.java b/sonar-core/src/main/java/org/sonar/core/component/GraphPerspectiveBuilder.java index df2199c7103..5668a840d69 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/GraphPerspectiveBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/component/GraphPerspectiveBuilder.java @@ -30,7 +30,7 @@ public abstract class GraphPerspectiveBuilder<T extends Perspective> extends Per protected final GraphPerspectiveLoader<T> perspectiveLoader; protected GraphPerspectiveBuilder(ScanGraph graph, Class<T> perspectiveClass, EdgePath path, - GraphPerspectiveLoader<T> perspectiveLoader) { + GraphPerspectiveLoader<T> perspectiveLoader) { super(perspectiveClass); this.graph = graph; this.path = path; @@ -39,7 +39,7 @@ public abstract class GraphPerspectiveBuilder<T extends Perspective> extends Per public T create(ComponentVertex component) { return (T) component.beanGraph().createAdjacentVertex(component, perspectiveLoader.getBeanClass(), - perspectiveLoader.getPerspectiveKey()); + perspectiveLoader.getPerspectiveKey()); } public EdgePath path() { @@ -51,7 +51,7 @@ public abstract class GraphPerspectiveBuilder<T extends Perspective> extends Per } @Override - protected T loadPerspective(Class<T> perspectiveClass, Component component) { + public T loadPerspective(Class<T> perspectiveClass, Component component) { ComponentVertex vertex; if (component instanceof ComponentVertex) { vertex = (ComponentVertex) component; diff --git a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java index 5b69ebaf05d..31f9c323d61 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java +++ b/sonar-core/src/main/java/org/sonar/core/component/PerspectiveBuilder.java @@ -34,10 +34,10 @@ public abstract class PerspectiveBuilder<T extends Perspective> implements Batch this.perspectiveClass = perspectiveClass; } - protected Class<T> getPerspectiveClass() { + public Class<T> getPerspectiveClass() { return perspectiveClass; } @CheckForNull - protected abstract T loadPerspective(Class<T> perspectiveClass, Component component); + public abstract T loadPerspective(Class<T> perspectiveClass, Component component); } diff --git a/sonar-core/src/test/java/org/sonar/core/component/PerspectiveBuilderTest.java b/sonar-core/src/test/java/org/sonar/core/component/PerspectiveBuilderTest.java index 4275f2c63d9..d3bb10520d5 100644 --- a/sonar-core/src/test/java/org/sonar/core/component/PerspectiveBuilderTest.java +++ b/sonar-core/src/test/java/org/sonar/core/component/PerspectiveBuilderTest.java @@ -30,7 +30,7 @@ public class PerspectiveBuilderTest { public void testGetPerspectiveClass() throws Exception { PerspectiveBuilder<FakePerspective> builder = new PerspectiveBuilder<FakePerspective>(FakePerspective.class) { @Override - protected FakePerspective loadPerspective(Class<FakePerspective> perspectiveClass, Component component) { + public FakePerspective loadPerspective(Class<FakePerspective> perspectiveClass, Component component) { return null; } }; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java index 768986d8c0c..4e0af79ccc7 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java @@ -19,6 +19,8 @@ */ package org.sonar.api.batch.fs; +import org.sonar.api.batch.fs.internal.DefaultInputFile; + import javax.annotation.CheckForNull; import java.io.File; @@ -26,6 +28,14 @@ import java.nio.file.Path; /** * This layer over {@link java.io.File} adds information for code analyzers. + * For unit testing purpose you can create some {@link DefaultInputFile} and initialize + * all fields using + * + * <pre> + * new DefaultInputFile("moduleKey", "relative/path/from/module/baseDir.java") + * .setModuleBaseDir(path) + * .initMetadata(new FileMetadata().readMetadata(someReader)); + * </pre> * * @since 4.2 */ @@ -100,4 +110,20 @@ public interface InputFile extends InputPath { */ int lines(); + /** + * Return a {@link TextPointer} in the given file. + * @param line Line of the pointer. Start at 1. + * @param lineOffset Offset in the line. Start at 0. + * @throw {@link IllegalArgumentException} if line or offset is not valid for the given file. + */ + TextPointer newPointer(int line, int lineOffset); + + /** + * Return a {@link TextRange} in the given file. + * @param start + * @param end + * @throw {@link IllegalArgumentException} if start or stop pointers are not valid for the given file. + */ + TextRange newRange(TextPointer start, TextPointer end); + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/symbol/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextPointer.java index 2ac5be1323d..63f49b7d407 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/symbol/package-info.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextPointer.java @@ -17,5 +17,23 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -@javax.annotation.ParametersAreNonnullByDefault -package org.sonar.batch.symbol; +package org.sonar.api.batch.fs; + +/** + * Represents a position in a text file {@link InputFile} + * + * @since 5.2 + */ +public interface TextPointer extends Comparable<TextPointer> { + + /** + * The logical line where this pointer is located. First line is 1. + */ + int line(); + + /** + * The offset of this pointer in the current line. First position in a line is 0. + */ + int lineOffset(); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextRange.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextRange.java new file mode 100644 index 00000000000..08239a1e0cd --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/TextRange.java @@ -0,0 +1,46 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +/** + * Represents a text range in an {@link InputFile} + * + * @since 5.2 + */ +public interface TextRange { + + /** + * Start position of the range + */ + TextPointer start(); + + /** + * End position of the range + */ + TextPointer end(); + + /** + * Test if the current range has some common area with another range. + * Exemple: say the two ranges are on same line. Range with offsets [1,3] overlaps range with offsets [2,4] but not + * range with offset [3,5] + */ + boolean overlap(TextRange another); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java index a24060a5df1..83b4473fbea 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java @@ -19,7 +19,11 @@ */ package org.sonar.api.batch.fs.internal; +import com.google.common.base.Preconditions; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextPointer; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.FileMetadata.Metadata; import org.sonar.api.utils.PathUtils; import javax.annotation.CheckForNull; @@ -28,6 +32,7 @@ import javax.annotation.Nullable; import java.io.File; import java.nio.charset.Charset; import java.nio.file.Path; +import java.util.Arrays; /** * @since 4.2 @@ -40,9 +45,13 @@ public class DefaultInputFile implements InputFile { private String language; private Type type = Type.MAIN; private Status status; - private int lines; + private int lines = -1; private Charset charset; - private int lastValidOffset; + private int lastValidOffset = -1; + private String hash; + private int nonBlankLines; + private int[] originalLineOffsets; + private boolean empty; public DefaultInputFile(String moduleKey, String relativePath) { this.moduleKey = moduleKey; @@ -145,6 +154,7 @@ public class DefaultInputFile implements InputFile { } public int lastValidOffset() { + Preconditions.checkState(lastValidOffset >= 0, "InputFile is not properly initialized. Please set 'lastValidOffset' property."); return lastValidOffset; } @@ -153,6 +163,108 @@ public class DefaultInputFile implements InputFile { return this; } + /** + * Digest hash of the file. + */ + public String hash() { + return hash; + } + + public int nonBlankLines() { + return nonBlankLines; + } + + public int[] originalLineOffsets() { + Preconditions.checkState(originalLineOffsets != null, "InputFile is not properly initialized. Please set 'originalLineOffsets' property."); + Preconditions.checkState(originalLineOffsets.length == lines, "InputFile is not properly initialized. 'originalLineOffsets' property length should be equal to 'lines'"); + return originalLineOffsets; + } + + public DefaultInputFile setHash(String hash) { + this.hash = hash; + return this; + } + + public DefaultInputFile setNonBlankLines(int nonBlankLines) { + this.nonBlankLines = nonBlankLines; + return this; + } + + public DefaultInputFile setOriginalLineOffsets(int[] originalLineOffsets) { + this.originalLineOffsets = originalLineOffsets; + return this; + } + + public boolean isEmpty() { + return this.empty; + } + + public DefaultInputFile setEmpty(boolean empty) { + this.empty = empty; + return this; + } + + @Override + public TextPointer newPointer(int line, int lineOffset) { + DefaultTextPointer textPointer = new DefaultTextPointer(line, lineOffset); + checkValid(textPointer, "pointer"); + return textPointer; + } + + private void checkValid(TextPointer pointer, String owner) { + Preconditions.checkArgument(pointer.line() >= 1, "%s is not a valid line for a file", pointer.line()); + Preconditions.checkArgument(pointer.line() <= this.lines, "%s is not a valid line for %s. File %s has %s line(s)", pointer.line(), owner, this, lines); + Preconditions.checkArgument(pointer.lineOffset() >= 0, "%s is not a valid line offset for a file", pointer.lineOffset()); + int lineLength = lineLength(pointer.line()); + Preconditions.checkArgument(pointer.lineOffset() <= lineLength, + "%s is not a valid line offset for %s. File %s has %s character(s) at line %s", pointer.lineOffset(), owner, this, lineLength, pointer.line()); + } + + private int lineLength(int line) { + return lastValidGlobalOffsetForLine(line) - originalLineOffsets()[line - 1]; + } + + private int lastValidGlobalOffsetForLine(int line) { + return line < this.lines ? (originalLineOffsets()[line] - 1) : lastValidOffset(); + } + + @Override + public TextRange newRange(TextPointer start, TextPointer end) { + checkValid(start, "start pointer"); + checkValid(end, "end pointer"); + Preconditions.checkArgument(start.compareTo(end) < 0, "Start pointer %s should be before end pointer %s", start, end); + return new DefaultTextRange(start, end); + } + + /** + * Create Range from global offsets. Used for backward compatibility with older API. + */ + public TextRange newRange(int startOffset, int endOffset) { + return newRange(newPointer(startOffset), newPointer(endOffset)); + } + + public TextPointer newPointer(int globalOffset) { + Preconditions.checkArgument(globalOffset >= 0, "%s is not a valid offset for a file", globalOffset); + Preconditions.checkArgument(globalOffset <= lastValidOffset(), "%s is not a valid offset for file %s. Max offset is %s", globalOffset, this, lastValidOffset()); + int line = findLine(globalOffset); + int startLineOffset = originalLineOffsets()[line - 1]; + return new DefaultTextPointer(line, globalOffset - startLineOffset); + } + + private int findLine(int globalOffset) { + return Math.abs(Arrays.binarySearch(originalLineOffsets(), globalOffset) + 1); + } + + public DefaultInputFile initMetadata(Metadata metadata) { + this.setLines(metadata.lines); + this.setLastValidOffset(metadata.lastValidOffset); + this.setNonBlankLines(metadata.nonBlankLines); + this.setHash(metadata.hash); + this.setOriginalLineOffsets(metadata.originalLineOffsets); + this.setEmpty(metadata.empty); + return this; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextPointer.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextPointer.java new file mode 100644 index 00000000000..572aa4cb838 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextPointer.java @@ -0,0 +1,74 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal; + +import org.sonar.api.batch.fs.TextPointer; + +/** + * @since 5.2 + */ +public class DefaultTextPointer implements TextPointer { + + private final int line; + private final int lineOffset; + + public DefaultTextPointer(int line, int lineOffset) { + this.line = line; + this.lineOffset = lineOffset; + } + + @Override + public int line() { + return line; + } + + @Override + public int lineOffset() { + return lineOffset; + } + + @Override + public String toString() { + return "[line=" + line + ", lineOffset=" + lineOffset + "]"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DefaultTextPointer)) { + return false; + } + DefaultTextPointer other = (DefaultTextPointer) obj; + return other.line == this.line && other.lineOffset == this.lineOffset; + } + + @Override + public int hashCode() { + return 37 * this.line + lineOffset; + } + + @Override + public int compareTo(TextPointer o) { + if (this.line == o.line()) { + return Integer.compare(this.lineOffset, o.lineOffset()); + } + return Integer.compare(this.line, o.line()); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextRange.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextRange.java new file mode 100644 index 00000000000..b976d1656bc --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultTextRange.java @@ -0,0 +1,74 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal; + +import org.sonar.api.batch.fs.TextPointer; +import org.sonar.api.batch.fs.TextRange; + +/** + * @since 5.2 + */ +public class DefaultTextRange implements TextRange { + + private final TextPointer start; + private final TextPointer end; + + public DefaultTextRange(TextPointer start, TextPointer end) { + this.start = start; + this.end = end; + } + + @Override + public TextPointer start() { + return start; + } + + @Override + public TextPointer end() { + return end; + } + + @Override + public boolean overlap(TextRange another) { + // [A,B] and [C,D] + // B > C && D > A + return this.end.compareTo(another.start()) > 0 && another.end().compareTo(this.start) > 0; + } + + @Override + public String toString() { + return "Range[from " + start + " to " + end + "]"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DefaultTextRange)) { + return false; + } + DefaultTextRange other = (DefaultTextRange) obj; + return start.equals(other.start) && end.equals(other.end); + } + + @Override + public int hashCode() { + return start.hashCode() * 17 + end.hashCode(); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileMetadata.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java index 05161741c13..bb357542239 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileMetadata.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/FileMetadata.java @@ -17,7 +17,7 @@ * 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.batch.scan.filesystem; +package org.sonar.api.batch.fs.internal; import com.google.common.base.Charsets; import com.google.common.primitives.Ints; @@ -27,8 +27,6 @@ import org.apache.commons.io.ByteOrderMark; import org.apache.commons.io.input.BOMInputStream; import org.sonar.api.BatchComponent; import org.sonar.api.CoreProperties; -import org.sonar.api.batch.AnalysisMode; -import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -51,11 +49,6 @@ public class FileMetadata implements BatchComponent { private static final char LINE_FEED = '\n'; private static final char CARRIAGE_RETURN = '\r'; - private final AnalysisMode analysisMode; - - public FileMetadata(AnalysisMode analysisMode) { - this.analysisMode = analysisMode; - } private abstract static class CharHandler { @@ -90,8 +83,8 @@ public class FileMetadata implements BatchComponent { void handleAll(char c) { this.empty = false; if (!alreadyLoggedInvalidCharacter && c == '\ufffd') { - LOG.warn("Invalid character encountered in file " + file + " at line " + lines - + " for encoding " + encoding + ". Please fix file content or configure the encoding to be used using property '" + CoreProperties.ENCODING_PROPERTY + "'."); + LOG.warn("Invalid character encountered in file {} at line {} for encoding {}. Please fix file content or configure the encoding to be used using property '{}'.", file, + lines, encoding, CoreProperties.ENCODING_PROPERTY); alreadyLoggedInvalidCharacter = true; } } @@ -230,66 +223,82 @@ public class FileMetadata implements BatchComponent { * Compute hash of a file ignoring line ends differences. * Maximum performance is needed. */ - Metadata read(File file, Charset encoding) { + public Metadata readMetadata(File file, Charset encoding) { LineCounter lineCounter = new LineCounter(file, encoding); FileHashComputer fileHashComputer = new FileHashComputer(); LineOffsetCounter lineOffsetCounter = new LineOffsetCounter(); - if (!analysisMode.isPreview()) { - scanFile(file, encoding, lineCounter, fileHashComputer, lineOffsetCounter); - } else { - // No need to compute line offsets in preview mode since there is no syntax highlighting - scanFile(file, encoding, lineCounter, fileHashComputer); + readFile(file, encoding, lineCounter, fileHashComputer, lineOffsetCounter); + return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineOffsets(), + lineOffsetCounter.getLastValidOffset(), + lineCounter.isEmpty()); + } + + /** + * For testing purpose + */ + public Metadata readMetadata(Reader reader) { + LineCounter lineCounter = new LineCounter(new File("fromString"), Charsets.UTF_16); + FileHashComputer fileHashComputer = new FileHashComputer(); + LineOffsetCounter lineOffsetCounter = new LineOffsetCounter(); + try { + read(reader, lineCounter, fileHashComputer, lineOffsetCounter); + } catch (IOException e) { + throw new IllegalStateException("Should never occurs", e); } return new Metadata(lineCounter.lines(), lineCounter.nonBlankLines(), fileHashComputer.getHash(), lineOffsetCounter.getOriginalLineOffsets(), lineOffsetCounter.getLastValidOffset(), lineCounter.isEmpty()); } - private static void scanFile(File file, Charset encoding, CharHandler... handlers) { - char c = (char) 0; + private static void readFile(File file, Charset encoding, CharHandler... handlers) { try (BOMInputStream bomIn = new BOMInputStream(new FileInputStream(file), ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE); Reader reader = new BufferedReader(new InputStreamReader(bomIn, encoding))) { - int i = reader.read(); - boolean afterCR = false; - while (i != -1) { - c = (char) i; - if (afterCR) { - for (CharHandler handler : handlers) { - if (c != CARRIAGE_RETURN && c != LINE_FEED) { - handler.handleIgnoreEoL(c); - } - handler.handleAll(c); - handler.newLine(); - } - afterCR = c == CARRIAGE_RETURN; - } else if (c == LINE_FEED) { - for (CharHandler handler : handlers) { - handler.handleAll(c); - handler.newLine(); - } - } else if (c == CARRIAGE_RETURN) { - afterCR = true; - for (CharHandler handler : handlers) { - handler.handleAll(c); - } - } else { - for (CharHandler handler : handlers) { + read(reader, handlers); + } catch (IOException e) { + throw new IllegalStateException(String.format("Fail to read file '%s' with encoding '%s'", file.getAbsolutePath(), encoding), e); + } + } + + private static void read(Reader reader, CharHandler... handlers) throws IOException { + char c = (char) 0; + int i = reader.read(); + boolean afterCR = false; + while (i != -1) { + c = (char) i; + if (afterCR) { + for (CharHandler handler : handlers) { + if (c != CARRIAGE_RETURN && c != LINE_FEED) { handler.handleIgnoreEoL(c); - handler.handleAll(c); } + handler.handleAll(c); + handler.newLine(); + } + afterCR = c == CARRIAGE_RETURN; + } else if (c == LINE_FEED) { + for (CharHandler handler : handlers) { + handler.handleAll(c); + handler.newLine(); + } + } else if (c == CARRIAGE_RETURN) { + afterCR = true; + for (CharHandler handler : handlers) { + handler.handleAll(c); + } + } else { + for (CharHandler handler : handlers) { + handler.handleIgnoreEoL(c); + handler.handleAll(c); } - i = reader.read(); - } - for (CharHandler handler : handlers) { - handler.eof(); } - } catch (IOException e) { - throw new IllegalStateException(String.format("Fail to read file '%s' with encoding '%s'", file.getAbsolutePath(), encoding), e); + i = reader.read(); + } + for (CharHandler handler : handlers) { + handler.eof(); } } - static class Metadata { + public static class Metadata { final int lines; final int nonBlankLines; final String hash; @@ -317,6 +326,6 @@ public class FileMetadata implements BatchComponent { * Compute a MD5 hash of each line of the file after removing of all blank chars */ public static void computeLineHashesForIssueTracking(DefaultInputFile f, LineHashConsumer consumer) { - scanFile(f.file(), f.charset(), new LineHashComputer(consumer)); + readFile(f.file(), f.charset(), new LineHashComputer(consumer)); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlighting.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlighting.java index 60e0db9a2d3..50bb7cbab30 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlighting.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlighting.java @@ -36,7 +36,7 @@ import java.util.Set; public class DefaultHighlighting extends DefaultStorable implements NewHighlighting { - private InputFile inputFile; + private DefaultInputFile inputFile; private Set<SyntaxHighlightingRule> syntaxHighlightingRuleSet; public DefaultHighlighting() { @@ -48,9 +48,9 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight syntaxHighlightingRuleSet = Sets.newTreeSet(new Comparator<SyntaxHighlightingRule>() { @Override public int compare(SyntaxHighlightingRule left, SyntaxHighlightingRule right) { - int result = left.getStartPosition() - right.getStartPosition(); + int result = left.range().start().compareTo(right.range().start()); if (result == 0) { - result = right.getEndPosition() - left.getEndPosition(); + result = right.range().end().compareTo(left.range().end()); } return result; } @@ -67,9 +67,9 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight SyntaxHighlightingRule previous = it.next(); while (it.hasNext()) { SyntaxHighlightingRule current = it.next(); - if (previous.getEndPosition() > current.getStartPosition() && !(previous.getEndPosition() >= current.getEndPosition())) { - String errorMsg = String.format("Cannot register highlighting rule for characters from %s to %s as it " + - "overlaps at least one existing rule", current.getStartPosition(), current.getEndPosition()); + if (previous.range().end().compareTo(current.range().start()) > 0 && !(previous.range().end().compareTo(current.range().end()) >= 0)) { + String errorMsg = String.format("Cannot register highlighting rule for characters at %s as it " + + "overlaps at least one existing rule", current.range()); throw new IllegalStateException(errorMsg); } previous = current; @@ -80,7 +80,7 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight @Override public DefaultHighlighting onFile(InputFile inputFile) { Preconditions.checkNotNull(inputFile, "file can't be null"); - this.inputFile = inputFile; + this.inputFile = (DefaultInputFile) inputFile; return this; } @@ -91,21 +91,11 @@ public class DefaultHighlighting extends DefaultStorable implements NewHighlight @Override public DefaultHighlighting highlight(int startOffset, int endOffset, TypeOfText typeOfText) { Preconditions.checkState(inputFile != null, "Call onFile() first"); - int maxValidOffset = ((DefaultInputFile) inputFile).lastValidOffset(); - checkOffset(startOffset, maxValidOffset, "startOffset"); - checkOffset(endOffset, maxValidOffset, "endOffset"); - Preconditions.checkArgument(startOffset < endOffset, "startOffset (" + startOffset + ") should be < endOffset (" + endOffset + ") for file " + inputFile + "."); - SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(startOffset, endOffset, - typeOfText); + SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(inputFile.newRange(startOffset, endOffset), typeOfText); this.syntaxHighlightingRuleSet.add(syntaxHighlightingRule); return this; } - private void checkOffset(int offset, int maxValidOffset, String label) { - Preconditions.checkArgument(offset >= 0 && offset <= maxValidOffset, "Invalid " + label + " " + offset + ". Should be >= 0 and <= " + maxValidOffset - + " for file " + inputFile); - } - @Override protected void doSave() { Preconditions.checkState(inputFile != null, "Call onFile() first"); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/SyntaxHighlightingRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/SyntaxHighlightingRule.java index 9989449ff00..c9b1f000c09 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/SyntaxHighlightingRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/highlighting/internal/SyntaxHighlightingRule.java @@ -19,32 +19,27 @@ */ package org.sonar.api.batch.sensor.highlighting.internal; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import java.io.Serializable; +public class SyntaxHighlightingRule { -public class SyntaxHighlightingRule implements Serializable { - - private final int startPosition; - private final int endPosition; + private final TextRange range; private final TypeOfText textType; - private SyntaxHighlightingRule(int startPosition, int endPosition, TypeOfText textType) { - this.startPosition = startPosition; - this.endPosition = endPosition; + private SyntaxHighlightingRule(TextRange range, TypeOfText textType) { + this.range = range; this.textType = textType; } - public static SyntaxHighlightingRule create(int startPosition, int endPosition, TypeOfText textType) { - return new SyntaxHighlightingRule(startPosition, endPosition, textType); - } - - public int getStartPosition() { - return startPosition; + public static SyntaxHighlightingRule create(TextRange range, TypeOfText textType) { + return new SyntaxHighlightingRule(range, textType); } - public int getEndPosition() { - return endPosition; + public TextRange range() { + return range; } public TypeOfText getTextType() { @@ -53,6 +48,6 @@ public class SyntaxHighlightingRule implements Serializable { @Override public String toString() { - return "" + startPosition + "," + endPosition + "," + textType.cssClass(); + return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java index 2846e0676fb..278d9ad1741 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java @@ -27,6 +27,7 @@ import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultTextPointer; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.sensor.Sensor; @@ -55,12 +56,7 @@ import javax.annotation.Nullable; import java.io.File; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Utility class to help testing {@link Sensor}. @@ -179,14 +175,15 @@ public class SensorContextTester implements SensorContext { return new DefaultHighlighting(sensorStorage); } - public List<TypeOfText> highlightingTypeFor(String componentKey, int charIndex) { + public List<TypeOfText> highlightingTypeAt(String componentKey, int line, int lineOffset) { DefaultHighlighting syntaxHighlightingData = sensorStorage.highlightingByComponent.get(componentKey); if (syntaxHighlightingData == null) { return Collections.emptyList(); } List<TypeOfText> result = new ArrayList<TypeOfText>(); + DefaultTextPointer location = new DefaultTextPointer(line, lineOffset); for (SyntaxHighlightingRule sortedRule : syntaxHighlightingData.getSyntaxHighlightingRuleSet()) { - if (sortedRule.getStartPosition() <= charIndex && sortedRule.getEndPosition() > charIndex) { + if (sortedRule.range().start().compareTo(location) <= 0 && sortedRule.range().end().compareTo(location) > 0) { result.add(sortedRule.getTextType()); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java index 850d3e6cdc7..121ba2a874c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java @@ -19,6 +19,7 @@ */ package org.sonar.api.batch.sensor.internal; +import org.sonar.api.BatchComponent; import org.sonar.api.batch.sensor.dependency.Dependency; import org.sonar.api.batch.sensor.duplication.Duplication; import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; @@ -29,7 +30,7 @@ import org.sonar.api.batch.sensor.measure.Measure; * Interface for storing data computed by sensors. * @since 5.1 */ -public interface SensorStorage { +public interface SensorStorage extends BatchComponent { void store(Measure measure); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspectives.java b/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspectives.java index a5f7773e745..8f16849892e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspectives.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspectives.java @@ -22,6 +22,10 @@ package org.sonar.api.component; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; +/** + * @deprecated since 5.2 unused + */ +@Deprecated public interface Perspectives extends BatchComponent, ServerComponent { <P extends Perspective> P as(Class<P> perspectiveClass, Component component); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java index d92592678eb..e8555ed4e35 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ComponentContainer.java @@ -32,6 +32,7 @@ import org.sonar.api.ServerComponent; import org.sonar.api.config.PropertyDefinitions; import javax.annotation.Nullable; + import java.util.Collection; import java.util.List; @@ -172,7 +173,11 @@ public class ComponentContainer implements BatchComponent, ServerComponent { if (component instanceof ComponentAdapter) { pico.addAdapter((ComponentAdapter) component); } else { - pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component); + try { + pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component); + } catch (Throwable t) { + throw new IllegalStateException("Unable to register component " + getName(component), t); + } declareExtension(null, component); } return this; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java index dd4c7d40883..091b881fabe 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbol.java @@ -22,12 +22,19 @@ package org.sonar.api.source; public interface Symbol { + /** + * @deprecated in 5.2 not used. + */ + @Deprecated int getDeclarationStartOffset(); + /** + * @deprecated in 5.2 not used. + */ + @Deprecated int getDeclarationEndOffset(); /** - * @since unused * @deprecated in 4.3 not used. */ @Deprecated diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java index 1cc82162047..f205ec41c17 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/source/Symbolizable.java @@ -44,6 +44,10 @@ public interface Symbolizable extends Perspective { List<Symbol> symbols(); + /** + * @deprecated since 5.2 not used + */ + @Deprecated List<Integer> references(Symbol symbol); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java index edf072dca79..6b8b6af9fea 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/DefaultInputFileTest.java @@ -27,6 +27,7 @@ import org.sonar.api.batch.fs.InputFile; import java.io.File; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; public class DefaultInputFileTest { @@ -73,4 +74,110 @@ public class DefaultInputFileTest { DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php"); assertThat(file.toString()).isEqualTo("[moduleKey=ABCDE, relative=src/Foo.php, basedir=null]"); } + + @Test + public void checkValidPointer() { + DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php"); + file.setLines(2); + file.setOriginalLineOffsets(new int[] {0, 10}); + file.setLastValidOffset(15); + assertThat(file.newPointer(1, 0).line()).isEqualTo(1); + assertThat(file.newPointer(1, 0).lineOffset()).isEqualTo(0); + // Don't fail + file.newPointer(1, 9); + file.newPointer(2, 0); + file.newPointer(2, 5); + + try { + file.newPointer(0, 1); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("0 is not a valid line for a file"); + } + try { + file.newPointer(3, 1); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("3 is not a valid line for pointer. File [moduleKey=ABCDE, relative=src/Foo.php, basedir=null] has 2 line(s)"); + } + try { + file.newPointer(1, -1); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("-1 is not a valid line offset for a file"); + } + try { + file.newPointer(1, 10); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("10 is not a valid line offset for pointer. File [moduleKey=ABCDE, relative=src/Foo.php, basedir=null] has 9 character(s) at line 1"); + } + } + + @Test + public void checkValidPointerUsingGlobalOffset() { + DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php"); + file.setLines(2); + file.setOriginalLineOffsets(new int[] {0, 10}); + file.setLastValidOffset(15); + assertThat(file.newPointer(0).line()).isEqualTo(1); + assertThat(file.newPointer(0).lineOffset()).isEqualTo(0); + + assertThat(file.newPointer(9).line()).isEqualTo(1); + assertThat(file.newPointer(9).lineOffset()).isEqualTo(9); + + assertThat(file.newPointer(10).line()).isEqualTo(2); + assertThat(file.newPointer(10).lineOffset()).isEqualTo(0); + + assertThat(file.newPointer(15).line()).isEqualTo(2); + assertThat(file.newPointer(15).lineOffset()).isEqualTo(5); + + try { + file.newPointer(-1); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("-1 is not a valid offset for a file"); + } + + try { + file.newPointer(16); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("16 is not a valid offset for file [moduleKey=ABCDE, relative=src/Foo.php, basedir=null]. Max offset is 15"); + } + } + + @Test + public void checkValidRange() { + DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php"); + file.setLines(2); + file.setOriginalLineOffsets(new int[] {0, 10}); + file.setLastValidOffset(15); + assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 1)).start().line()).isEqualTo(1); + // Don't fail + file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)); + file.newRange(file.newPointer(1, 0), file.newPointer(1, 9)); + file.newRange(file.newPointer(1, 0), file.newPointer(2, 0)); + assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(2, 5))).isEqualTo(file.newRange(0, 15)); + + try { + file.newRange(file.newPointer(1, 0), file.newPointer(1, 0)); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("Start pointer [line=1, lineOffset=0] should be before end pointer [line=1, lineOffset=0]"); + } + } + + @Test + public void testRangeOverlap() { + DefaultInputFile file = new DefaultInputFile("ABCDE", "src/Foo.php"); + file.setLines(2); + file.setOriginalLineOffsets(new int[] {0, 10}); + file.setLastValidOffset(15); + // 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(); + assertThat(file.newRange(file.newPointer(1, 0), file.newPointer(1, 1)).overlap(file.newRange(file.newPointer(1, 1), file.newPointer(1, 2)))).isFalse(); + assertThat(file.newRange(file.newPointer(1, 2), file.newPointer(1, 3)).overlap(file.newRange(file.newPointer(1, 0), file.newPointer(1, 2)))).isFalse(); + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileMetadataTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileMetadataTest.java index a470378a99e..7696c1afcbf 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/FileMetadataTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/FileMetadataTest.java @@ -17,7 +17,7 @@ * 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.batch.scan.filesystem; +package org.sonar.api.batch.fs.internal; import com.google.common.base.Charsets; import org.apache.commons.codec.binary.Hex; @@ -26,11 +26,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.AnalysisMode; -import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata.LineHashConsumer; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.batch.scan.filesystem.FileMetadata.LineHashConsumer; import javax.annotation.Nullable; @@ -39,7 +37,6 @@ import java.nio.charset.Charset; import static org.apache.commons.codec.digest.DigestUtils.md5Hex; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; public class FileMetadataTest { @@ -49,8 +46,6 @@ public class FileMetadataTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - private AnalysisMode mode = mock(AnalysisMode.class); - @Rule public LogTester logTester = new LogTester(); @@ -59,7 +54,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.touch(tempFile); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(1); assertThat(metadata.nonBlankLines).isEqualTo(0); assertThat(metadata.hash).isNotEmpty(); @@ -73,7 +68,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "foo\r\nbar\r\nbaz", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(3); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz")); @@ -87,7 +82,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "marker´s\n", Charset.forName("cp1252")); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(2); assertThat(metadata.hash).isEqualTo(md5Hex("marker\ufffds\n")); assertThat(metadata.originalLineOffsets).containsOnly(0, 9); @@ -98,7 +93,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "föo\r\nbàr\r\n\u1D11Ebaßz\r\n", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(4); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("föo\nbàr\n\u1D11Ebaßz\n")); @@ -110,7 +105,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "föo\r\nbàr\r\n\u1D11Ebaßz\r\n", Charsets.UTF_16, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_16); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_16); assertThat(metadata.lines).isEqualTo(4); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("föo\nbàr\n\u1D11Ebaßz\n")); @@ -122,7 +117,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "foo\nbar\nbaz", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(3); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz")); @@ -135,7 +130,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "foo\nbar\nbaz\n", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(4); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz\n")); @@ -148,7 +143,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "foo\nbar\r\nbaz\n", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(4); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz\n")); @@ -160,7 +155,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "foo\n\n\nbar", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(4); assertThat(metadata.nonBlankLines).isEqualTo(2); assertThat(metadata.hash).isEqualTo(md5Hex("foo\n\n\nbar")); @@ -172,7 +167,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "foo\nbar\r\nbaz", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(3); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz")); @@ -184,7 +179,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "\nfoo\nbar\r\nbaz", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(4); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("\nfoo\nbar\nbaz")); @@ -196,7 +191,7 @@ public class FileMetadataTest { File tempFile = temp.newFile(); FileUtils.write(tempFile, "\uFEFFfoo\nbar\r\nbaz", Charsets.UTF_8, true); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(tempFile, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(tempFile, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(3); assertThat(metadata.nonBlankLines).isEqualTo(3); assertThat(metadata.hash).isEqualTo(md5Hex("foo\nbar\nbaz")); @@ -238,7 +233,7 @@ public class FileMetadataTest { thrown.expect(IllegalStateException.class); thrown.expectMessage("Fail to read file '" + file.getAbsolutePath() + "' with encoding 'UTF-8'"); - new FileMetadata(mode).read(file, Charsets.UTF_8); + new FileMetadata().readMetadata(file, Charsets.UTF_8); } @Test @@ -253,9 +248,9 @@ public class FileMetadataTest { File file2 = temp.newFile(); FileUtils.write(file2, "foo\nbar", Charsets.UTF_8, true); - String hash1 = new FileMetadata(mode).read(file1, Charsets.UTF_8).hash; - String hash1a = new FileMetadata(mode).read(file1a, Charsets.UTF_8).hash; - String hash2 = new FileMetadata(mode).read(file2, Charsets.UTF_8).hash; + String hash1 = new FileMetadata().readMetadata(file1, Charsets.UTF_8).hash; + String hash1a = new FileMetadata().readMetadata(file1a, Charsets.UTF_8).hash; + String hash2 = new FileMetadata().readMetadata(file2, Charsets.UTF_8).hash; assertThat(hash1).isEqualTo(hash1a); assertThat(hash1).isNotEqualTo(hash2); } @@ -264,7 +259,7 @@ public class FileMetadataTest { public void binary_file_with_unmappable_character() throws Exception { File woff = new File(this.getClass().getResource("glyphicons-halflings-regular.woff").toURI()); - FileMetadata.Metadata metadata = new FileMetadata(mode).read(woff, Charsets.UTF_8); + FileMetadata.Metadata metadata = new FileMetadata().readMetadata(woff, Charsets.UTF_8); assertThat(metadata.lines).isEqualTo(135); assertThat(metadata.nonBlankLines).isEqualTo(134); assertThat(metadata.hash).isNotEmpty(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlightingTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlightingTest.java index 4cf1225e27b..79f7db9e06b 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlightingTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/highlighting/internal/DefaultHighlightingTest.java @@ -23,7 +23,10 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultTextPointer; +import org.sonar.api.batch.fs.internal.DefaultTextRange; import org.sonar.api.batch.sensor.internal.SensorStorage; import java.util.Collection; @@ -36,6 +39,11 @@ import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD; public class DefaultHighlightingTest { + private static final DefaultInputFile INPUT_FILE = new DefaultInputFile("foo", "src/Foo.java") + .setLines(2) + .setOriginalLineOffsets(new int[] {0, 50}) + .setLastValidOffset(100); + private Collection<SyntaxHighlightingRule> highlightingRules; @Rule @@ -45,7 +53,7 @@ public class DefaultHighlightingTest { public void setUpSampleRules() { DefaultHighlighting highlightingDataBuilder = new DefaultHighlighting() - .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100)) + .onFile(INPUT_FILE) .highlight(0, 10, COMMENT) .highlight(10, 12, KEYWORD) .highlight(24, 38, KEYWORD) @@ -61,17 +69,25 @@ public class DefaultHighlightingTest { assertThat(highlightingRules).hasSize(6); } + private static TextRange rangeOf(int startLine, int startOffset, int endLine, int endOffset) { + return new DefaultTextRange(new DefaultTextPointer(startLine, startOffset), new DefaultTextPointer(endLine, endOffset)); + } + @Test public void should_order_by_start_then_end_offset() throws Exception { - assertThat(highlightingRules).extracting("startPosition").containsOnly(0, 10, 12, 24, 24, 42); - assertThat(highlightingRules).extracting("endPosition").containsOnly(10, 12, 20, 38, 65, 50); + assertThat(highlightingRules).extracting("range", TextRange.class).containsExactly(rangeOf(1, 0, 1, 10), + rangeOf(1, 10, 1, 12), + rangeOf(1, 12, 1, 20), + rangeOf(1, 24, 2, 15), + rangeOf(1, 24, 1, 38), + rangeOf(1, 42, 2, 0)); assertThat(highlightingRules).extracting("textType").containsOnly(COMMENT, KEYWORD, COMMENT, KEYWORD, CPP_DOC, KEYWORD); } @Test public void should_suport_overlapping() throws Exception { new DefaultHighlighting(mock(SensorStorage.class)) - .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100)) + .onFile(INPUT_FILE) .highlight(0, 15, KEYWORD) .highlight(8, 12, CPP_DOC) .save(); @@ -80,49 +96,14 @@ public class DefaultHighlightingTest { @Test public void should_prevent_boudaries_overlapping() throws Exception { throwable.expect(IllegalStateException.class); - throwable.expectMessage("Cannot register highlighting rule for characters from 8 to 15 as it overlaps at least one existing rule"); + throwable + .expectMessage("Cannot register highlighting rule for characters at Range[from [line=1, lineOffset=8] to [line=1, lineOffset=15]] as it overlaps at least one existing rule"); new DefaultHighlighting(mock(SensorStorage.class)) - .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100)) + .onFile(INPUT_FILE) .highlight(0, 10, KEYWORD) .highlight(8, 15, KEYWORD) .save(); } - @Test - public void should_prevent_invalid_offset() throws Exception { - throwable.expect(IllegalArgumentException.class); - throwable.expectMessage("Invalid endOffset 15. Should be >= 0 and <= 10 for file [moduleKey=foo, relative=src/Foo.java, basedir=null]"); - - new DefaultHighlighting(mock(SensorStorage.class)) - .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(10)) - .highlight(0, 10, KEYWORD) - .highlight(8, 15, KEYWORD) - .save(); - } - - @Test - public void positive_offset() throws Exception { - throwable.expect(IllegalArgumentException.class); - throwable.expectMessage("Invalid startOffset -8. Should be >= 0 and <= 10 for file [moduleKey=foo, relative=src/Foo.java, basedir=null]"); - - new DefaultHighlighting(mock(SensorStorage.class)) - .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(10)) - .highlight(0, 10, KEYWORD) - .highlight(-8, 15, KEYWORD) - .save(); - } - - @Test - public void should_prevent_invalid_offset_order() throws Exception { - throwable.expect(IllegalArgumentException.class); - throwable.expectMessage("startOffset (18) should be < endOffset (15) for file [moduleKey=foo, relative=src/Foo.java, basedir=null]."); - - new DefaultHighlighting(mock(SensorStorage.class)) - .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(20)) - .highlight(0, 10, KEYWORD) - .highlight(18, 15, KEYWORD) - .save(); - } - } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java index 0be267797b7..c1784a24dfc 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java @@ -26,6 +26,7 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.sensor.highlighting.TypeOfText; @@ -34,6 +35,7 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.rule.RuleKey; import java.io.File; +import java.io.StringReader; import static org.assertj.core.api.Assertions.assertThat; @@ -142,15 +144,15 @@ public class SensorContextTesterTest { @Test public void testHighlighting() { - assertThat(tester.highlightingTypeFor("foo:src/Foo.java", 3)).isEmpty(); + assertThat(tester.highlightingTypeAt("foo:src/Foo.java", 1, 3)).isEmpty(); tester.newHighlighting() - .onFile(new DefaultInputFile("foo", "src/Foo.java").setLastValidOffset(100)) + .onFile(new DefaultInputFile("foo", "src/Foo.java").initMetadata(new FileMetadata().readMetadata(new StringReader("annot dsf fds foo bar")))) .highlight(0, 4, TypeOfText.ANNOTATION) .highlight(8, 10, TypeOfText.CONSTANT) .highlight(9, 10, TypeOfText.COMMENT) .save(); - assertThat(tester.highlightingTypeFor("foo:src/Foo.java", 3)).containsExactly(TypeOfText.ANNOTATION); - assertThat(tester.highlightingTypeFor("foo:src/Foo.java", 9)).containsExactly(TypeOfText.CONSTANT, TypeOfText.COMMENT); + assertThat(tester.highlightingTypeAt("foo:src/Foo.java", 1, 3)).containsExactly(TypeOfText.ANNOTATION); + assertThat(tester.highlightingTypeAt("foo:src/Foo.java", 1, 9)).containsExactly(TypeOfText.CONSTANT, TypeOfText.COMMENT); } @Test diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/filesystem/glyphicons-halflings-regular.woff b/sonar-plugin-api/src/test/resources/org/sonar/api/batch/fs/internal/glyphicons-halflings-regular.woff Binary files differindex 2cc3e4852a5..2cc3e4852a5 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/scan/filesystem/glyphicons-halflings-regular.woff +++ b/sonar-plugin-api/src/test/resources/org/sonar/api/batch/fs/internal/glyphicons-halflings-regular.woff |