001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
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 */
020package org.sonar.plugins.design.batch;
021
022import com.google.common.collect.Lists;
023import org.sonar.api.batch.Decorator;
024import org.sonar.api.batch.DecoratorContext;
025import org.sonar.api.batch.SonarIndex;
026import org.sonar.api.measures.CoreMetrics;
027import org.sonar.api.measures.Measure;
028import org.sonar.api.measures.PersistenceMode;
029import org.sonar.api.resources.Project;
030import org.sonar.api.resources.Resource;
031import org.sonar.api.resources.ResourceUtils;
032import org.sonar.graph.*;
033
034import java.util.Collection;
035import java.util.List;
036import java.util.Set;
037
038/**
039 * For performance reasons, this decorator is currently limited to matrix between modules.
040 * Squid is optimized for cycle detections (better hashCode and equals methods of SourceCode classes than Resource).
041 */
042public class ProjectDsmDecorator implements Decorator {
043
044  // hack as long as DecoratorContext does not implement SonarIndex
045  private SonarIndex index;
046
047  public ProjectDsmDecorator(SonarIndex index) {
048    this.index = index;
049  }
050
051  public boolean shouldExecuteOnProject(Project project) {
052    return true;
053  }
054
055  public void decorate(final Resource resource, DecoratorContext context) {
056    if (shouldDecorateResource(resource, context)) {
057      Collection<Resource> subProjects = getSubProjects((Project) resource);
058
059      if (!subProjects.isEmpty()) {
060        Dsm<Resource> dsm = getDsm(subProjects);
061        saveDsm(context, dsm);
062      }
063    }
064  }
065
066  private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
067    Measure measure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm));
068    measure.setPersistenceMode(PersistenceMode.DATABASE);
069    context.saveMeasure(measure);
070  }
071
072  private Dsm<Resource> getDsm(Collection<Resource> subProjects) {
073    CycleDetector<Resource> cycleDetector = new CycleDetector<Resource>(index, subProjects);
074    Set<Cycle> cycles = cycleDetector.getCycles();
075
076    MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
077    Set<Edge> feedbackEdges = solver.getEdges();
078
079    Dsm<Resource> dsm = new Dsm<Resource>(index, subProjects, feedbackEdges);
080    DsmTopologicalSorter.sort(dsm);
081    return dsm;
082  }
083
084  /**
085   * sub-projects, including all descendants but not only direct children
086   */
087  private Collection<Resource> getSubProjects(final Project project) {
088    List<Resource> subProjects = Lists.newArrayList();
089    addSubProjects(project, subProjects);
090    return subProjects;
091  }
092
093  private void addSubProjects(Project project, List<Resource> subProjects) {
094    for (Project subProject : project.getModules()) {
095      Project indexedSubProject = index.getResource(subProject);
096      if (indexedSubProject != null) {
097        subProjects.add(indexedSubProject);
098      }
099      addSubProjects(subProject, subProjects);
100    }
101  }
102
103  private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
104    return ResourceUtils.isProject(resource) && context.getMeasure(CoreMetrics.DEPENDENCY_MATRIX) == null;
105  }
106}