summaryrefslogtreecommitdiffstats
path: root/sonar-batch/src
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2012-02-09 17:23:05 +0400
committerEvgeny Mandrikov <mandrikov@gmail.com>2012-02-09 18:48:53 +0400
commit90a3277d3f57a4414fc6bbc48986345d56af23b6 (patch)
tree55e78d0b2fa1861d5827ca82bf5ad3913edb8aac /sonar-batch/src
parentefaa03159609208211d8271e2db44c847b1ea7c8 (diff)
downloadsonarqube-90a3277d3f57a4414fc6bbc48986345d56af23b6.tar.gz
sonarqube-90a3277d3f57a4414fc6bbc48986345d56af23b6.zip
SONAR-3209,SONAR-3210 Provide API to save and retrieve measures by line
+ Use this API to save LoC in Java files
Diffstat (limited to 'sonar-batch/src')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java151
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java9
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java112
3 files changed, 271 insertions, 1 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java
new file mode 100644
index 00000000000..c4a8e9a7579
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultFileLinesContext.java
@@ -0,0 +1,151 @@
+/*
+ * 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 com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.measures.FileLinesContext;
+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.utils.KeyValueFormat;
+import org.sonar.api.utils.KeyValueFormat.Converter;
+
+import java.util.Map;
+
+/**
+ * @since 2.14
+ */
+@Beta
+public class DefaultFileLinesContext implements FileLinesContext {
+
+ private final SonarIndex index;
+ private final Resource resource;
+
+ /**
+ * metric key -> line -> value
+ */
+ private final Map<String, Map<Integer, Object>> map = Maps.newHashMap();
+
+ public DefaultFileLinesContext(SonarIndex index, Resource resource) {
+ this.index = index;
+ this.resource = resource;
+ }
+
+ public void setIntValue(String metricKey, int line, int value) {
+ Preconditions.checkNotNull(metricKey);
+ Preconditions.checkArgument(line > 0);
+
+ setValue(metricKey, line, value);
+ }
+
+ public Integer getIntValue(String metricKey, int line) {
+ Preconditions.checkNotNull(metricKey);
+ Preconditions.checkArgument(line > 0);
+
+ Map lines = map.get(metricKey);
+ if (lines == null) {
+ // not in memory, so load
+ lines = loadData(metricKey, KeyValueFormat.newIntegerConverter());
+ map.put(metricKey, lines);
+ }
+ return (Integer) lines.get(line);
+ }
+
+ public void setStringValue(String metricKey, int line, String value) {
+ Preconditions.checkNotNull(metricKey);
+ Preconditions.checkArgument(line > 0);
+ Preconditions.checkNotNull(value);
+
+ setValue(metricKey, line, value);
+ }
+
+ public String getStringValue(String metricKey, int line) {
+ Preconditions.checkNotNull(metricKey);
+ Preconditions.checkArgument(line > 0);
+
+ Map lines = map.get(metricKey);
+ if (lines == null) {
+ // not in memory, so load
+ lines = loadData(metricKey, KeyValueFormat.newStringConverter());
+ map.put(metricKey, lines);
+ }
+ return (String) lines.get(line);
+ }
+
+ private Map<Integer, Object> getOrCreateLines(String metricKey) {
+ Map<Integer, Object> lines = map.get(metricKey);
+ if (lines == null) {
+ lines = Maps.newHashMap();
+ map.put(metricKey, lines);
+ }
+ return lines;
+ }
+
+ private void setValue(String metricKey, int line, Object value) {
+ getOrCreateLines(metricKey).put(line, value);
+ }
+
+ public void save() {
+ for (Map.Entry<String, Map<Integer, Object>> entry : map.entrySet()) {
+ String metricKey = entry.getKey();
+ Map<Integer, Object> lines = entry.getValue();
+ if (shouldSave(lines)) {
+ String data = KeyValueFormat.format(lines);
+ Measure measure = new Measure(metricKey)
+ .setPersistenceMode(PersistenceMode.DATABASE)
+ .setData(data);
+ index.addMeasure(resource, measure);
+ }
+ }
+ }
+
+ private Map loadData(String metricKey, Converter converter) {
+ // FIXME no way to load measure only by key
+ Measure measure = index.getMeasure(resource, new Metric(metricKey));
+ if (measure == null || measure.getData() == null) {
+ // no such measure
+ return ImmutableMap.of();
+ }
+ return ImmutableMap.copyOf(KeyValueFormat.parse(measure.getData(), KeyValueFormat.newIntegerConverter(), converter));
+ }
+
+ /**
+ * Checks that measure was not loaded.
+ *
+ * @see #loadData(String, Converter)
+ */
+ private boolean shouldSave(Map<Integer, Object> lines) {
+ return !(lines instanceof ImmutableMap);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("map", map)
+ .toString();
+ }
+
+}
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 1a5ecd309c6..2a75b837b6d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java
@@ -23,10 +23,13 @@ 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;
-import org.sonar.api.resources.*;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ProjectLink;
+import org.sonar.api.resources.Resource;
import org.sonar.api.rules.Violation;
import java.util.Collection;
@@ -120,6 +123,10 @@ 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/test/java/org/sonar/batch/DefaultFileLinesContextTest.java b/sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java
new file mode 100644
index 00000000000..2cf15e2bd68
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+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.Resource;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.*;
+
+public class DefaultFileLinesContextTest {
+
+ private SonarIndex index;
+ private Resource resource;
+ private DefaultFileLinesContext fileLineMeasures;
+
+ @Before
+ public void setUp() {
+ index = mock(SonarIndex.class);
+ resource = mock(Resource.class);
+ fileLineMeasures = new DefaultFileLinesContext(index, resource);
+ }
+
+ @Test
+ public void shouldSave() {
+ fileLineMeasures.setIntValue("hits", 1, 2);
+ fileLineMeasures.setIntValue("hits", 3, 4);
+ fileLineMeasures.save();
+
+ ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class);
+ verify(index).addMeasure(Mockito.eq(resource), measureCaptor.capture());
+ Measure measure = measureCaptor.getValue();
+ assertThat(measure.getMetricKey(), is("hits"));
+ assertThat(measure.getPersistenceMode(), is(PersistenceMode.DATABASE));
+ assertThat(measure.getData(), is("1=2;3=4"));
+ }
+
+ @Test
+ public void shouldSaveSeveral() {
+ fileLineMeasures.setIntValue("hits", 1, 2);
+ fileLineMeasures.setIntValue("hits", 3, 4);
+ fileLineMeasures.setStringValue("author", 1, "simon");
+ fileLineMeasures.setStringValue("author", 3, "evgeny");
+ fileLineMeasures.save();
+
+ verify(index, times(2)).addMeasure(Mockito.eq(resource), Mockito.any(Measure.class));
+ }
+
+ @Test
+ public void shouldLoadIntValues() {
+ when(index.getMeasure(Mockito.any(Resource.class), Mockito.any(Metric.class)))
+ .thenReturn(new Measure("hits").setData("1=2;3=4"));
+
+ assertThat(fileLineMeasures.getIntValue("hits", 1), is(2));
+ assertThat(fileLineMeasures.getIntValue("hits", 3), is(4));
+ assertThat("no measure on line", fileLineMeasures.getIntValue("hits", 5), nullValue());
+ }
+
+ @Test
+ public void shouldLoadStringValues() {
+ when(index.getMeasure(Mockito.any(Resource.class), Mockito.any(Metric.class)))
+ .thenReturn(new Measure("author").setData("1=simon;3=evgeny"));
+
+ assertThat(fileLineMeasures.getStringValue("author", 1), is("simon"));
+ assertThat(fileLineMeasures.getStringValue("author", 3), is("evgeny"));
+ assertThat("no measure on line", fileLineMeasures.getStringValue("author", 5), nullValue());
+ }
+
+ @Test
+ public void shouldNotSaveAfterLoad() {
+ 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.save();
+
+ verify(index, never()).addMeasure(Mockito.eq(resource), Mockito.any(Measure.class));
+ }
+
+ @Test
+ public void shouldNotFailIfNoMeasureInIndex() {
+ assertThat(fileLineMeasures.getIntValue("hits", 1), nullValue());
+ assertThat(fileLineMeasures.getStringValue("author", 1), nullValue());
+ }
+
+}