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;
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++) {
*/
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;
*/
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
return Collections.emptyList();
}
+ public FileLinesContext createFileLinesContext(SourceFile file) {
+ JavaFile javaFile = SquidUtils.convertJavaFileKeyFromSquidFormat(file.getKey());
+ return factory.createFor(javaFile);
+ }
+
}
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;
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) {
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();
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;
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"));
*/
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;
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);
@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));
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;
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;
}
.setPersistenceMode(PersistenceMode.DATABASE)
.setData(data);
index.addMeasure(resource, measure);
+ entry.setValue(ImmutableMap.copyOf(lines));
}
}
}
}
/**
- * 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);
--- /dev/null
+/*
+ * 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);
+ }
+
+}
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;
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()));
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;
addCoreSingleton(ProjectConfigurator.class);
addCoreSingleton(DefaultResourceCreationLock.class);
addCoreSingleton(DefaultIndex.class);
+ addCoreSingleton(DefaultFileLinesContextFactory.class);
if (dryRun) {
addCoreSingleton(ReadOnlyPersistenceManager.class);
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;
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);
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
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());
*/
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;
*/
Measure saveMeasure(Resource resource, Measure measure);
- /**
- * @since 2.14
- */
- @Beta
- FileLinesContext createFileLinesContext(Resource resource);
-
// ----------- RULE VIOLATIONS --------------
/**
* <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>
*
@Beta
public interface FileLinesContext {
+ /**
+ * @throws UnsupportedOperationException on attempt to update already saved data
+ */
void setIntValue(String metricKey, int line, int value);
/**
*/
Integer getIntValue(String metricKey, int line);
+ /**
+ * @throws UnsupportedOperationException on attempt to update already saved data
+ */
void setStringValue(String metricKey, int line, String value);
/**
*/
String getStringValue(String metricKey, int line);
+ /**
+ * Saves unsaved values.
+ */
void save();
}
--- /dev/null
+/*
+ * 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);
+
+}