From e476635c3816598feefd88517b6f12421b807a0c Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Thu, 9 Feb 2012 21:11:53 +0400 Subject: [PATCH] SONAR-3209 Add new batch component - FileLinesContextFactory * As was discussed with Simon: better to have new beta class than new beta method. * This also provides ability to use this factory from Decorator. --- .../java/ast/visitor/FileLinesVisitor.java | 5 +-- .../sonar/plugins/squid/SonarAccessor.java | 21 ++++++---- .../org/sonar/plugins/squid/SquidSensor.java | 8 +++- .../ast/visitor/FileLinesVisitorTest.java | 10 ++--- .../sonar/plugins/squid/SquidSensorTest.java | 14 +++---- .../sonar/batch/DefaultFileLinesContext.java | 7 +++- .../batch/DefaultFileLinesContextFactory.java | 39 +++++++++++++++++++ .../org/sonar/batch/DefaultSensorContext.java | 5 --- .../sonar/batch/bootstrap/BatchModule.java | 2 + .../batch/DefaultFileLinesContextTest.java | 31 ++++++++++++++- .../org/sonar/api/batch/SensorContext.java | 8 ---- .../sonar/api/measures/FileLinesContext.java | 12 ++++++ .../api/measures/FileLinesContextFactory.java | 36 +++++++++++++++++ 13 files changed, 157 insertions(+), 41 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContextFactory.java diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/FileLinesVisitor.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/FileLinesVisitor.java index 596df2cc277..60ace8723ea 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/FileLinesVisitor.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/FileLinesVisitor.java @@ -20,10 +20,8 @@ package org.sonar.java.ast.visitor; import com.puppycrawl.tools.checkstyle.api.DetailAST; -import org.sonar.api.batch.SquidUtils; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; -import org.sonar.api.resources.JavaFile; import org.sonar.plugins.squid.SonarAccessor; import org.sonar.squid.api.SourceFile; import org.sonar.squid.measures.Metric; @@ -56,8 +54,7 @@ public class FileLinesVisitor extends JavaAstVisitor { private void processFile() { SourceFile file = (SourceFile) peekSourceCode(); - JavaFile javaFile = SquidUtils.convertJavaFileKeyFromSquidFormat(file.getKey()); - FileLinesContext measures = sonarAccessor.getSensorContext().createFileLinesContext(javaFile); + FileLinesContext measures = sonarAccessor.createFileLinesContext(file); Source source = getSource(); for (int line = 1; line <= source.getNumberOfLines(); line++) { diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SonarAccessor.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SonarAccessor.java index 0e6b9b661dd..2b11eba119b 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SonarAccessor.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SonarAccessor.java @@ -19,8 +19,12 @@ */ package org.sonar.plugins.squid; -import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.SquidUtils; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonar.api.resources.JavaFile; import org.sonar.squid.api.CodeScanner; +import org.sonar.squid.api.SourceFile; import java.util.Collection; import java.util.Collections; @@ -30,14 +34,10 @@ import java.util.Collections; */ public class SonarAccessor extends CodeScanner { - private SensorContext sensorContext; + private FileLinesContextFactory factory; - public void setSensorContext(SensorContext sensorContext) { - this.sensorContext = sensorContext; - } - - public SensorContext getSensorContext() { - return sensorContext; + public void setFileLinesContextFactory(FileLinesContextFactory factory) { + this.factory = factory; } @Override @@ -45,4 +45,9 @@ public class SonarAccessor extends CodeScanner { return Collections.emptyList(); } + public FileLinesContext createFileLinesContext(SourceFile file) { + JavaFile javaFile = SquidUtils.convertJavaFileKeyFromSquidFormat(file.getKey()); + return factory.createFor(javaFile); + } + } diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidSensor.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidSensor.java index 5f35c1ee39c..68a332285b3 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidSensor.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidSensor.java @@ -23,6 +23,7 @@ import org.sonar.api.CoreProperties; import org.sonar.api.batch.*; import org.sonar.api.checks.AnnotationCheckFactory; import org.sonar.api.checks.NoSonarFilter; +import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.InputFile; import org.sonar.api.resources.Java; @@ -45,12 +46,15 @@ public class SquidSensor implements Sensor { private RulesProfile profile; private ProjectClasspath projectClasspath; private ResourceCreationLock lock; + private FileLinesContextFactory fileLinesContextFactory; - public SquidSensor(RulesProfile profile, NoSonarFilter noSonarFilter, ProjectClasspath projectClasspath, ResourceCreationLock lock) { + public SquidSensor(RulesProfile profile, NoSonarFilter noSonarFilter, ProjectClasspath projectClasspath, ResourceCreationLock lock, + FileLinesContextFactory fileLinesContextFactory) { this.noSonarFilter = noSonarFilter; this.profile = profile; this.projectClasspath = projectClasspath; this.lock = lock; + this.fileLinesContextFactory = fileLinesContextFactory; } public boolean shouldExecuteOnProject(Project project) { @@ -74,7 +78,7 @@ public class SquidSensor implements Sensor { AnnotationCheckFactory factory = AnnotationCheckFactory.create(profile, SquidConstants.REPOSITORY_KEY, SquidRuleRepository.getCheckClasses()); SquidExecutor squidExecutor = new SquidExecutor(analyzePropertyAccessors, fieldNamesToExcludeFromLcom4Computation, factory, charset); - squidExecutor.getSquid().register(SonarAccessor.class).setSensorContext(context); + squidExecutor.getSquid().register(SonarAccessor.class).setFileLinesContextFactory(fileLinesContextFactory); squidExecutor.scan(getMainSourceFiles(project), getBytecodeFiles(project)); squidExecutor.save(project, context, noSonarFilter); squidExecutor.flush(); diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/FileLinesVisitorTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/FileLinesVisitorTest.java index a4be9d8ff2d..b92e56890ef 100644 --- a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/FileLinesVisitorTest.java +++ b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/FileLinesVisitorTest.java @@ -22,9 +22,9 @@ package org.sonar.java.ast.visitor; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.sonar.api.batch.SensorContext; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.resources.Resource; import org.sonar.java.ast.JavaAstScanner; import org.sonar.java.ast.SquidTestUtils; @@ -39,23 +39,23 @@ import static org.mockito.Mockito.*; public class FileLinesVisitorTest { private Squid squid; - private SensorContext context; + private FileLinesContextFactory factory; private FileLinesContext measures; @Before public void setUp() { squid = new Squid(new JavaSquidConfiguration()); - context = mock(SensorContext.class); + factory = mock(FileLinesContextFactory.class); measures = mock(FileLinesContext.class); } @Test public void analyseTestNcloc() { ArgumentCaptor resourceCaptor = ArgumentCaptor.forClass(Resource.class); - when(context.createFileLinesContext(resourceCaptor.capture())) + when(factory.createFor(resourceCaptor.capture())) .thenReturn(measures); - squid.register(SonarAccessor.class).setSensorContext(context); + squid.register(SonarAccessor.class).setFileLinesContextFactory(factory); squid.register(JavaAstScanner.class).scanFile(SquidTestUtils.getInputFile("/metrics/ncloc/TestNcloc.java")); assertThat(resourceCaptor.getValue().getKey(), is("[default].TestNcloc")); diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/SquidSensorTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/SquidSensorTest.java index 70a83928884..3a1cb1bb5c2 100644 --- a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/SquidSensorTest.java +++ b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/SquidSensorTest.java @@ -19,11 +19,6 @@ */ package org.sonar.plugins.squid; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.*; - import org.apache.commons.configuration.BaseConfiguration; import org.apache.commons.configuration.Configuration; import org.junit.Test; @@ -36,12 +31,17 @@ import java.io.File; import java.util.Arrays; import java.util.Collection; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.*; + public class SquidSensorTest { @Test public void testGetBytecodeFiles() { ProjectClasspath projectClasspath = mock(ProjectClasspath.class); when(projectClasspath.getElements()).thenReturn(Arrays.asList(new File("classes"))); - SquidSensor sensor = new SquidSensor(null, null, projectClasspath, null); + SquidSensor sensor = new SquidSensor(null, null, projectClasspath, null, null); Configuration configuration = new BaseConfiguration(); Project project = mock(Project.class); when(project.getConfiguration()).thenReturn(configuration); @@ -56,7 +56,7 @@ public class SquidSensorTest { @Test public void onlyForJava() { - SquidSensor sensor = new SquidSensor(null, null, null, null); + SquidSensor sensor = new SquidSensor(null, null, null, null, null); Project project = mock(Project.class); when(project.getLanguageKey()).thenReturn(Java.KEY).thenReturn("groovy"); assertThat(sensor.shouldExecuteOnProject(project), is(true)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java index c4a8e9a7579..4c998af512c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java @@ -30,6 +30,7 @@ import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; import org.sonar.api.measures.PersistenceMode; import org.sonar.api.resources.Resource; +import org.sonar.api.resources.Scopes; import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.KeyValueFormat.Converter; @@ -50,6 +51,8 @@ public class DefaultFileLinesContext implements FileLinesContext { private final Map> map = Maps.newHashMap(); public DefaultFileLinesContext(SonarIndex index, Resource resource) { + Preconditions.checkNotNull(index); + Preconditions.checkArgument(Scopes.isFile(resource)); this.index = index; this.resource = resource; } @@ -118,6 +121,7 @@ public class DefaultFileLinesContext implements FileLinesContext { .setPersistenceMode(PersistenceMode.DATABASE) .setData(data); index.addMeasure(resource, measure); + entry.setValue(ImmutableMap.copyOf(lines)); } } } @@ -133,9 +137,10 @@ public class DefaultFileLinesContext implements FileLinesContext { } /** - * Checks that measure was not loaded. + * Checks that measure was not saved. * * @see #loadData(String, Converter) + * @see #save() */ private boolean shouldSave(Map lines) { return !(lines instanceof ImmutableMap); diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java new file mode 100644 index 00000000000..497cbe1c08e --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java @@ -0,0 +1,39 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.batch.SonarIndex; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonar.api.resources.Resource; + +public class DefaultFileLinesContextFactory implements FileLinesContextFactory { + + private final SonarIndex index; + + public DefaultFileLinesContextFactory(SonarIndex index) { + this.index = index; + } + + public FileLinesContext createFor(Resource resource) { + return new DefaultFileLinesContext(index, resource); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java index 2a75b837b6d..fe42bfca0c0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java @@ -23,7 +23,6 @@ import org.sonar.api.batch.Event; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.SonarIndex; import org.sonar.api.design.Dependency; -import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.Metric; @@ -123,10 +122,6 @@ public class DefaultSensorContext implements SensorContext { return index.addMeasure(resourceOrProject(resource), measure); } - public FileLinesContext createFileLinesContext(Resource resource) { - return new DefaultFileLinesContext(index, resource); - } - public void saveViolation(Violation violation, boolean force) { if (violation.getResource() == null) { violation.setResource(resourceOrProject(violation.getResource())); diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java index 1948b6a686b..bc7516ae550 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java @@ -24,6 +24,7 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.resources.Project; import org.sonar.api.utils.ServerHttpClient; +import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.batch.ProjectConfigurator; import org.sonar.batch.ProjectTree; @@ -53,6 +54,7 @@ public class BatchModule extends Module { addCoreSingleton(ProjectConfigurator.class); addCoreSingleton(DefaultResourceCreationLock.class); addCoreSingleton(DefaultIndex.class); + addCoreSingleton(DefaultFileLinesContextFactory.class); if (dryRun) { addCoreSingleton(ReadOnlyPersistenceManager.class); diff --git a/sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java b/sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java index 2cf15e2bd68..d3f73831031 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java @@ -27,7 +27,9 @@ import org.sonar.api.batch.SonarIndex; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; import org.sonar.api.measures.PersistenceMode; +import org.sonar.api.resources.Directory; import org.sonar.api.resources.Resource; +import org.sonar.api.resources.Scopes; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -44,9 +46,15 @@ public class DefaultFileLinesContextTest { public void setUp() { index = mock(SonarIndex.class); resource = mock(Resource.class); + when(resource.getScope()).thenReturn(Scopes.FILE); fileLineMeasures = new DefaultFileLinesContext(index, resource); } + @Test(expected = IllegalArgumentException.class) + public void shouldNotAllowCreationForDirectory() { + new DefaultFileLinesContext(index, new Directory("key")); + } + @Test public void shouldSave() { fileLineMeasures.setIntValue("hits", 1, 2); @@ -68,8 +76,20 @@ public class DefaultFileLinesContextTest { fileLineMeasures.setStringValue("author", 1, "simon"); fileLineMeasures.setStringValue("author", 3, "evgeny"); fileLineMeasures.save(); + fileLineMeasures.setIntValue("branches", 1, 2); + fileLineMeasures.setIntValue("branches", 3, 4); + fileLineMeasures.save(); - verify(index, times(2)).addMeasure(Mockito.eq(resource), Mockito.any(Measure.class)); + verify(index, times(3)).addMeasure(Mockito.eq(resource), Mockito.any(Measure.class)); + } + + @Test(expected = UnsupportedOperationException.class) + public void shouldNotModifyAfterSave() { + fileLineMeasures.setIntValue("hits", 1, 2); + fileLineMeasures.save(); + fileLineMeasures.save(); + verify(index).addMeasure(Mockito.eq(resource), Mockito.any(Measure.class)); + fileLineMeasures.setIntValue("hits", 1, 2); } @Test @@ -103,6 +123,15 @@ public class DefaultFileLinesContextTest { verify(index, never()).addMeasure(Mockito.eq(resource), Mockito.any(Measure.class)); } + @Test(expected = UnsupportedOperationException.class) + public void shouldNotModifyAfterLoad() { + when(index.getMeasure(Mockito.any(Resource.class), Mockito.any(Metric.class))) + .thenReturn(new Measure("author").setData("1=simon;3=evgeny")); + + fileLineMeasures.getStringValue("author", 1); + fileLineMeasures.setStringValue("author", 1, "evgeny"); + } + @Test public void shouldNotFailIfNoMeasureInIndex() { assertThat(fileLineMeasures.getIntValue("hits", 1), nullValue()); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java index 1cc5b291403..fc9189410c2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java @@ -19,9 +19,7 @@ */ package org.sonar.api.batch; -import com.google.common.annotations.Beta; import org.sonar.api.design.Dependency; -import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.Metric; @@ -152,12 +150,6 @@ public interface SensorContext { */ Measure saveMeasure(Resource resource, Measure measure); - /** - * @since 2.14 - */ - @Beta - FileLinesContext createFileLinesContext(Resource resource); - // ----------- RULE VIOLATIONS -------------- /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContext.java index f2fe4dcaf18..51a16d337f4 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContext.java @@ -31,6 +31,9 @@ import com.google.common.annotations.Beta; *
  • author of line 4 is Simon
  • * * Numbering of lines starts from 1. + * Also note that you can't update what already was saved, however it is safe to call {@link #save()} several times. + *

    + * Instances of this interface can be obtained using {@link FileLinesContextFactory}. * *

    This interface is not intended to be implemented by clients.

    * @@ -39,6 +42,9 @@ import com.google.common.annotations.Beta; @Beta public interface FileLinesContext { + /** + * @throws UnsupportedOperationException on attempt to update already saved data + */ void setIntValue(String metricKey, int line, int value); /** @@ -46,6 +52,9 @@ public interface FileLinesContext { */ Integer getIntValue(String metricKey, int line); + /** + * @throws UnsupportedOperationException on attempt to update already saved data + */ void setStringValue(String metricKey, int line, String value); /** @@ -53,6 +62,9 @@ public interface FileLinesContext { */ String getStringValue(String metricKey, int line); + /** + * Saves unsaved values. + */ void save(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContextFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContextFactory.java new file mode 100644 index 00000000000..5fff3a6380c --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContextFactory.java @@ -0,0 +1,36 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.measures; + +import com.google.common.annotations.Beta; +import org.sonar.api.BatchComponent; +import org.sonar.api.resources.Resource; + +/** + *

    This interface is not intended to be implemented by clients.

    + * + * @since 2.14 + */ +@Beta +public interface FileLinesContextFactory extends BatchComponent { + + FileLinesContext createFor(Resource resource); + +} -- 2.39.5