]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3209 Add new batch component - FileLinesContextFactory
authorEvgeny Mandrikov <mandrikov@gmail.com>
Thu, 9 Feb 2012 17:11:53 +0000 (21:11 +0400)
committerEvgeny Mandrikov <mandrikov@gmail.com>
Thu, 9 Feb 2012 18:48:17 +0000 (22:48 +0400)
* 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.

13 files changed:
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/ast/visitor/FileLinesVisitor.java
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SonarAccessor.java
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidSensor.java
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/ast/visitor/FileLinesVisitorTest.java
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/SquidSensorTest.java
sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java
sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java
sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContext.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/FileLinesContextFactory.java [new file with mode: 0644]

index 596df2cc2776934c13638dec15bceae7f01bd4d6..60ace8723ea54444576bc7251d7f708625533370 100644 (file)
 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++) {
index 0e6b9b661dd64b3eacc72d442dfe04f1dff3a5b0..2b11eba119b5b48aa8366212b5fbcc9c9eabc69d 100644 (file)
  */
 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);
+  }
+
 }
index 5f35c1ee39c54b96679b8176181c0b42466bab9d..68a332285b3afa3e8eec126fdeede59a15f94a86 100644 (file)
@@ -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();
index a4be9d8ff2df346f0af7c99c4f690a18eb0f3dbe..b92e56890ef6a8bb55d3b2fc6a92dd0477c604eb 100644 (file)
@@ -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<Resource> 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"));
index 70a8392888458e66d3b65e57d9e84d4c06f99047..3a1cb1bb5c22c3484f0b184affd4d686f6163d8c 100644 (file)
  */
 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));
index c4a8e9a7579f0245bb5d81d5a8a2a9549cb926e1..4c998af512c34ada83a8fc8786358ac1d6b1ac99 100644 (file)
@@ -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<String, Map<Integer, Object>> 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<Integer, Object> 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 (file)
index 0000000..497cbe1
--- /dev/null
@@ -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);
+  }
+
+}
index 2a75b837b6df77dc7b64c6707851004d6b68191d..fe42bfca0c0f344b7d17e02e7ffd446da5757f8d 100644 (file)
@@ -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()));
index 1948b6a686b6a1cc61e91f2f6df0ade777950aaf..bc7516ae550a2b72a4bc20c4d858cefe5b9fce37 100644 (file)
@@ -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);
index 2cf15e2bd683c0abeed94b6925cb18cce648d1a4..d3f73831031d9a54f190660dc38858b66c7a28bd 100644 (file)
@@ -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());
index 1cc5b2914038345c76b467acef329eaf047f89e8..fc9189410c24f54deb53599dfa1eabf2bdae4265 100644 (file)
@@ -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 --------------
 
   /**
index f2fe4dcaf18a17d0578e054fdd902d64b0ac67d7..51a16d337f436adcecb3182488c651ab91ab4183 100644 (file)
@@ -31,6 +31,9 @@ import com.google.common.annotations.Beta;
  * <li>author of line 4 is Simon</li>
  * </ul>
  * 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.
+ * <p>
+ * Instances of this interface can be obtained using {@link FileLinesContextFactory}.
  *
  * <p>This interface is not intended to be implemented by clients.</p>
  *
@@ -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 (file)
index 0000000..5fff3a6
--- /dev/null
@@ -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;
+
+/**
+ * <p>This interface is not intended to be implemented by clients.</p>
+ *
+ * @since 2.14
+ */
+@Beta
+public interface FileLinesContextFactory extends BatchComponent {
+
+  FileLinesContext createFor(Resource resource);
+
+}