123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.server.measure.live;
-
- import com.google.common.collect.ArrayTable;
- import com.google.common.collect.Table;
- import java.math.BigDecimal;
- import java.math.RoundingMode;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Objects;
- import java.util.Optional;
- import java.util.Set;
- import java.util.function.Consumer;
- import java.util.stream.Collectors;
- import java.util.stream.Stream;
- import javax.annotation.Nullable;
- import org.sonar.db.component.ComponentDto;
- import org.sonar.db.measure.LiveMeasureDto;
- import org.sonar.db.metric.MetricDto;
- import org.sonar.server.measure.Rating;
-
- import static com.google.common.base.Preconditions.checkArgument;
- import static java.util.Objects.requireNonNull;
-
- /**
- * Keep the measures in memory during refresh of live measures:
- * <ul>
- * <li>the values of last analysis, restricted to the needed metrics</li>
- * <li>the refreshed values</li>
- * </ul>
- */
- class MeasureMatrix {
- // component uuid -> metric key -> measure
- private final Table<String, String, MeasureCell> table;
-
- private final Map<String, MetricDto> metricsByKeys = new HashMap<>();
- private final Map<String, MetricDto> metricsByUuids = new HashMap<>();
-
- MeasureMatrix(Collection<ComponentDto> components, Collection<MetricDto> metrics, List<LiveMeasureDto> dbMeasures) {
- this(components.stream().map(ComponentDto::uuid).collect(Collectors.toSet()), metrics, dbMeasures);
- }
-
- MeasureMatrix(Set<String> componentUuids, Collection<MetricDto> metrics, List<LiveMeasureDto> dbMeasures) {
- for (MetricDto metric : metrics) {
- this.metricsByKeys.put(metric.getKey(), metric);
- this.metricsByUuids.put(metric.getUuid(), metric);
- }
- this.table = ArrayTable.create(componentUuids, metricsByKeys.keySet());
-
- for (LiveMeasureDto dbMeasure : dbMeasures) {
- table.put(dbMeasure.getComponentUuid(), metricsByUuids.get(dbMeasure.getMetricUuid()).getKey(), new MeasureCell(dbMeasure));
- }
- }
-
- MetricDto getMetricByUuid(String uuid) {
- return requireNonNull(metricsByUuids.get(uuid), () -> String.format("Metric with uuid %s not found", uuid));
- }
-
- private MetricDto getMetric(String key) {
- return requireNonNull(metricsByKeys.get(key), () -> String.format("Metric with key %s not found", key));
- }
-
- Optional<LiveMeasureDto> getMeasure(ComponentDto component, String metricKey) {
- checkArgument(table.containsColumn(metricKey), "Metric with key %s is not registered", metricKey);
- MeasureCell cell = table.get(component.uuid(), metricKey);
- return cell == null ? Optional.empty() : Optional.of(cell.measure);
- }
-
- void setValue(ComponentDto component, String metricKey, double value) {
- changeCell(component, metricKey, m -> m.setValue(scale(getMetric(metricKey), value)));
- }
-
- void setValue(ComponentDto component, String metricKey, Rating value) {
- changeCell(component, metricKey, m -> {
- m.setData(value.name());
- m.setValue((double) value.getIndex());
- });
- }
-
- void setValue(ComponentDto component, String metricKey, @Nullable String data) {
- changeCell(component, metricKey, m -> m.setData(data));
- }
-
- Stream<LiveMeasureDto> getChanged() {
- return table.values().stream()
- .filter(Objects::nonNull)
- .filter(MeasureCell::isChanged)
- .map(MeasureCell::getMeasure);
- }
-
- private void changeCell(ComponentDto component, String metricKey, Consumer<LiveMeasureDto> changer) {
- MeasureCell cell = table.get(component.uuid(), metricKey);
- if (cell == null) {
- LiveMeasureDto measure = new LiveMeasureDto()
- .setComponentUuid(component.uuid())
- .setProjectUuid(component.branchUuid())
- .setMetricUuid(metricsByKeys.get(metricKey).getUuid());
- cell = new MeasureCell(measure);
- table.put(component.uuid(), metricKey, cell);
- }
- changer.accept(cell.getMeasure());
- }
-
- /**
- * Round a measure value by applying the scale defined on the metric.
- * Example: scale(0.1234) returns 0.12 if metric scale is 2
- */
- private static double scale(MetricDto metric, double value) {
- if (metric.getDecimalScale() == null) {
- return value;
- }
- BigDecimal bd = BigDecimal.valueOf(value);
- return bd.setScale(metric.getDecimalScale(), RoundingMode.HALF_UP).doubleValue();
- }
-
- private static class MeasureCell {
- private final LiveMeasureDto measure;
- private final Double initialValue;
- private final byte[] initialData;
- private final String initialTextValue;
-
- private MeasureCell(LiveMeasureDto measure) {
- this.measure = measure;
- this.initialValue = measure.getValue();
- this.initialData = measure.getData();
- this.initialTextValue = measure.getTextValue();
- }
-
- public LiveMeasureDto getMeasure() {
- return measure;
- }
-
- public boolean isChanged() {
- return !Objects.equals(initialValue, measure.getValue()) || !Arrays.equals(initialData, measure.getData()) || !Objects.equals(initialTextValue, measure.getTextValue());
- }
- }
- }
|