/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package br.uff.ic.oceano.ostra.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.SoftwareProject; import br.uff.ic.oceano.core.model.Revision; import br.uff.ic.oceano.ostra.model.Item; import br.uff.ic.oceano.ostra.model.VersionedItem; import br.uff.ic.oceano.ostra.model.VersionedItemMetricValue; import br.uff.ic.oceano.ostra.controle.Constantes; import br.uff.ic.oceano.ostra.model.DataBaseSnapshot; import br.uff.ic.oceano.ostra.discretizer.Discretizer; import br.uff.ic.oceano.core.service.MetricValueService; import br.uff.ic.oceano.core.service.RevisionService; import br.uff.ic.oceano.core.service.controletransacao.Transacional; import br.uff.ic.oceano.ostra.discretizer.DayOfWeekDiscretizer; import static br.uff.ic.oceano.ostra.discretizer.DiscretizerFactory.getDiscretizer; import br.uff.ic.oceano.ostra.discretizer.HourOfDayDiscretizer; import br.uff.ic.oceano.ostra.discretizer.NumberOfFilesDiscretizer; import br.uff.ic.oceano.ostra.discretizer.RoundOfDayDiscretizer; import br.uff.ic.oceano.util.DateUtil; import br.uff.ic.oceano.util.NumberUtil; import br.uff.ic.oceano.util.Output; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author DanCastellani */ public class DeltaMetricsRevisionDataBaseService { private static final String ATTRIBUTE_INSTANCE_ID = "project-revision"; private static final String ATTRIBUTE_REVISION_DATE = "rdate"; private static final String ATTRIBUTE_REVISION_DATE_DAY_OF_WEEK = "rday"; private static final String ATTRIBUTE_REVISION_DATE_HOUR = "rhour"; private static final String ATTRIBUTE_REVISION_DATE_ROUND = "rRound"; private static final String ATTRIBUTE_REVISION_COMMITER = "rcommiter"; private static final String ATTRIBUTE_REVISION_COMPILES = "rcompile"; private static final String ATTRIBUTE_NUMBER_OF_CHANGED_FILES = Constantes.PREFIX_ATTRIBUTE_NUMBER + "files"; private static final String ATTRIBUTE_PREFIX_DELTA_AVG = Constantes.PREFIX_DELTA_METRIC_AVARAGE; private static final String ATTRIBUTE_PREFIX_DELTA_SD = Constantes.PREFIX_DELTA_METRIC_STANDARD_DEVIATON; // private Set<String> attributeNames; private Map<Item, Map<Metric, Double>> mapWithLastMetricValueForItem; private Map<Metric, Double> mapWithLastValueForEachMetric; private Map<Revision, Map<String, String>> instanceAttributes; private List<MetricValue> metricValuesToPersist; private RevisionService revisionService; private MetricValueService metricValueService; private DataBaseSnapshot dataBaseSnapshot; private boolean calculateStandardDeviation = false; private boolean usesOnlyCompilingRevisions = false; // private Map<String, Discretizer> discretizersMap; private boolean useDiscretizers = false; private List<Metric> metricsToConsider; public DeltaMetricsRevisionDataBaseService() { revisionService = ObjectFactory.getObjectWithDataBaseDependencies(RevisionService.class); metricValueService = ObjectFactory.getObjectWithDataBaseDependencies(MetricValueService.class); } public synchronized DataBaseSnapshot buildDeltaMetricsDataBase(List<SoftwareProject> projects, List<Discretizer> discretizers, boolean calculateStandardDeviation, boolean usesOnlyCompilingRevisions, List<Metric> metricsListToConsider, boolean calculateDeltaMetrics) throws ServiceException { this.calculateStandardDeviation = calculateStandardDeviation; this.usesOnlyCompilingRevisions = usesOnlyCompilingRevisions; this.metricsToConsider = metricsListToConsider; initialize(); dataBaseSnapshot = new DataBaseSnapshot(); for (SoftwareProject project : projects) { buildDeltaMetricsDataBase(project.getRevisions(), calculateDeltaMetrics); } saveMetricValuesForRevisions(); // System.out.println("--------------------------------------------------"); // System.out.println("attributeNames = " + attributeNames); // System.out.println("--------------------------------------------------"); dataBaseSnapshot.getAttributes().addAll(attributeNames); initializeDiscretizers(discretizers); //the instances must be create after all the processing. //This is needed 'cause may exist metrics that some instance doesn't have. //And we just know all the metrics after all the instances be processed. populateDataBaseSnapshotWithInstances(); return dataBaseSnapshot; } private void buildDeltaMetricsDataBase(Set<Revision> revisions, boolean calculateDeltaMetrics) throws ServiceException { List<Revision> revisionsList = new ArrayList<Revision>(revisions); Collections.sort(revisionsList); //>debuging //TODO remove after testing //revisionsList = revisionsList.subList(0, 30); //<debuging mapWithLastMetricValueForItem = new HashMap<Item, Map<Metric, Double>>(); mapWithLastValueForEachMetric = new HashMap<Metric, Double>(); for (Revision revision : revisionsList) { //skips revisions that doesnt compile boolean cannotCompile = revision.getCannotCompile() == null ? true : revision.getCannotCompile(); if (usesOnlyCompilingRevisions && cannotCompile) { continue; } //Output.println(">>>>>>>>> Processando revisão: " + revision); if (calculateDeltaMetrics) { revision = revisionService.getWithVersionedItemsAndItemsAndMetricValues(revision); } else { revision = revisionService.getWithChangedFiles(revision); } //instance id addAttributeValue(ATTRIBUTE_INSTANCE_ID, revision.getProject().getConfigurationItem().getName() + "-r" + revision.getNumber(), revision); final String commitDate = DateUtil.format(revision.getCommitDate()); addAttributeValue(ATTRIBUTE_REVISION_DATE, commitDate, revision); addAttributeValue(ATTRIBUTE_REVISION_DATE_DAY_OF_WEEK, commitDate, revision); addAttributeValue(ATTRIBUTE_REVISION_DATE_HOUR, commitDate, revision); addAttributeValue(ATTRIBUTE_REVISION_DATE_ROUND, commitDate, revision); //revision commiter addAttributeValue(ATTRIBUTE_REVISION_COMMITER, revision.getCommiter(), revision); //does it compiles if (!usesOnlyCompilingRevisions) { Boolean compiles = !(revision.getCannotCompile() != null && revision.getCannotCompile()); addAttributeValue(ATTRIBUTE_REVISION_COMPILES, compiles.toString(), revision); } //number of commited files if (revision.getChangedFiles() != null) { addAttributeValue(ATTRIBUTE_NUMBER_OF_CHANGED_FILES, revision.getChangedFiles().size() + "", revision); } else { addAttributeValue(ATTRIBUTE_NUMBER_OF_CHANGED_FILES, Constantes.ATTRIBUTE_NOT_KNOWN_SYMBOL, revision); } Output.println(">>>>>>>>>>>>>>>>>>>> gerando delta values for r-" + revision.getNumber()+"(" +revision+") #files "+ revision.getChangedFiles().size()); //delta metrics if (calculateDeltaMetrics) { calculateAndAddDeltaMetricValuesForMetricsOfVersionedItems(revision); } calculateDeltaForProjectMetrics(revision); } } private Double getLastItemMetricValue(final Item actualItem, Metric metricToCalculateDelta) { final Map<Metric, Double> mapWithMetricsAndValuesForActualItem = mapWithLastMetricValueForItem.get(actualItem); if (mapWithMetricsAndValuesForActualItem == null) { return 0d; } final Double metricValue = mapWithMetricsAndValuesForActualItem.get(metricToCalculateDelta); if (metricValue == null) { return 0d; } return metricValue; } private void initialize() { instanceAttributes = new LinkedHashMap<Revision, Map<String, String>>(); attributeNames = new LinkedHashSet<String>(); metricValuesToPersist = new LinkedList<MetricValue>(); } private void addAttributeValue(String attributeName, String attributeValue, Revision revision) { attributeNames.add(attributeName); if (!instanceAttributes.containsKey(revision)) { instanceAttributes.put(revision, new HashMap<String, String>()); } instanceAttributes.get(revision).put(attributeName, attributeValue); } private void calculateAndAddDeltaMetricValuesForMetricsOfVersionedItems(Revision revision) throws ServiceException { //Output.println("\nCalculating delta for revision: " + revision); Set<Metric> setOfMetricsToCalculatedTheDelta = new HashSet<Metric>(); Map<Metric, Set<VersionedItemMetricValue>> mapWithMetricValuesForEachMetricOfThisRevision = new HashMap<Metric, Set<VersionedItemMetricValue>>(); //get the metric to calculate its delta for (VersionedItem versionedItem : revision.getChangedFiles()) { for (VersionedItemMetricValue versionedItemMetricValue : versionedItem.getMetricValues()) { Metric extractedMetric = versionedItemMetricValue.getMetric(); if (!this.metricsToConsider.contains(extractedMetric)) { continue; } setOfMetricsToCalculatedTheDelta.add(extractedMetric); if (!mapWithMetricValuesForEachMetricOfThisRevision.containsKey(extractedMetric)) { mapWithMetricValuesForEachMetricOfThisRevision.put(extractedMetric, new HashSet<VersionedItemMetricValue>()); } mapWithMetricValuesForEachMetricOfThisRevision.get(extractedMetric).add(versionedItemMetricValue); } } //calculate and store the delta for each metric for (Metric metricToCalculateDelta : setOfMetricsToCalculatedTheDelta) { Set<VersionedItemMetricValue> setOfMetricValuesOfTheActualMetric = mapWithMetricValuesForEachMetricOfThisRevision.get(metricToCalculateDelta); double deltaValueForActualMetric = 0d; System.out.print("."); //calculates the sum of deltas for each item for (VersionedItemMetricValue versionedItemMetricValue : setOfMetricValuesOfTheActualMetric) { Double actualItemMetricValue = versionedItemMetricValue.getDoubleValue(); final char changeType = versionedItemMetricValue.getVersionedItem().getType(); final Item actualItem = versionedItemMetricValue.getVersionedItem().getItem(); final Double lastValueOfTheActualMetricForTheActualItem = getLastItemMetricValue(actualItem, metricToCalculateDelta); if (changeType == VersionedItem.TYPE_MODIFIED || changeType == VersionedItem.TYPE_REPLACED || changeType == VersionedItem.TYPE_ADDED || changeType == VersionedItem.TYPE_DELETED) { deltaValueForActualMetric += (actualItemMetricValue - lastValueOfTheActualMetricForTheActualItem); } else { throw new ServiceException("Change type " + changeType + " not knwon for versionedItem " + versionedItemMetricValue.getVersionedItem()); } updateLastMetricValueForActualItemAndMetric(actualItem, metricToCalculateDelta, actualItemMetricValue); } //calculate the average os the delta deltaValueForActualMetric /= setOfMetricValuesOfTheActualMetric.size(); markToSaveDeltaMetricAndAddItsAttribute(revision, metricToCalculateDelta, deltaValueForActualMetric, false); if (calculateStandardDeviation) { final Double sdValue = getStandardDeviationForMetric(setOfMetricValuesOfTheActualMetric, deltaValueForActualMetric); markToSaveDeltaMetricAndAddItsAttribute(revision, metricToCalculateDelta, sdValue, true); } } //>Debugging // Output.println("Metrics values of revision"); // for (Map.Entry<Metric, Set<VersionedItemMetricValue>> entry : mapWithMetricValuesForEachMetricOfThisRevision.entrySet()) { // Metric metric = entry.getKey(); // Output.println("Metric: " + metric.getName()); // // Set<VersionedItemMetricValue> set = entry.getValue(); // for (VersionedItemMetricValue versionedItemMetricValue : set) { // Output.println("Vers. item: " + versionedItemMetricValue.getDoubleValue() + "(" + NumberUtil.format(versionedItemMetricValue.getDoubleValue()) + ")"); // } // } // Output.println("Delta last value mapping result"); // for (Map.Entry<Item, Map<Metric, Double>> entry : mapWithLastMetricValueForItem.entrySet()) { // Output.print("Item: " + entry.getKey()+ "\t"); // for (Map.Entry<Metric, Double> entry2 : entry.getValue().entrySet()) { // Output.print(entry2.getKey() + ": " +entry2.getValue()+ "\t"); // } // } // // Output.println("Delta mapping result"); // for (Map.Entry<Revision, Map<String, String>> entry : instanceAttributes.entrySet()) { // Output.print("Revision: " + entry.getKey()+ "\t"); // final Map<String, String> mapAtt2Valu = entry.getValue(); // for (Map.Entry<String, String> entry2 : mapAtt2Valu.entrySet()) { // Output.print(entry2.getKey() + ": " +entry2.getValue() + "\t"); // } // Output.println(""); // } //<Debugging } private void updateLastMetricValueForActualItemAndMetric(final Item actualItem, Metric metricToCalculateDelta, Double actualItemMetricValue) { if (!mapWithLastMetricValueForItem.containsKey(actualItem)) { mapWithLastMetricValueForItem.put(actualItem, new HashMap<Metric, Double>()); } mapWithLastMetricValueForItem.get(actualItem).put(metricToCalculateDelta, actualItemMetricValue); } private void markToSaveDeltaMetricAndAddItsAttribute(final Revision revision, final Metric metric, final double value, final boolean standartDeviation) { if (standartDeviation) { addAttributeValue(ATTRIBUTE_PREFIX_DELTA_SD + metric.getName(), NumberUtil.format(value), revision); } else { final MetricValue deltaMetricValue = new MetricValue(); deltaMetricValue.setRevision(revision); deltaMetricValue.setMetric(metric); deltaMetricValue.setDoubleValue(value); deltaMetricValue.setDelta(true); metricValuesToPersist.add(deltaMetricValue); addAttributeValue(ATTRIBUTE_PREFIX_DELTA_AVG + metric.getName(), NumberUtil.format(value), revision); } } private Double getStandardDeviationForMetric(final Collection<VersionedItemMetricValue> versionedItemMetricValues, final double avgValue) { double varianceSum = 0d; // System.out.println("avgValue = " + avgValue); // System.out.println("deltaValues = " + deltaValues); for (VersionedItemMetricValue versionedItemMetricValue : versionedItemMetricValues) { double delta = versionedItemMetricValue.getDoubleValue(); varianceSum += Math.pow((delta - avgValue), 2); // System.out.println(" varianceSum = " + varianceSum); } if (versionedItemMetricValues.size() > 0) { final double variance = varianceSum / versionedItemMetricValues.size(); // System.out.println("variance = " + variance); // System.out.println("1. sd = " + Math.pow(variance, 2)); return Math.sqrt(variance); } else { // System.out.println("2. sd = " + 0); return 0D; } } /** * This method populates a snapshot of the actual processed database (just * the projects indicated) with all its metric values. When a metric value * is missing for a given revision it puts an known symbol that indicates * the missing value, otherwise put the respective metric value. */ private void populateDataBaseSnapshotWithInstances() { Output.println("Populating database snapshot"); for (Revision revision : instanceAttributes.keySet()) { Output.println("Revision: " + revision); final StringBuilder sb = new StringBuilder(); for (Iterator<String> it = attributeNames.iterator(); it.hasNext();) { final String attributeName = it.next(); final String attributeValue = instanceAttributes.get(revision).get(attributeName); if (attributeValue == null) { sb.append(Constantes.ATTRIBUTE_NOT_KNOWN_SYMBOL); } else { final Discretizer discret = discretizersMap.get(attributeName); if (useDiscretizers && discret != null) { final String result = discret.discretize(attributeValue); sb.append(result); Output.println(attributeName + ": " + attributeValue + "=>" + result); } else { sb.append(attributeValue); } } if (it.hasNext()) { sb.append(Constantes.ATTRIBUTE_SEPARATOR); } } Output.println(">> Instance: " + sb.toString()); dataBaseSnapshot.getInstances().add(sb.toString()); } } @Transacional private void saveMetricValuesForRevisions() { for (MetricValue metricValue : metricValuesToPersist) { try { metricValue.setId(metricValueService.getMetricValueId(metricValue)); } catch (ServiceException ex) { //nice! It's a new metricValue! =D } Output.println("metricValue = " + metricValue); metricValue.setDelta(true); metricValueService.save(metricValue); } } /** * Initializes the discretizer's list validating it and updating the * attribute's names when necessary. * * @param discretizers * @throws ServiceException */ private void initializeDiscretizers(List<Discretizer> discretizers) throws ServiceException { useDiscretizers = discretizers != null && !discretizers.isEmpty(); if (!useDiscretizers) { return; } validateDiscretizers(discretizers); this.discretizersMap = new HashMap<String, Discretizer>(); for (Discretizer discretizer : discretizers) { this.discretizersMap.put(discretizer.getAttributeTarget(), discretizer); } } /** * Verify if the discretizers list hasn't duplicated discretizers for the * same attribute or a discretizer references a target not known. * * @param discretizers * @throws ServiceException */ private void validateDiscretizers(List<Discretizer> discretizers) throws ServiceException { for (int i = 0; i < discretizers.size() - 1; i++) { final Discretizer discretizer = discretizers.get(i); if (!attributeNames.contains(discretizer.getAttributeTarget())) { final String msg = "Attribute target not known " + discretizer.getAttributeTarget() + " of " + discretizer.getClass().getCanonicalName(); Logger.getLogger(DeltaMetricsRevisionDataBaseService.class.getName()).log(Level.WARNING, msg); } for (int j = i + 1; j < discretizers.size(); j++) { final Discretizer anotherDiscretizer = discretizers.get(j); if (discretizer.getAttributeTarget().equals(anotherDiscretizer.getAttributeTarget())) { throw new ServiceException("Cant deal with two discretizers for the same attribute: " + discretizer.getAttributeTarget()); } } } } private void calculateDeltaForProjectMetrics(Revision revision) { //Output.println("\nCalculating Delta for Project Metrics of revision: "+revision); final List<MetricValue> metricValuesOfTheCurrentRevision = metricValueService.getByRevision(revision); for (MetricValue metricValue : metricValuesOfTheCurrentRevision) { final Metric metric = metricValue.getMetric(); if (!this.metricsToConsider.contains(metric)) { continue; }/// Output.print(metric.getName()+":"); Double deltaValue; if (metricValue.isDelta()) { //already is a delta value deltaValue = metricValue.getDoubleValue(); Output.println( "delta" + deltaValue); } else { //verifica se existe um mv que seja delta para a mesma métrica. //caso exista, continue para a proxima metrica. boolean hasADeltaMetricValue = false; for (final MetricValue mvo : metricValuesOfTheCurrentRevision) { if (mvo.isDelta() && mvo.getMetric().equals(metricValue.getMetric())) { hasADeltaMetricValue = true; break; } } if (hasADeltaMetricValue) { //Output.println("Metric already has delta value, continuing."); Output.println("delta exists"); continue; } final Double lastValue = mapWithLastValueForEachMetric.get(metricValue.getMetric()); //Output.println("Metric previous value:"+lastValue); Output.print("previous:"+lastValue); final Double currentValue = metricValue.getDoubleValue(); Output.print(" current:"+currentValue); if (lastValue == null || NumberUtil.isNAN(lastValue)) { //last value for metric not know, so new value is the delta. deltaValue = currentValue; } else if (currentValue == null || NumberUtil.isNAN(currentValue)) { //no new value for metric, so no changes. deltaValue = 0.0; } else { //last and current values known. deltaValue = currentValue - lastValue; } mapWithLastValueForEachMetric.put(metricValue.getMetric(), currentValue); markToSaveDeltaMetricAndAddItsAttribute(revision, metric, deltaValue, false); } final String strValue = String.valueOf(deltaValue); //Output.println("Metric delta value:"+deltaValue + "("+strValue+")"); Output.println(" delta:"+deltaValue); addAttributeValue(ATTRIBUTE_PREFIX_DELTA_AVG + metricValue.getMetric().getName(), strValue, revision); } } public static List<Discretizer> getDefaultDiscretizers() throws ServiceException { List<Discretizer> discretizers = new ArrayList<Discretizer>(); discretizers.add(getDiscretizer(ATTRIBUTE_NUMBER_OF_CHANGED_FILES, NumberOfFilesDiscretizer.class)); discretizers.add(getDiscretizer(ATTRIBUTE_REVISION_DATE_DAY_OF_WEEK, DayOfWeekDiscretizer.class)); discretizers.add(getDiscretizer(ATTRIBUTE_REVISION_DATE_HOUR, HourOfDayDiscretizer.class)); discretizers.add(getDiscretizer(ATTRIBUTE_REVISION_DATE_ROUND, RoundOfDayDiscretizer.class)); return discretizers; } }