rule.squid.UnusedProtectedMethod.name=Unused protected method
rule.squid.CommentedOutCodeLine.name=Avoid commented-out lines of code
+
+rule.squid.CycleBetweenPackages.name=Avoid cycle between java packages
--- /dev/null
+<p>
+When several packages are involved in a cycle (package A > package B > package C > package A where ">" means "depends upon"),
+that means that those packages are highly coupled and that there is no way to reuse/extract one of those packages without importing all the other packages.
+Such cycle could quickly increase the effort required to maintain an application and to embrace business change.
+Sonar not only detect cycles between packages but also determines what is the minimum effort to break those cycles.
+This rule log a violation on each source file having an outgoing dependency to be but in order to break a cycle.
+</p>
--- /dev/null
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.java.squid.check;
+
+import org.sonar.api.checks.CheckFactory;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+
+import javax.annotation.CheckForNull;
+
+/**
+ * Companion of {@link org.sonar.plugins.squid.bridges.DesignBridge} which actually does the job on finding cycles and creation of violations.
+ */
+@Rule(key = "CycleBetweenPackages", priority = Priority.MAJOR)
+public class CycleBetweenPackagesCheck extends SquidCheck {
+
+ /**
+ * @return null, if this check is inactive
+ */
+ @CheckForNull
+ public static ActiveRule getActiveRule(CheckFactory checkFactory) {
+ for (Object check : checkFactory.getChecks()) {
+ if (check.getClass().getName() == CycleBetweenPackagesCheck.class.getName()) {
+ return checkFactory.getActiveRule(check);
+ }
+ }
+ return null;
+ }
+
+}
UndocumentedApiCheck.class, ContinueCheck.class, BreakCheck.class,
// Squid checks
DITCheck.class, ClassComplexityCheck.class, MethodComplexityCheck.class, NoSonarCheck.class, EmptyFileCheck.class,
+ CycleBetweenPackagesCheck.class,
CommentedOutCodeLineCheck.class);
}
}
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.squid.check.CycleBetweenPackagesCheck;
import org.sonar.squid.Squid;
import org.sonar.squid.api.SourceCode;
import org.sonar.squid.api.SourceCodeEdge;
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);
}
}
+ private void saveViolations(Set<Edge> 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.");
+ context.saveViolation(violation);
+ }
+ }
+ }
+ }
+
/**
* Save file dependencies
*/