From 255d7243980180740c6e4e276cc6413209921596 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 22 Jul 2015 09:10:50 +0200 Subject: [PATCH] Revert deletion of CountDistributionBuilder --- .../measures/CountDistributionBuilder.java | 168 ++++++++++++++++++ .../CountDistributionBuilderTest.java | 87 +++++++++ 2 files changed, 255 insertions(+) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/measures/CountDistributionBuilderTest.java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java new file mode 100644 index 00000000000..721a26be215 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java @@ -0,0 +1,168 @@ +/* + * 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.measures; + +import java.util.Map; +import org.apache.commons.collections.SortedBag; +import org.apache.commons.collections.bag.TreeBag; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.math.NumberUtils; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.SonarException; + +/** + * Utility to build a distribution based on discrete values + * + *

An example of usage : you wish to record the number of violations for each level of rules priority

+ * + * @since 1.10 + */ +public class CountDistributionBuilder implements MeasureBuilder { + + private Metric metric; + // TODO to be replaced by com.google.common.collect.SortedMultiset while upgrading to Guava 11+ + private SortedBag countBag; + + /** + * Creates an empty CountDistributionBuilder for a specified metric + * + * @param metric the metric + */ + public CountDistributionBuilder(Metric metric) { + setMetric(metric); + this.countBag = new TreeBag(); + } + + /** + * Increments an entry + * + * @param value the value that should be incremented + * @param count the number by which to increment + * @return the current object + */ + public CountDistributionBuilder add(Object value, int count) { + if (count == 0) { + addZero(value); + + } else { + if (this.countBag.add(value, count)) { + //hack + this.countBag.add(value, 1); + } + } + return this; + } + + /** + * Increments an entry by one + * + * @param value the value that should be incremented + * @return the current object + */ + public CountDistributionBuilder add(Object value) { + return add(value, 1); + } + + /** + * Adds an entry without a zero count if it does not exist + * + * @param value the entry to be added + * @return the current object + */ + public CountDistributionBuilder addZero(Object value) { + if (!countBag.contains(value)) { + countBag.add(value, 1); + } + return this; + } + + /** + * Adds an existing Distribution to the current one. + * It will create the entries if they don't exist. + * Can be used to add the values of children resources for example + * + * @param measure the measure to add to the current one + * @return the current object + */ + public CountDistributionBuilder add(Measure measure) { + if (measure != null && measure.getData() != null) { + Map map = KeyValueFormat.parse(measure.getData()); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + int value = StringUtils.isBlank(entry.getValue()) ? 0 : Integer.parseInt(entry.getValue()); + if (NumberUtils.isNumber(key)) { + add(NumberUtils.toInt(key), value); + } else { + add(key, value); + } + } + } + return this; + } + + /** + * @return whether the current object is empty or not + */ + public boolean isEmpty() { + return countBag.isEmpty(); + } + + /** + * Resets all entries to zero + * + * @return the current object + */ + public CountDistributionBuilder clear() { + countBag.clear(); + return this; + } + + /** + * Shortcut for build(true) + * + * @return the built measure + */ + @Override + public Measure build() { + return build(true); + } + + /** + * Used to build a measure from the current object + * + * @param allowEmptyData should be built if current object is empty + * @return the built measure + */ + public Measure build(boolean allowEmptyData) { + if (!isEmpty() || allowEmptyData) { + //-1 is a hack to include zero values + return new Measure(metric, KeyValueFormat.format(countBag, -1)); + } + return null; + } + + private void setMetric(Metric metric) { + if (metric == null || !metric.isDataType()) { + throw new SonarException("Metric is null or has unvalid type"); + } + this.metric = metric; + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/measures/CountDistributionBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/measures/CountDistributionBuilderTest.java new file mode 100644 index 00000000000..2a832c77bce --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/measures/CountDistributionBuilderTest.java @@ -0,0 +1,87 @@ +/* + * 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.measures; + +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CountDistributionBuilderTest { + @Test + public void buildDistribution() { + CountDistributionBuilder builder = new CountDistributionBuilder(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION); + Measure measure = builder + .add("foo") + .add("bar") + .add("foo") + .add("hello") + .build(); + + assertThat(measure.getData(), is("bar=1;foo=2;hello=1")); + } + + @Test + public void addZeroValues() { + CountDistributionBuilder builder = new CountDistributionBuilder(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION); + Measure measure = builder + .addZero("foo") + .add("bar") + .add("foo") + .addZero("hello") + .build(); + + assertThat(measure.getData(), is("bar=1;foo=1;hello=0")); + } + + @Test + public void addDistributionMeasureAsStrings() { + Measure measureToAdd = mock(Measure.class); + when(measureToAdd.getData()).thenReturn("foo=3;hello=5;none=0"); + + CountDistributionBuilder builder = new CountDistributionBuilder(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION); + Measure measure = builder + .add("bar") + .add("foo") + .add(measureToAdd) + .build(); + + assertThat(measure.getData(), is("bar=1;foo=4;hello=5;none=0")); + } + + @Test + public void intervalsAreSorted() { + Measure measureToAdd = mock(Measure.class); + when(measureToAdd.getData()).thenReturn("10=5;3=2;1=3"); + + CountDistributionBuilder builder = new CountDistributionBuilder(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION); + Measure measure = builder + .add(10) + .add(measureToAdd) + .add(1) + .build(); + + assertThat(measure.getData(), is("1=4;3=2;10=6")); + } + +} -- 2.39.5