001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.api.measures; 021 022 import org.apache.commons.collections.SortedBag; 023 import org.apache.commons.collections.Transformer; 024 import org.apache.commons.collections.bag.TransformedSortedBag; 025 import org.apache.commons.collections.bag.TreeBag; 026 import org.sonar.api.utils.KeyValueFormat; 027 import org.sonar.api.utils.SonarException; 028 029 import java.util.Arrays; 030 import java.util.Map; 031 032 /** 033 * An utility to build a range distribution based on 2 criteria: one is measure, the other one is recorded. 034 * 035 * <p>An example of usage : you wish to save the number of lines that belong to methods that have complexity 036 * belonging to certain ranges.</p> 037 * 038 * @since 1.10 039 */ 040 public class RangeDistributionBuilder implements MeasureBuilder { 041 042 private Metric metric; 043 private SortedBag countBag; 044 private boolean isEmpty = true; 045 private final Number[] bottomLimits; 046 047 /** 048 * RangeDistributionBuilder for a metric and a defined range 049 * 050 * @param metric the metric for the measure being built 051 * @param bottomLimits the bottom limits for lookup 052 */ 053 public RangeDistributionBuilder(Metric metric, Number[] bottomLimits) { 054 setMetric(metric); 055 this.bottomLimits = new Number[bottomLimits.length]; 056 System.arraycopy(bottomLimits, 0, this.bottomLimits, 0, this.bottomLimits.length); 057 Arrays.sort(this.bottomLimits); 058 countBag = TransformedSortedBag.decorate(new TreeBag(), new RangeTransformer()); 059 doClear(); 060 } 061 062 /** 063 * Gets the defined bottom limits 064 * 065 * @return the bottom limits of defined range for the distribution 066 */ 067 public Number[] getBottomLimits() { 068 return bottomLimits; 069 } 070 071 /** 072 * Add 1 to the range object belongs to 073 * 074 * @param object the Number to use to pick the range 075 * @return this 076 */ 077 public RangeDistributionBuilder add(Number object) { 078 return add(object, 1); 079 } 080 081 public RangeDistributionBuilder add(Number object, int count) { 082 if (object != null && greaterOrEqualsThan(object, bottomLimits[0])) { 083 this.countBag.add(object, count); 084 isEmpty = false; 085 } 086 return this; 087 } 088 089 public RangeDistributionBuilder add(Measure measure) { 090 if (measure != null && measure.getData() != null) { 091 Map<Double, Double> map = KeyValueFormat.parse(measure.getData(), new KeyValueFormat.DoubleNumbersPairTransformer()); 092 for (Map.Entry<Double, Double> entry : map.entrySet()) { 093 add(entry.getKey(), entry.getValue().intValue()); 094 } 095 } 096 return this; 097 } 098 099 public RangeDistributionBuilder clear() { 100 doClear(); 101 return this; 102 } 103 104 private void doClear() { 105 countBag.clear(); 106 countBag.addAll(Arrays.asList(bottomLimits)); 107 isEmpty = true; 108 } 109 110 public boolean isEmpty() { 111 return isEmpty; 112 } 113 114 public Measure build() { 115 return build(true); 116 } 117 118 public Measure build(boolean allowEmptyData) { 119 if (!isEmpty || allowEmptyData) { 120 return new Measure(metric, KeyValueFormat.format(countBag, -1)); 121 } 122 return null; 123 } 124 125 private class RangeTransformer implements Transformer { 126 public Object transform(Object o) { 127 Number n = (Number) o; 128 for (int i = bottomLimits.length - 1; i >= 0; i--) { 129 if (greaterOrEqualsThan(n, bottomLimits[i])) { 130 return bottomLimits[i]; 131 } 132 } 133 return null; 134 } 135 } 136 137 private static boolean greaterOrEqualsThan(Number n1, Number n2) { 138 return n1.doubleValue() >= n2.doubleValue(); 139 } 140 141 private void setMetric(Metric metric) { 142 if (metric == null || !metric.isDataType()) { 143 throw new SonarException("Metric is null or has unvalid type"); 144 } 145 this.metric = metric; 146 } 147 }