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 */
020 package org.sonar.plugins.design.batch;
021
022 import com.google.common.collect.Lists;
023 import org.sonar.api.batch.Decorator;
024 import org.sonar.api.batch.DecoratorContext;
025 import org.sonar.api.batch.SonarIndex;
026 import org.sonar.api.measures.CoreMetrics;
027 import org.sonar.api.measures.Measure;
028 import org.sonar.api.measures.PersistenceMode;
029 import org.sonar.api.resources.Project;
030 import org.sonar.api.resources.Resource;
031 import org.sonar.api.resources.ResourceUtils;
032 import org.sonar.graph.*;
033
034 import java.util.Collection;
035 import java.util.List;
036 import 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 */
042 public 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 = (Project) 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 }