/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package br.uff.ic.oceano.core.tools.metrics.service;
import br.uff.ic.oceano.core.exception.ServiceException;
import br.uff.ic.oceano.core.factory.ObjectFactory;
import br.uff.ic.oceano.core.model.Metric;
import br.uff.ic.oceano.core.model.MetricValue;
import br.uff.ic.oceano.core.model.Revision;
import br.uff.ic.oceano.core.model.SoftwareProject;
import br.uff.ic.oceano.core.model.transiente.Language;
import br.uff.ic.oceano.core.service.MetricValueService;
import br.uff.ic.oceano.core.tools.compiler.CompilerService;
import br.uff.ic.oceano.core.tools.metrics.MetricManager;
import br.uff.ic.oceano.core.tools.revision.RevisionUtil;
import br.uff.ic.oceano.core.tools.maven.MavenUtil;
import br.uff.ic.oceano.util.NumberUtil;
import br.uff.ic.oceano.util.file.PathUtil;
import br.uff.ic.oceano.ostra.controle.Constantes;
import br.uff.ic.oceano.ostra.exception.CompilerException;
import br.uff.ic.oceano.ostra.model.VersionedItem;
import br.uff.ic.oceano.ostra.model.VersionedItemMetricValue;
import br.uff.ic.oceano.ostra.service.VersionedItemMetricValueService;
import br.uff.ic.oceano.util.Output;
import java.io.File;
import java.util.*;
/**
*
* @author DanCastellani
*/
public class MeasurementService {
private VersionedItemMetricValueService versionedItemMetricValueService;
private MetricValueService metricValueService;
public MeasurementService() {
versionedItemMetricValueService = ObjectFactory.getObjectWithDataBaseDependencies(VersionedItemMetricValueService.class);
metricValueService = ObjectFactory.getObjectWithDataBaseDependencies(MetricValueService.class);
}
/**
* This method extract all metrics from the parameter revision even if the
* metric's target isn't a project. If the metric's target is a package or
* file, the resultant metric is an average value for all the changedFiles.
*
* @param revision
* @param metricManagers
* @return
* @throws ServiceException
*/
public List<MetricValue> extractMetricsFromRevision(Revision revision, Collection<MetricManager> metricManagers) throws ServiceException {
return extractProjectMetrics(revision, metricManagers, false);
}
/**
* This method extracts the parameter metrics from the parameter revision,
* but only the project metrics.
*
* @param revision
* @param metricManagers
* @return
* @throws ServiceException
*/
public List<MetricValue> extractProjectMetricsOnly(Revision revision, Collection<MetricManager> metricManagers) throws ServiceException {
return extractProjectMetrics(revision, metricManagers, true);
}
private List<MetricValue> extractProjectMetrics(Revision revision, Collection<MetricManager> metricManagers, boolean dontExtractIfTargetIsNotProject) throws ServiceException {
List<MetricValue> metricValues = new ArrayList<MetricValue>(metricManagers.size());
for (MetricManager metricManager : metricManagers) {
if (metricManager.getMetric().getExtratcsFrom() != Metric.EXTRACTS_FROM_PROJECT) {
if (dontExtractIfTargetIsNotProject) {
continue;
}
Output.println("WARN[" + this.getClass().getSimpleName() + "] Extracting metric <" + metricManager.getMetric().getName() + "> from Project. But it's not from project by default.");
}
MetricValue value = extractMetric(metricManager, revision);
if (value != null) {
metricValues.add(value);
}
}
return metricValues;
}
/**
*
* @param revision
* @param metricManagers
* @param stopWithExceptions True if want to stop with a single exception.
* False if want to ignore if one metric throws exception and continue with
* the other.
*
* @return
* @throws ServiceException
*/
public List<VersionedItemMetricValue> extractMetricsFromVersionedItems(Revision revision, Collection<MetricManager> metricManagers, boolean stopWithExceptions) throws ServiceException {
Set<VersionedItem> versionedFiles;
Set<VersionedItem> versionedPackages;
try {
versionedFiles = RevisionUtil.get().getSourceFilesFromChangedFiles(revision);
versionedPackages = RevisionUtil.get().getPackagesFromChangedFiles(revision);
} catch (Exception ex) {
throw new ServiceException(ex);
}
List<VersionedItemMetricValue> viMetricValues = new ArrayList<VersionedItemMetricValue>(metricManagers.size() * versionedFiles.size());
//split metricManagers in files or packages
Collection<MetricManager> fileMetricManagers = new HashSet<MetricManager>();
Collection<MetricManager> packageMetricManagers = new HashSet<MetricManager>();
for (MetricManager metricManager : metricManagers) {
if (metricManager.getMetric().getExtratcsFrom() == Metric.EXTRACTS_FROM_FILE) {
fileMetricManagers.add(metricManager);
}
if (metricManager.getMetric().getExtratcsFrom() == Metric.EXTRACTS_FROM_PACKAGE) {
packageMetricManagers.add(metricManager);
}
}
//extract metrics from files
Output.println(" Arquivos a serem analisados: " + versionedFiles);
viMetricValues.addAll(extractVersionedItemMetricValues(versionedFiles, fileMetricManagers, stopWithExceptions));
//extract metrics from packages
Output.println(" Pacotes a serem analisados: " + versionedPackages.size());
viMetricValues.addAll(extractVersionedItemMetricValues(versionedPackages, packageMetricManagers, stopWithExceptions));
return viMetricValues;
}
/**
*
* @param versionedItems
* @param metricManagers
* @param stopWithExceptions True if want to stop with a single exception.
* False if want to ignore if one metric throws exception and continue with
* the other.
* @return
* @throws ServiceException
*/
private List<VersionedItemMetricValue> extractVersionedItemMetricValues(final Set<VersionedItem> versionedItems, final Collection<MetricManager> metricManagers, boolean stopWithExceptions) throws ServiceException {
List<VersionedItemMetricValue> viMetricValues = new LinkedList<VersionedItemMetricValue>();
for (MetricManager metricManager : metricManagers) {
try {
Output.println(" Extracting " + metricManager.getName());
viMetricValues.addAll(extractMetricValuesForVersionedItems(versionedItems, metricManager));
} catch (ServiceException ex) {
if (stopWithExceptions) {
throw ex;
} else {
Output.println("[ERROR] Metric " + metricManager.getName() + " threw exception: " + ex.getClass().getName());
Output.append("Exception: \n" + ex.getMessage() + "\n ------------ end of exception");
}
}
}
return viMetricValues;
}
private List<VersionedItemMetricValue> extractMetricValuesForVersionedItems(final Set<VersionedItem> versionedItems, final MetricManager metricManager) throws ServiceException {
List<VersionedItemMetricValue> metricValuesFromThisMetricManager = new ArrayList<VersionedItemMetricValue>(versionedItems.size());
for (VersionedItem versionedItem : versionedItems) {
VersionedItemMetricValue metricValue;
if (versionedItem.getType() == VersionedItem.TYPE_ADDED || versionedItem.getType() == VersionedItem.TYPE_MODIFIED) {
metricValue = this.extracMetricValueForVersionedItem(metricManager, versionedItem);
} else {
metricValue = versionedItemMetricValueService.createVersionedMetricWithZeroValue(metricManager, versionedItem);
}
if (metricValue != null) {
metricValuesFromThisMetricManager.add(metricValue);
}
}
return metricValuesFromThisMetricManager;
}
public VersionedItemMetricValue extracMetricValueForVersionedItem(MetricManager metricManager, VersionedItem versionedItem) throws ServiceException {
final Revision revision = versionedItem.getRevision();
String path = PathUtil.getWellFormedPath(revision.getLocalPath() + versionedItem.getItem().getPath());
MetricValue metricValueExtracted = extractMetric(metricManager, revision, path);
if (metricValueExtracted != null) {
final VersionedItemMetricValue returningMetricValueForVersionedItem = new VersionedItemMetricValue();
returningMetricValueForVersionedItem.setDoubleValue(metricValueExtracted.getDoubleValue());
returningMetricValueForVersionedItem.setMetric(metricValueExtracted.getMetric());
returningMetricValueForVersionedItem.setVersionedItem(versionedItem);
return returningMetricValueForVersionedItem;
} else {
return null;
}
}
/**
* This method extract a metric for one revision. If the metric target is a
* File or a Package it return the average value as the metricValue
*
* @param metricManager
* @param revision
* @return
* @throws CompilerException
* @throws ServiceException
*/
public static MetricValue extractMetric(MetricManager metricManager, Revision revision) throws ServiceException {
if (metricManager == null) {
throw new ServiceException("Null parameter: metricManager");
}
if (metricManager.getMetric() == null) {
throw new ServiceException("Metric not set");
}
int metricTarget = metricManager.getMetric().getExtratcsFrom();
if (metricTarget == Metric.EXTRACTS_FROM_PROJECT) {
return extractMetric(metricManager, revision, null);
}
MetricValue result = new MetricValue();
result.setMetric(metricManager.getMetric());
result.setDelta(false);
result.setRevision(revision);
result.setDoubleValue(0d);
Double acumulatedValue = 0d;
if (metricTarget == Metric.EXTRACTS_FROM_PACKAGE) {
try {
Collection<String> packages = RevisionUtil.get().getSourceClassPaths(revision);
for (String packagePath : packages) {
MetricValue value = extractMetric(metricManager, revision, packagePath);
if (value != null) {
acumulatedValue += value.getDoubleValue();
}
}
result.setDoubleValue(NumberUtil.roundDecimal(acumulatedValue));
} catch (Exception ex) {
throw new ServiceException("Fail to extract metric for revision: " + revision, ex);
}
} else if (metricTarget == Metric.EXTRACTS_FROM_FILE) {
try {
Collection<String> files = RevisionUtil.get().getSourceFiles(revision);
for (String filePath : files) {
MetricValue value = extractMetric(metricManager, revision, filePath);
if (value != null) {
acumulatedValue += value.getDoubleValue();
}
}
result.setDoubleValue(NumberUtil.roundDecimal(acumulatedValue));
} catch (Exception ex) {
throw new ServiceException("Fail to extract metric for revision: " + revision, ex);
}
} else {
throw new ServiceException("Unknown metric target:" + metricTarget);
}
return result;
}
/**
* This method extracts a metricValue for an especific Item, identified by
* the filePath.
*
* @param metricManager
* @param revision
* @param path
* @return
* @throws CompilerException
* @throws ServiceException
*/
public static MetricValue extractMetric(MetricManager metricManager, Revision revision, String path) throws CompilerException, ServiceException {
//Fix path
if (path != null && path.isEmpty()) {
throw new ServiceException("Variable path is empty. Set path to null or valid path");
}
//Fixed file separators
path = path != null ? PathUtil.getWellFormedPath(path) : null;
path = PathUtil.getAbsolutePathFromRelativetoCurrentPath(path);
String revisionPath = revision.getLocalPath();
revisionPath = revisionPath != null ? PathUtil.getWellFormedPath(revisionPath) : null;
revisionPath = PathUtil.getAbsolutePathFromRelativetoCurrentPath(revisionPath);
//Add revisionPath when necessary
String fixedPath;
if (path != null && !path.contains(revisionPath)) {
fixedPath = revisionPath + path;
} else if (path != null) {
fixedPath = path;
} else {
//Use revision path when no path supplied
fixedPath = revisionPath;
}
final Metric metricToExtract = metricManager.getMetric();
if (!metricToExtract.isExtractsFromFont()) {
if (revision.getProject().isMavenProject()) {
fixedPath = fixedPath.replace(MavenUtil.MAVEN2_BASE_MAIN_SOURCE_FILES, MavenUtil.MAVEN2_BASE_COMPILED_FILES);
}
if (revision.getProject().getLanguage().equals(Language.JAVA)) {
fixedPath = fixedPath.replace(Constantes.DOT_JAVA, Constantes.DOT_CLASS);
}
}
//validating
final File file = new File(fixedPath);
if (!file.exists()) {
if (metricToExtract.isExtractsFromFont()) {
throw new ServiceException("The file [" + fixedPath + "] must exist!");
} else {
return MetricValue.createMetricValueWithZero(revision, metricToExtract);
//Its a compiled metric and if the .java file is all comented will be no class for it.
//Things will be like this 'cause i fond that problem and don't know a better way to get it done right now.
}
}
if (metricToExtract.getExtratcsFrom() == Metric.EXTRACTS_FROM_FILE && !file.isFile()) {
throw new ServiceException("The metric " + metricToExtract.getName() + " can only be extracted from a file! " + fixedPath);
}
if (metricToExtract.getExtratcsFrom() == Metric.EXTRACTS_FROM_PACKAGE && !file.isDirectory()) {
throw new ServiceException("The metric " + metricToExtract.getName() + " can only be extracted from a package! " + fixedPath);
}
if (metricToExtract.getExtratcsFrom() == Metric.EXTRACTS_FROM_PROJECT && !file.isDirectory()) {
throw new ServiceException("The metric " + metricToExtract.getName() + " can only be extracted from a project! " + fixedPath);
}
//must compile the revision and then extract the metric.
if (!metricToExtract.isExtractsFromFont() && revision.getProject().getLanguage().equals(Language.JAVA)) {
CompilerService.compile(revision);
}
try {
return metricManager.extractMetric(revision, fixedPath);
} catch (Exception ex) {
throw new ServiceException(ex);
} catch (Throwable tw) {
//Don't think it's the best way, but any Exception or Runtime exception could come.
throw new ServiceException(tw);
}
}
public boolean isMeasured(MetricManager metricManager, Revision revision) throws ServiceException {
switch (metricManager.getMetric().getExtratcsFrom()) {
case Metric.EXTRACTS_FROM_FILE:
return versionedItemMetricValueService.isMeasured(revision, metricManager.getMetric());
case Metric.EXTRACTS_FROM_PACKAGE:
return versionedItemMetricValueService.isMeasured(revision, metricManager.getMetric());
case Metric.EXTRACTS_FROM_PROJECT:
return metricValueService.isMeasured(revision, metricManager.getMetric());
}
return false;
}
public Double getAvgValue(Metric metric, SoftwareProject softwareProject) throws ServiceException {
switch (metric.getExtratcsFrom()) {
case Metric.EXTRACTS_FROM_FILE:
case Metric.EXTRACTS_FROM_PACKAGE:
double total = 0;
double sum = 0;
for (Revision revision : softwareProject.getRevisions()) {
List<VersionedItemMetricValue> valuesTemp = versionedItemMetricValueService.getByRevisionAndMetric(revision, metric);
total += valuesTemp.size();
for (VersionedItemMetricValue versionedItemMetricValue : valuesTemp) {
Double value = versionedItemMetricValue.getDoubleValue();
if (value != null && !NumberUtil.isNAN(value)) {
sum += value;
}
}
}
return NumberUtil.ratio(sum, total);
case Metric.EXTRACTS_FROM_PROJECT:
List<MetricValue> valuesTemp = metricValueService.getAbsoluteValuesByProjectAndMetric(softwareProject, metric);
total = valuesTemp.size();
sum = 0;
for (MetricValue metricValue : valuesTemp) {
Double value = metricValue.getDoubleValue();
if (value != null && !NumberUtil.isNAN(value)) {
sum += value;
}
}
return NumberUtil.ratio(sum, total);
}
return null;
}
}