
org.sonar.plugins.java.bridges.DesignBridge Maven / Gradle / Ivy
/*
* Sonar Java
* Copyright (C) 2012 SonarSource
* [email protected]
*
* 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 02
*/
package org.sonar.plugins.java.bridges;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.design.Dependency;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rules.ActiveRule;
import org.sonar.api.rules.Violation;
import org.sonar.api.utils.TimeProfiler;
import org.sonar.graph.*;
import org.sonar.java.JavaSquid;
import org.sonar.java.checks.CycleBetweenPackagesCheck;
import org.sonar.squid.api.SourceCode;
import org.sonar.squid.api.SourceCodeEdge;
import org.sonar.squid.api.SourcePackage;
import org.sonar.squid.api.SourceProject;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class DesignBridge extends Bridge {
private static final Logger LOG = LoggerFactory.getLogger(DesignBridge.class);
/*
* This index is shared between onProject() and onPackage(). It works because onProject() is executed before onPackage().
*/
private DependencyIndex dependencyIndex = new DependencyIndex();
protected DesignBridge() {
super(true);
}
@Override
public void onProject(SourceProject squidProject, Project sonarProject) {
Set squidPackages = squidProject.getChildren();
if (squidPackages != null && !squidPackages.isEmpty()) {
TimeProfiler profiler = new TimeProfiler(LOG).start("Package design analysis");
LOG.debug("{} packages to analyze", squidPackages.size());
savePackageDependencies(squidPackages);
IncrementalCyclesAndFESSolver cyclesAndFESSolver = new IncrementalCyclesAndFESSolver(squid, squidPackages);
LOG.debug("{} cycles", cyclesAndFESSolver.getCycles().size());
Set feedbackEdges = cyclesAndFESSolver.getFeedbackEdgeSet();
LOG.debug("{} feedback edges", feedbackEdges.size());
int tangles = cyclesAndFESSolver.getWeightOfFeedbackEdgeSet();
saveViolations(feedbackEdges);
savePositiveMeasure(sonarProject, CoreMetrics.PACKAGE_CYCLES, cyclesAndFESSolver.getCycles().size());
savePositiveMeasure(sonarProject, CoreMetrics.PACKAGE_FEEDBACK_EDGES, feedbackEdges.size());
savePositiveMeasure(sonarProject, CoreMetrics.PACKAGE_TANGLES, tangles);
savePositiveMeasure(sonarProject, CoreMetrics.PACKAGE_EDGES_WEIGHT, getEdgesWeight(squidPackages));
String dsmJson = serializeDsm(squid, squidPackages, feedbackEdges);
Measure dsmMeasure = new Measure(CoreMetrics.DEPENDENCY_MATRIX, dsmJson).setPersistenceMode(PersistenceMode.DATABASE);
context.saveMeasure(sonarProject, dsmMeasure);
profiler.stop();
}
}
private void savePositiveMeasure(Resource sonarResource, Metric metric, double value) {
if (value >= 0.0) {
context.saveMeasure(sonarResource, metric, value);
}
}
@Override
public void onPackage(SourcePackage squidPackage, Resource sonarPackage) {
Set squidFiles = squidPackage.getChildren();
if (squidFiles != null && !squidFiles.isEmpty()) {
saveFileDependencies(squidFiles);
IncrementalCyclesAndFESSolver cycleDetector = new IncrementalCyclesAndFESSolver(squid, squidFiles);
Set cycles = cycleDetector.getCycles();
MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
Set feedbackEdges = solver.getEdges();
int tangles = solver.getWeightOfFeedbackEdgeSet();
savePositiveMeasure(sonarPackage, CoreMetrics.FILE_CYCLES, cycles.size());
savePositiveMeasure(sonarPackage, CoreMetrics.FILE_FEEDBACK_EDGES, feedbackEdges.size());
savePositiveMeasure(sonarPackage, CoreMetrics.FILE_TANGLES, tangles);
savePositiveMeasure(sonarPackage, CoreMetrics.FILE_EDGES_WEIGHT, getEdgesWeight(squidFiles));
String dsmJson = serializeDsm(squid, squidFiles, feedbackEdges);
context.saveMeasure(sonarPackage, new Measure(CoreMetrics.DEPENDENCY_MATRIX, dsmJson));
}
}
private double getEdgesWeight(Collection sourceCodes) {
List edges = squid.getEdges(sourceCodes);
double total = 0.0;
for (SourceCodeEdge edge : edges) {
total += edge.getWeight();
}
return total;
}
private String serializeDsm(JavaSquid squid, Set squidSources, Set feedbackEdges) {
Dsm dsm = new Dsm(squid, squidSources, feedbackEdges);
DsmTopologicalSorter.sort(dsm);
return DsmSerializer.serialize(dsm, dependencyIndex, resourceIndex);
}
/**
* Save package dependencies, including root file dependencies
*/
public void savePackageDependencies(Set squidPackages) {
for (SourceCode squidPackage : squidPackages) {
for (SourceCodeEdge edge : squid.getOutgoingEdges(squidPackage)) {
Dependency dependency = saveEdge(edge, context, null);
if (dependency != null) {
// save file dependencies
for (SourceCodeEdge subEdge : edge.getRootEdges()) {
saveEdge(subEdge, context, dependency);
}
}
}
}
}
private void saveViolations(Set feedbackEdges) {
ActiveRule rule = CycleBetweenPackagesCheck.getActiveRule(checkFactory);
if (rule == null) {
// Rule inactive
return;
}
for (Edge feedbackEdge : feedbackEdges) {
SourceCode fromPackage = (SourcePackage) feedbackEdge.getFrom();
SourceCode toPackage = (SourcePackage) feedbackEdge.getTo();
SourceCodeEdge edge = squid.getEdge(fromPackage, toPackage);
for (SourceCodeEdge subEdge : edge.getRootEdges()) {
Resource fromFile = resourceIndex.get(subEdge.getFrom());
Resource toFile = resourceIndex.get(subEdge.getTo());
// If resource cannot be obtained, then silently ignore, because anyway warning will be printed by method saveEdge
if ((fromFile != null) && (toFile != null)) {
Violation violation = Violation.create(rule, fromFile)
.setMessage("Remove the dependency on the source file \"" + toFile.getLongName() + "\" to break a package cycle.")
.setCost((double) subEdge.getWeight());
context.saveViolation(violation);
}
}
}
}
/**
* Save file dependencies
*/
public void saveFileDependencies(Set squidFiles) {
for (SourceCode squidFile : squidFiles) {
for (SourceCodeEdge edge : squid.getOutgoingEdges(squidFile)) {
saveEdge(edge, context, null);
}
}
}
private Dependency saveEdge(SourceCodeEdge edge, SensorContext context, Dependency parentDependency) {
Dependency dependency = dependencyIndex.get(edge);
if (dependency == null) {
Resource from = resourceIndex.get(edge.getFrom());
Resource to = resourceIndex.get(edge.getTo());
if (from != null && to != null) {
dependency = new Dependency(from, to).setUsage(edge.getUsage().name()).setWeight(edge.getWeight()).setParent(parentDependency);
context.saveDependency(dependency);
dependencyIndex.put(edge, dependency);
} else {
if (from == null) {
LOG.warn("Unable to find resource '" + edge.getFrom() + "' to create a dependency with '" + edge.getTo() + "'");
}
if (to == null) {
LOG.warn("Unable to find resource '" + edge.getTo() + "' to create a dependency with '" + edge.getFrom() + "'");
}
return null;
}
}
return dependency;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy