/*
* SonarQube
* Copyright (C) 2009-2017 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.scanner.index;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputModule;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
import org.sonar.api.batch.fs.internal.InputComponentTree;
import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.MeasuresFilters;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.Metric.ValueType;
import org.sonar.api.resources.Directory;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
import org.sonar.scanner.scan.measure.MeasureCache;
import org.sonar.scanner.sensor.DefaultSensorStorage;
public class DefaultIndex {
private final InputComponentStore componentStore;
private final MeasureCache measureCache;
private final MetricFinder metricFinder;
// caches
private DefaultSensorStorage sensorStorage;
private InputComponentTree tree;
public DefaultIndex(InputComponentStore componentStore, InputComponentTree tree, MeasureCache measureCache, MetricFinder metricFinder) {
this.componentStore = componentStore;
this.tree = tree;
this.measureCache = measureCache;
this.metricFinder = metricFinder;
}
public void setCurrentStorage(DefaultSensorStorage sensorStorage) {
// the following components depend on the current module, so they need to be reloaded.
this.sensorStorage = sensorStorage;
}
@CheckForNull
public Measure getMeasure(String key, org.sonar.api.batch.measure.Metric<?> metric) {
return getMeasures(key, MeasuresFilters.metric(metric));
}
@CheckForNull
public <M> M getMeasures(String key, MeasuresFilter<M> filter) {
Collection<DefaultMeasure<?>> unfiltered = new ArrayList<>();
if (filter instanceof MeasuresFilters.MetricFilter) {
// optimization
DefaultMeasure<?> byMetric = measureCache.byMetric(key, ((MeasuresFilters.MetricFilter<M>) filter).filterOnMetricKey());
if (byMetric != null) {
unfiltered.add(byMetric);
}
} else {
for (DefaultMeasure<?> measure : measureCache.byComponentKey(key)) {
unfiltered.add(measure);
}
}
return filter.filter(unfiltered.stream().map(DefaultIndex::toDeprecated).collect(Collectors.toList()));
}
private static Measure toDeprecated(org.sonar.api.batch.sensor.measure.Measure<?> measure) {
Measure deprecatedMeasure = new Measure((Metric<?>) measure.metric());
setValueAccordingToMetricType(measure, deprecatedMeasure);
return deprecatedMeasure;
}
private static void setValueAccordingToMetricType(org.sonar.api.batch.sensor.measure.Measure<?> measure, Measure measureToSave) {
ValueType deprecatedType = ((Metric<?>) measure.metric()).getType();
switch (deprecatedType) {
case BOOL:
measureToSave.setValue(Boolean.TRUE.equals(measure.value()) ? 1.0 : 0.0);
break;
case INT:
case MILLISEC:
case WORK_DUR:
case FLOAT:
case PERCENT:
case RATING:
measureToSave.setValue(((Number) measure.value()).doubleValue());
break;
case STRING:
case LEVEL:
case DATA:
case DISTRIB:
measureToSave.setData((String) measure.value());
break;
default:
throw new UnsupportedOperationException("Unsupported type :" + deprecatedType);
}
}
public Measure addMeasure(String key, Measure measure) {
InputComponent component = componentStore.getByKey(key);
if (component == null) {
throw new IllegalStateException("Invalid component key: " + key);
}
if (sensorStorage.isDeprecatedMetric(measure.getMetricKey())) {
// Ignore deprecated metrics
return measure;
}
org.sonar.api.batch.measure.Metric<?> metric = metricFinder.findByKey(measure.getMetricKey());
if (metric == null) {
throw new UnsupportedOperationException("Unknown metric: " + measure.getMetricKey());
}
DefaultMeasure<?> newMeasure;
if (Boolean.class.equals(metric.valueType())) {
newMeasure = new DefaultMeasure<Boolean>().forMetric((Metric<Boolean>) metric)
.withValue(measure.getValue() != 0.0);
} else if (Integer.class.equals(metric.valueType())) {
newMeasure = new DefaultMeasure<Integer>().forMetric((Metric<Integer>) metric)
.withValue(measure.getValue().intValue());
} else if (Double.class.equals(metric.valueType())) {
newMeasure = new DefaultMeasure<Double>().forMetric((Metric<Double>) metric)
.withValue(measure.getValue());
} else if (String.class.equals(metric.valueType())) {
newMeasure = new DefaultMeasure<String>().forMetric((Metric<String>) metric)
.withValue(measure.getData());
} else if (Long.class.equals(metric.valueType())) {
newMeasure = new DefaultMeasure<Long>().forMetric((Metric<Long>) metric)
.withValue(measure.getValue().longValue());
} else {
throw new UnsupportedOperationException("Unsupported type :" + metric.valueType());
}
sensorStorage.saveMeasure(component, newMeasure);
return measure;
}
/**
* @param key Effective key, without branch
*/
@CheckForNull
public Resource getParent(String key) {
InputComponent component = componentStore.getByKey(key);
if (component == null) {
return null;
}
InputComponent parent = tree.getParent(component);
if (parent == null) {
return null;
}
return toResource(parent);
}
/**
* @param key Effective key, without branch
*/
public Collection<Resource> getChildren(String key) {
InputComponent component = componentStore.getByKey(key);
Collection<InputComponent> children = tree.getChildren(component);
return children.stream().map(this::toResource).collect(Collectors.toList());
}
public Resource toResource(InputComponent inputComponent) {
Resource r;
if (inputComponent instanceof InputDir) {
r = Directory.create(((InputDir) inputComponent).relativePath());
} else if (inputComponent instanceof InputFile) {
r = File.create(((InputFile) inputComponent).relativePath());
} else if (inputComponent instanceof InputModule) {
r = new Project(((DefaultInputModule) inputComponent).definition());
} else {
throw new IllegalArgumentException("Unknow input path type: " + inputComponent);
}
return r;
}
/**
* Gets a component from the store as a resource.
* @param key Effective key, without branch
*/
@CheckForNull
public Resource getResource(String key) {
InputComponent component = componentStore.getByKey(key);
if (component == null) {
return null;
}
return toResource(component);
}
}