summaryrefslogtreecommitdiffstats
path: root/plugins/sonar-design-plugin
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/sonar-design-plugin')
-rw-r--r--plugins/sonar-design-plugin/pom.xml95
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/DesignPlugin.java84
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/DsmSerializer.java92
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/FileTangleIndexDecorator.java34
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/MavenDependenciesSensor.java126
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/OldDependenciesPurge.java44
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/PackageTangleIndexDecorator.java34
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/ProjectDsmDecorator.java103
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecorator.java90
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/TangleIndexDecorator.java91
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/GwtDependenciesTab.java45
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Data.java64
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTab.java98
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTable.java97
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Header.java75
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/I18nConstants.java52
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/GwtLcom4Tab.java42
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Data.java52
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Lcom4Tab.java104
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/GwtLibrariesPage.java41
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Filters.java116
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/I18nConstants.java45
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/KeywordFilter.java44
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/LibrariesPage.java103
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Library.java98
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/ProjectPanel.java130
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/GwtDesignPage.java43
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DependencyInfo.java180
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DesignPage.java72
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/Dsm.java366
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DsmData.java65
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/I18nConstants.java51
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/ChidamberKemererWidget.java40
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/FileDesignWidget.java40
-rw-r--r--plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/PackageDesignWidget.java40
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/DependenciesTab.gwt.xml13
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/client/I18nConstants_fr.properties7
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/dependencies-tab.css23
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/test.html31
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/Lcom4Tab.gwt.xml13
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.css23
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.html28
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/LibrariesPage.gwt.xml13
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/client/I18nConstants_fr.properties6
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/libraries.css22
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/test.html33
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/DesignPage.gwt.xml12
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/client/I18nConstants_fr.properties8
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/design.css227
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/green-stripes.pngbin0 -> 117 bytes
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/test.html27
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/yellow-stripes.pngbin0 -> 111 bytes
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/chidamber_kemerer.html.erb50
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/file_design.html.erb32
-rw-r--r--plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/package_design.html.erb38
-rw-r--r--plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/DsmSerializerTest.java60
-rw-r--r--plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/MavenDependenciesSensorTest.java27
-rw-r--r--plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/OldDependenciesPurgeTest.java40
-rw-r--r--plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecoratorTest.java62
-rw-r--r--plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/DsmSerializerTest/dsm.json1
-rw-r--r--plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/MavenDependenciesSensorTest/getEffectiveLibraries/pom.xml29
-rw-r--r--plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies-result.xml102
-rw-r--r--plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies.xml102
63 files changed, 3925 insertions, 0 deletions
diff --git a/plugins/sonar-design-plugin/pom.xml b/plugins/sonar-design-plugin/pom.xml
new file mode 100644
index 00000000000..9c577fbb325
--- /dev/null
+++ b/plugins/sonar-design-plugin/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <version>2.3-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-design-plugin</artifactId>
+ <packaging>sonar-plugin</packaging>
+ <name>Sonar :: Plugins :: Design</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-gwt-api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-user</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-incubator</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- unit tests -->
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-testing-harness</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>design</pluginKey>
+ <pluginName>Design</pluginName>
+ <pluginClass>org.sonar.plugins.design.DesignPlugin</pluginClass>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>gwt-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <configuration>
+ <modules>
+ <module>org.sonar.plugins.design.ui.lcom4.Lcom4Tab</module>
+ <module>org.sonar.plugins.design.ui.page.DesignPage</module>
+ <module>org.sonar.plugins.design.ui.dependencies.DependenciesTab</module>
+ <module>org.sonar.plugins.design.ui.libraries.LibrariesPage</module>
+ </modules>
+ <skip>${skipGwt}</skip>
+ <webappDirectory>${project.build.directory}/classes</webappDirectory>
+
+ <!-- do not break on two lines -->
+ <extraJvmArgs>-Xmx512m -Dgwt.jjs.permutationWorkerFactory=com.google.gwt.dev.ThreadedPermutationWorkerFactory</extraJvmArgs>
+ </configuration>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/DesignPlugin.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/DesignPlugin.java
new file mode 100644
index 00000000000..90fe463ed82
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/DesignPlugin.java
@@ -0,0 +1,84 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design;
+
+import org.sonar.api.*;
+import org.sonar.plugins.design.batch.*;
+import org.sonar.plugins.design.ui.dependencies.GwtDependenciesTab;
+import org.sonar.plugins.design.ui.lcom4.GwtLcom4Tab;
+import org.sonar.plugins.design.ui.page.GwtDesignPage;
+import org.sonar.plugins.design.ui.widgets.ChidamberKemererWidget;
+import org.sonar.plugins.design.ui.widgets.FileDesignWidget;
+import org.sonar.plugins.design.ui.widgets.PackageDesignWidget;
+import org.sonar.plugins.design.ui.libraries.GwtLibrariesPage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Properties({
+ @Property(
+ key = CoreProperties.DESIGN_SKIP_DESIGN_PROPERTY,
+ defaultValue = "" + CoreProperties.DESIGN_SKIP_DESIGN_DEFAULT_VALUE,
+ name = "Skip design analysis",
+ project = true,
+ global = true)
+})
+public class DesignPlugin implements Plugin {
+
+ public String getKey() {
+ return "design";
+ }
+
+ public String getName() {
+ return "Design";
+ }
+
+ public String getDescription() {
+ return "";
+ }
+
+ public List<Class<? extends Extension>> getExtensions() {
+ List<Class<? extends Extension>> extensions = new ArrayList<Class<? extends Extension>>();
+
+ // Batch
+ extensions.add(MavenDependenciesSensor.class);
+ extensions.add(ProjectDsmDecorator.class);
+ extensions.add(PackageTangleIndexDecorator.class);
+ extensions.add(FileTangleIndexDecorator.class);
+ extensions.add(OldDependenciesPurge.class);
+ extensions.add(SuspectLcom4DensityDecorator.class);
+ extensions.add(GwtLibrariesPage.class);
+
+ // UI
+ extensions.add(GwtDesignPage.class);
+ extensions.add(GwtDependenciesTab.class);
+ extensions.add(FileDesignWidget.class);
+ extensions.add(PackageDesignWidget.class);
+ extensions.add(ChidamberKemererWidget.class);
+ extensions.add(GwtLcom4Tab.class);
+
+ return extensions;
+ }
+
+ @Override
+ public String toString() {
+ return getKey();
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/DsmSerializer.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/DsmSerializer.java
new file mode 100644
index 00000000000..6ce0e006e57
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/DsmSerializer.java
@@ -0,0 +1,92 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.sonar.api.design.Dependency;
+import org.sonar.api.resources.Resource;
+import org.sonar.graph.Dsm;
+import org.sonar.graph.DsmCell;
+
+public final class DsmSerializer {
+
+ private Dsm dsm;
+ private StringBuilder json;
+
+ private DsmSerializer(Dsm<Resource> dsm) {
+ this.dsm = dsm;
+ this.json = new StringBuilder();
+ }
+
+ private String serialize() {
+ json.append('[');
+ serializeRows();
+ json.append(']');
+ return json.toString();
+ }
+
+ private void serializeRows() {
+ for (int y = 0; y < dsm.getDimension(); y++) {
+ if (y > 0) {
+ json.append(',');
+ }
+ serializeRow(y);
+ }
+ }
+
+ private void serializeRow(int y) {
+ Resource resource = (Resource) dsm.getVertex(y);
+
+ json.append("{");
+ if (resource != null) {
+ json.append("\"i\":");
+ json.append(resource.getId());
+ json.append(",\"n\":\"");
+ json.append(resource.getName());
+ json.append("\",\"q\":\"");
+ json.append(resource.getQualifier());
+ json.append("\",\"v\":[");
+ for (int x = 0; x < dsm.getDimension(); x++) {
+ if (x > 0) {
+ json.append(',');
+ }
+ serializeCell(y, x);
+ }
+ json.append("]");
+ }
+ json.append("}");
+ }
+
+ private void serializeCell(int y, int x) {
+ DsmCell cell = dsm.getCell(x, y);
+ json.append('{');
+ if (cell.getEdge() != null && cell.getWeight() > 0) {
+ Dependency dep = (Dependency) cell.getEdge();
+ json.append("\"i\":");
+ json.append(dep.getId());
+ json.append(",\"w\":");
+ json.append(cell.getWeight());
+ }
+ json.append('}');
+ }
+
+ public static String serialize(Dsm<Resource> dsm) {
+ return new DsmSerializer(dsm).serialize();
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/FileTangleIndexDecorator.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/FileTangleIndexDecorator.java
new file mode 100644
index 00000000000..fa449e0887e
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/FileTangleIndexDecorator.java
@@ -0,0 +1,34 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.sonar.api.measures.CoreMetrics;
+
+public class FileTangleIndexDecorator extends TangleIndexDecorator {
+
+ public FileTangleIndexDecorator() {
+ super(CoreMetrics.FILE_TANGLES, CoreMetrics.FILE_EDGES_WEIGHT, CoreMetrics.FILE_TANGLE_INDEX);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/MavenDependenciesSensor.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/MavenDependenciesSensor.java
new file mode 100644
index 00000000000..dea1bfceb7d
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/MavenDependenciesSensor.java
@@ -0,0 +1,126 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactCollector;
+import org.apache.maven.shared.dependency.tree.DependencyNode;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
+import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
+import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
+import org.apache.maven.shared.dependency.tree.filter.StateDependencyNodeFilter;
+import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
+import org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.resources.Library;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.SonarException;
+
+public class MavenDependenciesSensor implements Sensor {
+
+ private ArtifactRepository localRepository;
+ private ArtifactFactory artifactFactory;
+ private ArtifactMetadataSource artifactMetadataSource;
+ private ArtifactCollector artifactCollector;
+ private DependencyTreeBuilder treeBuilder;
+ private SonarIndex index;
+
+ public MavenDependenciesSensor(ArtifactRepository localRepository, ArtifactFactory artifactFactory, ArtifactMetadataSource artifactMetadataSource, ArtifactCollector artifactCollector, DependencyTreeBuilder treeBuilder, SonarIndex index) {
+ this.localRepository = localRepository;
+ this.artifactFactory = artifactFactory;
+ this.artifactMetadataSource = artifactMetadataSource;
+ this.artifactCollector = artifactCollector;
+ this.index = index;
+ this.treeBuilder = treeBuilder;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void analyse(final Project project, final SensorContext context) {
+ try {
+ DependencyNode root = treeBuilder.buildDependencyTree(project.getPom(), localRepository, artifactFactory, artifactMetadataSource, null, artifactCollector);
+
+ DependencyNodeVisitor visitor = new BuildingDependencyNodeVisitor(new DependencyNodeVisitor() {
+ public boolean visit(DependencyNode node) {
+ return true;
+ }
+
+ public boolean endVisit(DependencyNode node) {
+ if (node.getParent() != null && node.getParent() != node) {
+ saveDependency(node, project, context);
+ }
+ return true;
+ }
+ });
+
+ // mode verbose OFF : do not show the same lib many times
+ DependencyNodeFilter filter = StateDependencyNodeFilter.INCLUDED;
+
+ CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
+ DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor(collectingVisitor, filter);
+ root.accept(firstPassVisitor);
+
+ DependencyNodeFilter secondPassFilter = new AncestorOrSelfDependencyNodeFilter(collectingVisitor.getNodes());
+ visitor = new FilteringDependencyNodeVisitor(visitor, secondPassFilter);
+
+ root.accept(visitor);
+
+ } catch (DependencyTreeBuilderException e) {
+ throw new SonarException("Can not load the graph of dependencies of the project " + project.getKey(), e);
+ }
+ }
+
+ protected void saveDependency(DependencyNode node, Project project, SensorContext context) {
+ Resource from = (node.getParent().getParent()==null) ? index.getProject() : toResource(node.getParent().getArtifact(), context);
+ Resource to = toResource(node.getArtifact(), context);
+ Dependency dependency = new Dependency(from, to);
+ dependency.setUsage(node.getArtifact().getScope());
+ dependency.setWeight(1);
+ context.saveDependency(dependency);
+ }
+
+ protected static Resource toResource(Artifact artifact, SensorContext context) {
+ Project project = Project.createFromMavenIds(artifact.getGroupId(), artifact.getArtifactId());
+ Resource result = context.getResource(project);
+ if (result == null || !((Project) result).getAnalysisVersion().equals(artifact.getBaseVersion())) {
+ Library lib = new Library(project.getKey(), artifact.getBaseVersion());
+ context.saveResource(lib);
+ result = context.getResource(lib);
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Maven dependencies";
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/OldDependenciesPurge.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/OldDependenciesPurge.java
new file mode 100644
index 00000000000..2a17e576721
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/OldDependenciesPurge.java
@@ -0,0 +1,44 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.design.DependencyDto;
+import org.sonar.core.purge.AbstractPurge;
+
+import javax.persistence.Query;
+import java.util.List;
+
+public class OldDependenciesPurge extends AbstractPurge {
+
+ public OldDependenciesPurge(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ Query query = getSession().createQuery("SELECT d.projectSnapshotId FROM " + DependencyDto.class.getSimpleName() +
+ " d WHERE NOT EXISTS(FROM " + Snapshot.class.getSimpleName() + " s WHERE s.id=d.projectSnapshotId AND s.last=:last)");
+ query.setParameter("last", true);
+ final List<Integer> projectSnapshotIds = query.getResultList();
+ executeQuery(projectSnapshotIds, "DELETE FROM " + DependencyDto.class.getSimpleName() + " WHERE projectSnapshotId in (:ids)");
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/PackageTangleIndexDecorator.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/PackageTangleIndexDecorator.java
new file mode 100644
index 00000000000..fa4e440fbe5
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/PackageTangleIndexDecorator.java
@@ -0,0 +1,34 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.sonar.api.measures.CoreMetrics;
+
+public class PackageTangleIndexDecorator extends TangleIndexDecorator {
+
+ public PackageTangleIndexDecorator() {
+ super(CoreMetrics.PACKAGE_TANGLES, CoreMetrics.PACKAGE_EDGES_WEIGHT, CoreMetrics.PACKAGE_TANGLE_INDEX);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/ProjectDsmDecorator.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/ProjectDsmDecorator.java
new file mode 100644
index 00000000000..0f040a975a4
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/ProjectDsmDecorator.java
@@ -0,0 +1,103 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import com.google.common.collect.Lists;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.graph.*;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * For performance reasons, this decorator is currently limited to matrix between modules.
+ * Squid is optimized for cycle detections (better hashCode and equals methods of SourceCode classes than Resource).
+ */
+public class ProjectDsmDecorator implements Decorator {
+
+ // hack as long as DecoratorContext does not implement SonarIndex
+ private SonarIndex index;
+
+ public ProjectDsmDecorator(SonarIndex index) {
+ this.index = index;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(final Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(resource, context)) {
+ Collection<Resource> subProjects = getSubProjects((Project) resource);
+
+ if (!subProjects.isEmpty()) {
+ Dsm<Resource> dsm = getDsm(subProjects);
+ saveDsm(context, dsm);
+ }
+ }
+ }
+
+ private void saveDsm(DecoratorContext context, Dsm<Resource> dsm) {
+ context.saveMeasure(new Measure(CoreMetrics.DEPENDENCY_MATRIX, DsmSerializer.serialize(dsm)));
+ }
+
+ private Dsm<Resource> getDsm(Collection<Resource> subProjects) {
+ CycleDetector<Resource> cycleDetector = new CycleDetector<Resource>(index, subProjects);
+ Set<Cycle> cycles = cycleDetector.getCycles();
+
+ MinimumFeedbackEdgeSetSolver solver = new MinimumFeedbackEdgeSetSolver(cycles);
+ Set<Edge> feedbackEdges = solver.getEdges();
+
+ Dsm<Resource> dsm = new Dsm<Resource>(index, subProjects, feedbackEdges);
+ DsmTopologicalSorter.sort(dsm);
+ return dsm;
+ }
+
+ /**
+ * sub-projects, including all descendants but not only direct children
+ */
+ private Collection<Resource> getSubProjects(final Project project) {
+ List<Resource> subProjects = Lists.newArrayList();
+ addSubProjects(project, subProjects);
+ return subProjects;
+ }
+
+ private void addSubProjects(Project project, List<Resource> subProjects) {
+ for (Project subProject : project.getModules()) {
+ Project indexedSubProject = (Project) index.getResource(subProject);
+ if (indexedSubProject != null) {
+ subProjects.add(indexedSubProject);
+ }
+ addSubProjects(subProject, subProjects);
+ }
+ }
+
+ private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return ResourceUtils.isProject(resource) && context.getMeasure(CoreMetrics.DEPENDENCY_MATRIX) == null;
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecorator.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecorator.java
new file mode 100644
index 00000000000..35370a02d2f
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecorator.java
@@ -0,0 +1,90 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+import java.util.Collection;
+import java.util.List;
+
+public class SuspectLcom4DensityDecorator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public final Metric generatesMetric() {
+ return CoreMetrics.SUSPECT_LCOM4_DENSITY;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (ResourceUtils.isFile(resource)) {
+ // do nothing
+ } else if (ResourceUtils.isDirectory(resource)) {
+ decorateDirectory(context);
+
+ } else {
+ decorateProject(context);
+ }
+ }
+
+ private void decorateProject(DecoratorContext context) {
+ double total = 0.0;
+ int totalFiles = 0;
+
+ List<DecoratorContext> children = context.getChildren();
+ for (DecoratorContext child : children) {
+ int files = MeasureUtils.getValue(child.getMeasure(CoreMetrics.FILES), 0.0).intValue();
+ totalFiles += files;
+ total += MeasureUtils.getValue(child.getMeasure(CoreMetrics.SUSPECT_LCOM4_DENSITY), 0.0) * files;
+ }
+
+ if (totalFiles > 0) {
+ context.saveMeasure(CoreMetrics.SUSPECT_LCOM4_DENSITY, (total / totalFiles));
+ }
+ }
+
+ private void decorateDirectory(DecoratorContext context) {
+ double files = MeasureUtils.getValue(context.getMeasure(CoreMetrics.FILES), 0.0);
+ if (files > 0.0) {
+ double suspectFiles = 0.0;
+
+ // directory children are files
+ Collection<Measure> fileLcoms = context.getChildrenMeasures(CoreMetrics.LCOM4);
+ for (Measure fileLcom : fileLcoms) {
+ if (MeasureUtils.getValue(fileLcom, 0.0) > 1.0) {
+ suspectFiles++;
+ }
+ }
+ double density = (suspectFiles / files) * 100.0;
+ context.saveMeasure(CoreMetrics.SUSPECT_LCOM4_DENSITY, density);
+ }
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/TangleIndexDecorator.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/TangleIndexDecorator.java
new file mode 100644
index 00000000000..703bd6334ad
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/batch/TangleIndexDecorator.java
@@ -0,0 +1,91 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class TangleIndexDecorator implements Decorator {
+
+ private Metric tanglesMetric;
+ private Metric edgesWeightMetric;
+ private Metric tangleIndexMetric;
+
+ protected TangleIndexDecorator(Metric tanglesMetric, Metric edgesWeightMetric, Metric tangleIndexMetric) {
+ this.tanglesMetric = tanglesMetric;
+ this.edgesWeightMetric = edgesWeightMetric;
+ this.tangleIndexMetric = tangleIndexMetric;
+ }
+
+ @DependsUpon
+ public final List<Metric> dependsUponMetrics() {
+ return Arrays.asList(tanglesMetric, edgesWeightMetric);
+ }
+
+ /**
+ * Used to define downstream dependencies
+ */
+ @DependedUpon
+ public final Metric generatesMetric() {
+ return tangleIndexMetric;
+ }
+
+ public final boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void decorate(Resource resource, DecoratorContext context) {
+ if (!shouldDecorateResource(context)) {
+ return;
+ }
+ Measure tangles = context.getMeasure(tanglesMetric);
+ Measure totalweight = context.getMeasure(edgesWeightMetric);
+
+ if (MeasureUtils.hasValue(totalweight)) {
+ context.saveMeasure(new Measure(tangleIndexMetric, compute(MeasureUtils.getValue(tangles, 0.0), totalweight.getValue())));
+ }
+ }
+
+ private boolean shouldDecorateResource(DecoratorContext context) {
+ return context.getMeasure(tangleIndexMetric) == null;
+ }
+
+
+ private double compute(double tangles, double totalWeight) {
+ if (totalWeight==0.0) {
+ return 0.0;
+ }
+ double result = 2 * tangles / totalWeight;
+ return result * 100;
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/GwtDependenciesTab.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/GwtDependenciesTab.java
new file mode 100644
index 00000000000..f49bffdae1b
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/GwtDependenciesTab.java
@@ -0,0 +1,45 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.dependencies;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Java;
+
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.design.ui.dependencies.client.DependenciesTab;
+
+@ResourceLanguage({Java.KEY, "web"}) // 'web' is a temporary workaround. See http://sonar-dev.787459.n3.nabble.com/sonar-dev-Dependencies-in-Web-Plugin-td822980.html#a822980
+@ResourceQualifier({Resource.QUALIFIER_FILE, Resource.QUALIFIER_CLASS, Resource.QUALIFIER_PACKAGE, Resource.QUALIFIER_PROJECT, Resource.QUALIFIER_MODULE})
+@DefaultTab(metrics={CoreMetrics.AFFERENT_COUPLINGS_KEY, CoreMetrics.EFFERENT_COUPLINGS_KEY})
+@NavigationSection({NavigationSection.RESOURCE_TAB})
+@UserRole(UserRole.USER)
+public class GwtDependenciesTab extends GwtPage {
+
+ public String getTitle() {
+ return "Dependencies";
+ }
+
+ public String getGwtId() {
+ return DependenciesTab.GWT_ID;
+ }
+}
+
+
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Data.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Data.java
new file mode 100644
index 00000000000..2d457de0347
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Data.java
@@ -0,0 +1,64 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.dependencies.client;
+
+import org.sonar.wsclient.services.Dependency;
+import org.sonar.wsclient.services.Resource;
+
+import java.util.List;
+
+public class Data {
+
+ private long resourceId;
+ private List<Dependency> dependencies = null;
+ private Resource resource = null;
+
+ public Data(long resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public long getResourceId() {
+ return resourceId;
+ }
+
+ public boolean isLoaded() {
+ return dependencies!=null && resource!=null;
+ }
+
+ public boolean canDisplay() {
+ return resource.getMeasure("ca")!=null && resource.getMeasure("ce")!=null;
+ }
+
+ public List<Dependency> getDependencies() {
+ return dependencies;
+ }
+
+ public void setDependencies(List<Dependency> dependencies) {
+ this.dependencies = dependencies;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public void setMeasures(Resource resource) {
+ this.resource = resource;
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTab.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTab.java
new file mode 100644
index 00000000000..3e3541f0589
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTab.java
@@ -0,0 +1,98 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.dependencies.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.gwt.ui.Page;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Dependency;
+import org.sonar.wsclient.services.DependencyQuery;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.List;
+
+public class DependenciesTab extends Page {
+ public static final String GWT_ID = "org.sonar.plugins.design.ui.dependencies.DependenciesTab";
+
+ private FlowPanel panel = null;
+ private DependenciesTable dependenciesTable = null;
+ private Loading loading;
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ prepare();
+ Data data = new Data(resource.getId());
+ loadMeasures(data);
+ loadDependencies(data);
+ return panel;
+ }
+
+ private void prepare() {
+ if (panel == null) {
+ panel = new FlowPanel();
+ panel.getElement().setId("deps");
+ loading = new Loading();
+ dependenciesTable = new DependenciesTable();
+ panel.setWidth("100%");
+ }
+ panel.clear();
+ panel.add(loading);
+ }
+
+ private void loadMeasures(final Data data) {
+ ResourceQuery query = new ResourceQuery(data.getResourceId());
+ query.setMetrics(Metrics.EFFERENT_COUPLINGS, Metrics.AFFERENT_COUPLINGS);
+ query.setVerbose(true);
+ Sonar.getInstance().find(query, new AbstractCallback<org.sonar.wsclient.services.Resource>() {
+ @Override
+ protected void doOnResponse(org.sonar.wsclient.services.Resource resource) {
+ data.setMeasures(resource);
+ displayMeasures(data);
+ }
+ });
+ }
+
+ private void loadDependencies(final Data data) {
+ DependencyQuery query = DependencyQuery.createForResource(data.getResourceId());
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Dependency>() {
+
+ @Override
+ protected void doOnResponse(List<Dependency> dependencies) {
+ data.setDependencies(dependencies);
+ displayMeasures(data);
+ }
+ });
+ }
+
+
+ private void displayMeasures(Data data) {
+ if (data.isLoaded()) {
+ panel.clear();
+ dependenciesTable.display(data);
+ panel.add(dependenciesTable);
+ }
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTable.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTable.java
new file mode 100644
index 00000000000..9faa122e7e7
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/DependenciesTable.java
@@ -0,0 +1,97 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.dependencies.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.wsclient.services.Dependency;
+
+public class DependenciesTable extends Composite {
+
+ private HorizontalPanel panel;
+
+ public DependenciesTable() {
+ panel = new HorizontalPanel();
+ panel.setStylePrimaryName("dependencies");
+ initWidget(panel);
+ }
+
+
+ public void display(final Data data) {
+ panel.clear();
+ if (data.canDisplay()) {
+ panel.add(createIncomingColumn(data));
+ panel.add(createOutgoingColumn(data));
+ } else {
+ panel.add(new Label(I18nConstants.INSTANCE.noData()));
+ }
+ }
+
+
+ private Panel createIncomingColumn(Data data) {
+ FlexTable grid = new FlexTable();
+ grid.setStyleName("col");
+ grid.setWidget(0, 1, new HTML("<b>" + I18nConstants.INSTANCE.afferentCouplings() + "</b>: " + data.getResource().getMeasureIntValue("ca")));
+ grid.getRowFormatter().setStyleName(0, "coltitle");
+
+ int row = 1;
+ for (Dependency dependency : data.getDependencies()) {
+ if (data.getResourceId()==dependency.getToId()) {
+ addDependencyRow(grid, row, dependency.getFromId(), dependency.getFromName() + " (" + dependency.getWeight() + ")");
+ grid.setWidget(row, 0, Icons.forQualifier(dependency.getFromQualifier()).createImage());
+ row++;
+ }
+ }
+
+ return grid;
+ }
+
+ private Panel createOutgoingColumn(Data data) {
+ FlexTable grid = new FlexTable();
+ grid.setStyleName("col");
+ grid.setWidget(0, 1, new HTML("<b>" + I18nConstants.INSTANCE.efferentCouplings() + "</b>: " + data.getResource().getMeasureIntValue("ce")));
+ grid.getRowFormatter().setStyleName(0, "coltitle");
+
+ int row = 1;
+ for (Dependency dependency : data.getDependencies()) {
+ if (data.getResourceId()==dependency.getFromId()) {
+ addDependencyRow(grid, row, dependency.getToId(), dependency.getToName() + " (" + dependency.getWeight() + ")");
+ grid.setWidget(row, 0, Icons.forQualifier(dependency.getToQualifier()).createImage());
+ row++;
+ }
+ }
+
+ return grid;
+ }
+
+ private void addDependencyRow(final FlexTable grid, final int row, final long resourceId, final String name) {
+ Label link = new Label(name);
+ link.setStyleName("link");
+ link.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent clickEvent) {
+ Links.openResourcePopup(String.valueOf(resourceId));
+ }
+ });
+ grid.setWidget(row, 1, link);
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Header.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Header.java
new file mode 100644
index 00000000000..8de67b00dc6
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/Header.java
@@ -0,0 +1,75 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.dependencies.client;
+
+import com.google.gwt.user.client.ui.*;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public class Header extends Composite {
+ private FlowPanel header;
+
+ public Header() {
+ header = new FlowPanel();
+ header.setStyleName("gwt-ViewerHeader");
+ initWidget(header);
+ }
+
+ private void addMeasure(Panel panel, Resource resource, String key, String label) {
+ Measure measure = resource.getMeasure(key);
+ if (measure != null) {
+ HTML html = new HTML(label + ": ");
+ html.setStyleName("metric");
+ panel.add(html);
+
+ html = new HTML(measure.getFormattedValue("-"));
+ html.setStyleName("value");
+ panel.add(html);
+ }
+ }
+
+ public void display(Data data) {
+ header.clear();
+ HorizontalPanel panel = new HorizontalPanel();
+ header.add(panel);
+ addMeasure(panel, data.getResource(), "classes", I18nConstants.INSTANCE.classes());
+ addMeasure(panel, data.getResource(), "dit", I18nConstants.INSTANCE.dit());
+ addMeasure(panel, data.getResource(), "noc", I18nConstants.INSTANCE.noc());
+ addMeasure(panel, data.getResource(), "rfc", I18nConstants.INSTANCE.rfc());
+ addLcom4(data, panel);
+ }
+
+ private void addLcom4(Data data, HorizontalPanel panel) {
+ Measure lcom4 = data.getResource().getMeasure("lcom4");
+ if (lcom4 != null && lcom4.getIntValue()!=null) {
+ HTML html = new HTML(I18nConstants.INSTANCE.lcom4() + ": ");
+ html.setStyleName("metric");
+ panel.add(html);
+
+ html = new HTML(lcom4.getIntValue() + "");
+ html.setStyleName("value");
+ if (lcom4.getIntValue()>1) {
+ html.addStyleName("red bold");
+ }
+
+ panel.add(html);
+ }
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/I18nConstants.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/I18nConstants.java
new file mode 100644
index 00000000000..a90e4fbfde5
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/dependencies/client/I18nConstants.java
@@ -0,0 +1,52 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.dependencies.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.Constants;
+
+public interface I18nConstants extends com.google.gwt.i18n.client.Constants {
+
+ static I18nConstants INSTANCE = GWT.create(I18nConstants.class);
+
+ @DefaultStringValue("Afferent (incoming) couplings")
+ String afferentCouplings();
+
+ @DefaultStringValue("Efferent (outgoing) couplings")
+ String efferentCouplings();
+
+ @DefaultStringValue("Classes")
+ String classes();
+
+ @DefaultStringValue("Depth in Tree")
+ String dit();
+
+ @DefaultStringValue("Number of Children")
+ String noc();
+
+ @Constants.DefaultStringValue("Response for Class")
+ String rfc();
+
+ @DefaultStringValue("Lack of Cohesion of Methods")
+ String lcom4();
+
+ @DefaultStringValue("No data")
+ String noData();
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/GwtLcom4Tab.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/GwtLcom4Tab.java
new file mode 100644
index 00000000000..d4bee638d28
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/GwtLcom4Tab.java
@@ -0,0 +1,42 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.lcom4;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.design.ui.lcom4.client.Lcom4Tab;
+
+@ResourceLanguage(Java.KEY)
+@ResourceQualifier({Resource.QUALIFIER_CLASS})
+@DefaultTab(metrics = {CoreMetrics.LCOM4_KEY})
+@NavigationSection({NavigationSection.RESOURCE_TAB})
+@UserRole(UserRole.CODEVIEWER)
+public class GwtLcom4Tab extends GwtPage {
+
+ public String getTitle() {
+ return "LCOM4";
+ }
+
+ public String getGwtId() {
+ return Lcom4Tab.GWT_ID;
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Data.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Data.java
new file mode 100644
index 00000000000..3f34d7400fe
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Data.java
@@ -0,0 +1,52 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.lcom4.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public final class Data {
+
+ public static class Entity extends JavaScriptObject {
+ // Overlay types always have protected, zero-arg ctors
+ protected Entity() {
+ }
+
+ public final native String getName() /*-{ return this.n; }-*/;
+ public final native String getQualifier() /*-{ return this.q; }-*/;
+ }
+
+ public static class Block extends JavaScriptObject {
+ protected Block() {
+ }
+ public final native int size() /*-{ return this.length; }-*/;
+ public final native Entity get(int i) /*-{ return this[i]; }-*/;
+ }
+
+ public static class Blocks extends JavaScriptObject {
+ protected Blocks() {
+ }
+ public final native int size() /*-{ return this.length; }-*/;
+ public final native Block get(int i) /*-{ return this[i]; }-*/;
+ }
+
+ public static native Blocks parse(String json) /*-{
+ return eval('(' + json + ')')
+ }-*/;
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Lcom4Tab.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Lcom4Tab.java
new file mode 100644
index 00000000000..ee7a2f4292b
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/lcom4/client/Lcom4Tab.java
@@ -0,0 +1,104 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.lcom4.client;
+
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.gwt.ui.Page;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+public class Lcom4Tab extends Page {
+ public static final String GWT_ID = "org.sonar.plugins.design.ui.lcom4.Lcom4Tab";
+
+ private VerticalPanel panel;
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ panel = new VerticalPanel();
+ panel.setWidth("100%");
+ loadData(resource);
+ return panel;
+ }
+
+ private void loadData(Resource resource) {
+ panel.add(new Loading());
+ ResourceQuery query = ResourceQuery.createForMetrics(resource.getId().toString(), "lcom4", "lcom4_blocks");
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>() {
+ @Override
+ protected void doOnResponse(Resource resource) {
+ panel.clear();
+ panel.add(new Header(resource));
+ if (resource != null && resource.getMeasure("lcom4_blocks") != null) {
+ String json = resource.getMeasure("lcom4_blocks").getData();
+ Data.Blocks blocks = Data.parse(json);
+
+ Grid grid = new Grid(blocks.size(), 2);
+ grid.setStyleName("lcom4blocks");
+ grid.getColumnFormatter().setStyleName(0, "index");
+
+ for (int indexBlock = 0; indexBlock < blocks.size(); indexBlock++) {
+ Data.Block block = blocks.get(indexBlock);
+ grid.setHTML(indexBlock, 0, "<div class='index'>" + (indexBlock + 1) + "</div>");
+
+ VerticalPanel blockPanel = new VerticalPanel();
+ blockPanel.setStyleName("lcom4block");
+
+ for (int indexEntity = 0; indexEntity < block.size(); indexEntity++) {
+ Data.Entity entity = block.get(indexEntity);
+ HTML row = new HTML(Icons.forQualifier(entity.getQualifier()).getHTML() + " " + entity.getName());
+ row.setStyleName("lcom4row");
+ blockPanel.add(row);
+ }
+ grid.setWidget(indexBlock, 1, blockPanel);
+
+ }
+ panel.add(grid);
+ }
+ }
+ });
+ }
+
+ private static class Header extends Composite {
+ private FlowPanel header;
+
+ public Header(Resource resource) {
+ header = new FlowPanel();
+ header.setStyleName("gwt-ViewerHeader");
+
+ HorizontalPanel panel = new HorizontalPanel();
+ HTML html = new HTML("Lack of Cohesion of Methods: ");
+ html.setStyleName("metric");
+ panel.add(html);
+
+ if (resource != null) {
+ html = new HTML(resource.getMeasureFormattedValue("lcom4", "-"));
+ html.setStyleName("value");
+ panel.add(html);
+ }
+
+ header.add(panel);
+ initWidget(header);
+ }
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/GwtLibrariesPage.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/GwtLibrariesPage.java
new file mode 100644
index 00000000000..32d54114a8d
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/GwtLibrariesPage.java
@@ -0,0 +1,41 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.libraries;
+
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.design.ui.libraries.client.LibrariesPage;
+
+@ResourceLanguage(Java.KEY)
+@ResourceQualifier({Resource.QUALIFIER_PROJECT, Resource.QUALIFIER_MODULE})
+@NavigationSection(NavigationSection.RESOURCE)
+@UserRole(UserRole.USER)
+public class GwtLibrariesPage extends GwtPage {
+
+ public String getTitle() {
+ return "Libraries";
+ }
+
+ public String getGwtId() {
+ return LibrariesPage.GWT_ID;
+ }
+}
+
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Filters.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Filters.java
new file mode 100644
index 00000000000..31f7888c04e
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Filters.java
@@ -0,0 +1,116 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.libraries.client;
+
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Label;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Links;
+import org.sonar.wsclient.services.Resource;
+
+public class Filters extends Grid {
+
+ private static final String PARAM_TEST = "test";
+
+ private KeywordFilter keywordFilter;
+ private CheckBox testCheckbox;
+ private Anchor expandCollapse;
+ private boolean isExpanded;
+ private Anchor usageLink;
+
+
+ public Filters(Resource resource) {
+ super(1, 5);
+
+ setStyleName("libFilter");
+
+ keywordFilter = new KeywordFilter();
+ setWidget(0, 0, new Label(I18nConstants.INSTANCE.filter()));
+ setWidget(0, 1, keywordFilter);
+
+ testCheckbox = new CheckBox(I18nConstants.INSTANCE.displayTests());
+ testCheckbox.getElement().setId("testCb");
+ testCheckbox.setValue(Boolean.valueOf(Configuration.getRequestParameter(PARAM_TEST, "false")));
+ setWidget(0, 2, testCheckbox);
+
+ expandCollapse = new Anchor(I18nConstants.INSTANCE.collapse());
+ isExpanded = true;
+ setWidget(0, 3, expandCollapse);
+
+ usageLink = new Anchor(I18nConstants.INSTANCE.usageLink(), Links.baseUrl() + "/dependencies/index?search=" + resource.getKey());
+ setWidget(0, 4, usageLink);
+ }
+
+ public KeywordFilter getKeywordFilter() {
+ return keywordFilter;
+ }
+
+ public CheckBox getTestCheckbox() {
+ return testCheckbox;
+ }
+
+ public boolean isTestDisplayed() {
+ return testCheckbox.getValue();
+ }
+
+ public boolean isTestFiltered() {
+ return !isTestDisplayed();
+ }
+
+ public boolean hasKeyword() {
+ return getKeywordFilter().hasKeyword();
+ }
+
+ public Anchor getExpandCollapseLink() {
+ return expandCollapse;
+ }
+
+ public boolean isExpanded() {
+ return isExpanded;
+ }
+
+ public boolean isCollapsed() {
+ return !isExpanded;
+ }
+
+ public Anchor getUsageLink() {
+ return usageLink;
+ }
+
+ public void expand() {
+ if (!isExpanded) {
+ expandCollapse.setText(I18nConstants.INSTANCE.collapse());
+ isExpanded = true;
+ }
+ }
+
+ public void collapse() {
+ if (isExpanded) {
+ expandCollapse.setText(I18nConstants.INSTANCE.expand());
+ isExpanded = false;
+ }
+ }
+
+ public String toUrlParams() {
+ return PARAM_TEST + '=' + testCheckbox.getValue() + '&' + KeywordFilter.PARAM_FILTER + '=' + getKeywordFilter();
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/I18nConstants.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/I18nConstants.java
new file mode 100644
index 00000000000..496fb44a656
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/I18nConstants.java
@@ -0,0 +1,45 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.libraries.client;
+
+import com.google.gwt.core.client.GWT;
+
+public interface I18nConstants extends com.google.gwt.i18n.client.Constants {
+
+ static I18nConstants INSTANCE = GWT.create(I18nConstants.class);
+
+ @DefaultStringValue("Filter:")
+ String filter();
+
+ @DefaultStringValue("Display test libraries")
+ String displayTests();
+
+ @DefaultStringValue("Expand all")
+ String expand();
+
+ @DefaultStringValue("Collapse all")
+ String collapse();
+
+ @DefaultStringValue("No libraries")
+ String noLibraries();
+
+ @DefaultStringValue("Usages")
+ String usageLink();
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/KeywordFilter.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/KeywordFilter.java
new file mode 100644
index 00000000000..d162a4340e3
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/KeywordFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.libraries.client;
+
+import com.google.gwt.user.client.ui.TextBox;
+import org.sonar.gwt.Configuration;
+
+public class KeywordFilter extends TextBox {
+ public static final String PARAM_FILTER = "filter";
+
+ public KeywordFilter() {
+ getElement().setId("keywordFilter");
+ String filterValue = Configuration.getRequestParameter(PARAM_FILTER);
+ if (filterValue != null) {
+ setValue(filterValue);
+ }
+ }
+
+ public String getKeyword() {
+ return getText().trim().toUpperCase();
+ }
+
+ public boolean hasKeyword() {
+ String keyword = getKeyword();
+ return keyword!=null && !"".equals(keyword);
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/LibrariesPage.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/LibrariesPage.java
new file mode 100644
index 00000000000..54cb1deb2dc
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/LibrariesPage.java
@@ -0,0 +1,103 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.libraries.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.ui.Page;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LibrariesPage extends Page implements KeyUpHandler, ClickHandler {
+ public static final String GWT_ID = "org.sonar.plugins.design.ui.libraries.LibrariesPage";
+
+ private Filters filters;
+ private List<ProjectPanel> projectPanels = new ArrayList<ProjectPanel>();
+ private Panel container;
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ container = new VerticalPanel();
+ container.setWidth("100%");
+
+ filters = new Filters(resource);
+ filters.getKeywordFilter().addKeyUpHandler(this);
+ filters.getTestCheckbox().addClickHandler(this);
+ filters.getExpandCollapseLink().addClickHandler(this);
+
+ container.add(filters);
+ load(resource);
+ return container;
+ }
+
+
+ private void load(Resource resource) {
+ ResourceQuery resourceQuery = new ResourceQuery(resource.getId().toString());
+ resourceQuery.setDepth(-1).setScopes(Resource.SCOPE_SET);
+ Sonar.getInstance().findAll(resourceQuery, new AbstractListCallback<Resource>() {
+ @Override
+ protected void doOnResponse(List<Resource> subProjects) {
+ for (Resource subProject : subProjects) {
+ ProjectPanel projectPanel = new ProjectPanel(subProject, filters);
+ projectPanels.add(projectPanel);
+ container.add(projectPanel);
+ }
+ }
+ });
+ }
+
+ public void onKeyUp(KeyUpEvent event) {
+ for (ProjectPanel projectPanel : projectPanels) {
+ projectPanel.filter();
+ }
+ }
+
+ public void onClick(ClickEvent event) {
+ if (event.getSource() == filters.getTestCheckbox()) {
+ for (ProjectPanel projectPanel : projectPanels) {
+ projectPanel.filter();
+ }
+ } else if (event.getSource() == filters.getExpandCollapseLink()) {
+ if (filters.isExpanded()) {
+ filters.collapse();
+ expandCollapseLibs(false);
+ } else {
+ filters.expand();
+ expandCollapseLibs(true);
+ }
+ }
+ }
+
+ private void expandCollapseLibs(boolean expand) {
+ for (ProjectPanel projectPanel : projectPanels) {
+ projectPanel.expandCollapse(expand);
+ }
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Library.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Library.java
new file mode 100644
index 00000000000..1e20f0c77b7
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/Library.java
@@ -0,0 +1,98 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.libraries.client;
+
+import com.google.gwt.user.client.ui.TreeItem;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.wsclient.services.DependencyTree;
+
+public class Library extends TreeItem {
+
+ private String keywords;
+ private String usage;
+
+ public Library(DependencyTree dep) {
+ setHTML(toHTML(dep));
+ keywords = toKeywords(dep);
+ usage = dep.getUsage();
+ }
+
+ private static String toKeywords(DependencyTree dep) {
+ String text = dep.getResourceName() + " ";
+
+ if (dep.getResourceKey() != null) {
+ text += dep.getResourceKey() + " ";
+ }
+ if (dep.getResourceVersion() != null) {
+ text += dep.getResourceVersion() + " ";
+ }
+ text += dep.getUsage();
+ return text.toUpperCase();
+ }
+
+
+ /**
+ * @param keyword upper-case keyword
+ */
+ public boolean containsKeyword(String keyword) {
+ if (keywords.indexOf(keyword) >= 0) {
+ return true;
+ }
+ for (int index = 0; index < getChildCount(); index++) {
+ if (((Library) getChild(index)).containsKeyword(keyword)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String toHTML(DependencyTree tree) {
+ String html = Icons.forQualifier(tree.getResourceQualifier()).getHTML();
+ html += " <span> " + tree.getResourceName() + "</span> ";
+
+ if (tree.getResourceVersion() != null) {
+ html += tree.getResourceVersion() + " ";
+ }
+ html += "(" + tree.getUsage() + ")";
+ return html;
+ }
+
+
+ public boolean filter(String keyword, boolean testFiltered) {
+ if (testFiltered && "test".equals(usage)) {
+ setVisible(false);
+ return true;
+ }
+
+ boolean filtered = false;
+ if (!"".equals(keyword) && !containsKeyword(keyword)) {
+ filtered = true;
+ }
+
+ boolean hasVisibleChild = false;
+ for (int index = 0; index < getChildCount(); index++) {
+ hasVisibleChild |= !((Library) getChild(index)).filter(keyword, testFiltered);
+ }
+
+ boolean visible = !filtered || hasVisibleChild;
+ setVisible(visible);
+ return !visible;
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/ProjectPanel.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/ProjectPanel.java
new file mode 100644
index 00000000000..6cae74bf78c
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/libraries/client/ProjectPanel.java
@@ -0,0 +1,130 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.libraries.client;
+
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.DependencyTree;
+import org.sonar.wsclient.services.DependencyTreeQuery;
+import org.sonar.wsclient.services.Resource;
+
+import java.util.List;
+
+public class ProjectPanel extends FlowPanel {
+
+ private Label title;
+ private Tree tree;
+ private Filters filters;
+
+ public ProjectPanel(Resource project, Filters filters) {
+ this.filters = filters;
+ setStyleName("libs");
+ getElement().setId("libs-" + project.getKey());
+ add(new Loading(project.getName()));
+ loadLibraries(project);
+ }
+
+ private void loadLibraries(final Resource project) {
+ Sonar.getInstance().findAll(DependencyTreeQuery.createForProject(project.getId().toString()), new AbstractListCallback<DependencyTree>() {
+
+ @Override
+ protected void doOnResponse(List<DependencyTree> dependencyTrees) {
+ createTitle(project);
+ createTree();
+
+ if (dependencyTrees == null || dependencyTrees.isEmpty()) {
+ clear();
+ add(title);
+ add(createNoLibsMessage());
+
+ } else {
+ display(dependencyTrees, null);
+ filter();
+
+ clear();
+ add(title);
+ add(tree);
+ }
+ }
+
+ private void createTitle(Resource project) {
+ String html = Icons.forQualifier(project.getQualifier()).getHTML();
+ html += " <span class=''> " + project.getName() + "</span> ";
+
+ if (project.getVersion() != null) {
+ html += project.getVersion() + " ";
+ }
+ title = new HTML(html);
+ }
+
+ private void display(List<DependencyTree> depTrees, TreeItem parentLibrary) {
+ if (depTrees != null) {
+ for (DependencyTree depTree : depTrees) {
+ Library library = new Library(depTree);
+ if (parentLibrary == null) {
+ tree.addItem(library);
+ library.setState(true);
+ } else {
+ parentLibrary.addItem(library);
+ }
+ display(depTree.getTo(), library);
+ library.setState(true);
+ }
+ }
+ }
+
+ private void createTree() {
+ tree = new Tree();
+ tree.setAnimationEnabled(false);
+ }
+ });
+ }
+
+ private Label createNoLibsMessage() {
+ Label msg = new Label(I18nConstants.INSTANCE.noLibraries());
+ msg.setStyleName("nolibs");
+ return msg;
+ }
+
+ public void filter() {
+ boolean visible = (tree.getItemCount() == 0 && !filters.hasKeyword());
+ for (int index = 0; index < tree.getItemCount(); index++) {
+ Library lib = (Library) tree.getItem(index);
+ visible |= !lib.filter(filters.getKeywordFilter().getKeyword(), filters.isTestFiltered());
+ }
+ setVisible(visible);
+ }
+
+ public void expandCollapse(boolean expand) {
+ for (int index = 0; index < tree.getItemCount(); index++) {
+ openItem(tree.getItem(index), expand);
+ }
+ }
+
+ private void openItem(TreeItem item, boolean open) {
+ item.setState(open);
+ for (int i = 0; i < item.getChildCount(); i++) {
+ openItem(item.getChild(i), open);
+ }
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/GwtDesignPage.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/GwtDesignPage.java
new file mode 100644
index 00000000000..54e8140fcef
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/GwtDesignPage.java
@@ -0,0 +1,43 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.page;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.design.ui.page.client.DesignPage;
+
+@ResourceLanguage(Java.KEY)
+@ResourceScope({Resource.SCOPE_SET, Resource.SCOPE_SPACE})
+@DefaultTab(metrics={CoreMetrics.DEPENDENCY_MATRIX_KEY, CoreMetrics.PACKAGE_FEEDBACK_EDGES_KEY, CoreMetrics.PACKAGE_CYCLES_KEY, CoreMetrics.PACKAGE_TANGLE_INDEX_KEY, CoreMetrics.PACKAGE_TANGLES_KEY, CoreMetrics.FILE_CYCLES_KEY, CoreMetrics.FILE_TANGLE_INDEX_KEY, CoreMetrics.FILE_TANGLES_KEY, CoreMetrics.FILE_FEEDBACK_EDGES_KEY})
+@NavigationSection({NavigationSection.RESOURCE, NavigationSection.RESOURCE_TAB})
+@UserRole(UserRole.USER)
+public class GwtDesignPage extends GwtPage {
+
+ public String getGwtId() {
+ return DesignPage.GWT_ID;
+ }
+
+ public String getTitle() {
+ return "Design";
+ }
+
+} \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DependencyInfo.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DependencyInfo.java
new file mode 100644
index 00000000000..72e263da58a
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DependencyInfo.java
@@ -0,0 +1,180 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.page.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Dependency;
+import org.sonar.wsclient.services.DependencyQuery;
+import org.sonar.wsclient.services.Resource;
+
+import java.util.List;
+
+public class DependencyInfo extends Composite {
+
+ private static DependencyInfo INSTANCE = new DependencyInfo();
+
+ private VerticalPanel panel;
+ private Loading loading = new Loading();
+ private String currentDependencyId = null;
+ private boolean popupMode = false;
+
+ private DependencyInfo() {
+ panel = new VerticalPanel();
+ initWidget(panel);
+ }
+
+ public static DependencyInfo getInstance() {
+ return INSTANCE;
+ }
+
+
+ public void showOrPopup(String dependencyId) {
+ if (popupMode) {
+ Window.open(Links.urlForResourcePage(Configuration.getResourceId(), DesignPage.GWT_ID, "layout=false&depId=" + dependencyId), "dependency", Links.DEFAULT_POPUP_HTML_FEATURES);
+
+ } else {
+ INSTANCE.show(dependencyId);
+ }
+ }
+
+ public void show(String dependencyId) {
+ panel.clear();
+ currentDependencyId = dependencyId;
+ if (dependencyId !=null) {
+ panel.add(loading);
+ loadDependency(dependencyId);
+ }
+ }
+
+ public DependencyInfo setPopupMode(boolean b) {
+ this.popupMode = b;
+ return this;
+ }
+
+ public void popup() {
+ popupMode = true;
+ panel.clear();
+ showOrPopup(currentDependencyId);
+ }
+
+ private void setLoaded() {
+ loading.removeFromParent();
+ }
+
+ private void loadDependency(String dependencyId) {
+ DependencyQuery query = DependencyQuery.createForId(dependencyId);
+ Sonar.getInstance().find(query, new AbstractCallback<Dependency>() {
+ @Override
+ protected void doOnResponse(Dependency dependency) {
+ if (dependency == null) {
+ setLoaded();
+ panel.add(new Label(I18nConstants.INSTANCE.noData()));
+ } else {
+ loadSubDependencies(dependency);
+ }
+ }
+
+ @Override
+ protected void doOnError(int errorCode, String errorMessage) {
+ super.doOnError(errorCode, errorMessage);
+ }
+ });
+ }
+
+ private void loadSubDependencies(final Dependency dependency) {
+ DependencyQuery query = DependencyQuery.createForSubDependencies(dependency.getId());
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Dependency>() {
+
+ @Override
+ protected void doOnResponse(final List<Dependency> subDependencies) {
+ Grid table = new Grid(subDependencies.size() + 1, 5);
+ table.setStyleName("depInfo");
+ createHeader(dependency, table);
+
+ for (int row = 0; row < subDependencies.size(); row++) {
+ Dependency dep = subDependencies.get(row);
+ table.setWidget(row + 1, 0, new HTML(Icons.forQualifier(dep.getFromQualifier()).getHTML()));
+ if (Resource.QUALIFIER_FILE.equals(dep.getFromQualifier()) || Resource.QUALIFIER_CLASS.equals(dep.getFromQualifier())) {
+ table.setWidget(row + 1, 1, createLink(dep.getFromId(), dep.getFromName()));
+ } else {
+ table.setText(row + 1, 1, dep.getFromName());
+ }
+ table.setText(row + 1, 2, " " + dep.getUsage() + " ");
+ table.setWidget(row + 1, 3, new HTML(Icons.forQualifier(dep.getToQualifier()).getHTML()));
+ if (Resource.QUALIFIER_FILE.equals(dep.getToQualifier()) || Resource.QUALIFIER_CLASS.equals(dep.getToQualifier())) {
+ table.setWidget(row + 1, 4, createLink(dep.getToId(), dep.getToName()));
+ } else {
+ table.setText(row + 1, 4, dep.getToName());
+ }
+ }
+
+
+ panel.clear();
+ if (!popupMode) {
+ panel.add(createNewWindowLink());
+ }
+ panel.add(table);
+ }
+ });
+ }
+
+ private Label createLink(final long resourceId, final String resourceName) {
+ Label link = new Label(resourceName);
+ link.setStyleName("link");
+ link.addClickHandler(new ClickHandler() {
+
+ public void onClick(ClickEvent event) {
+ Links.openResourcePopup(String.valueOf(resourceId));
+ }
+ });
+ return link;
+ }
+
+ private void createHeader(final Dependency dependency, final Grid grid) {
+ grid.getRowFormatter().setStyleName(0, "depInfoHeader");
+
+ grid.setWidget(0, 0, Icons.forQualifier(dependency.getFromQualifier()).createImage());
+ grid.setText(0, 1, dependency.getFromName());
+
+ grid.setWidget(0, 3, Icons.forQualifier(dependency.getToQualifier()).createImage());
+ grid.setText(0, 4, dependency.getToName());
+ }
+
+ private Widget createNewWindowLink() {
+ Label popup = new Label(I18nConstants.INSTANCE.newWindow());
+ popup.setStyleName("newwindow");
+ popup.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ popup();
+ }
+ });
+ return popup;
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DesignPage.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DesignPage.java
new file mode 100644
index 00000000000..a8be3dfd472
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DesignPage.java
@@ -0,0 +1,72 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.page.client;
+
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+public class DesignPage extends Page {
+
+ public static final String GWT_ID = "org.sonar.plugins.design.ui.page.DesignPage";
+ private Dsm matrix;
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ String dependencyId = Configuration.getRequestParameter("depId");
+ if (dependencyId != null) {
+ DependencyInfo.getInstance().setPopupMode(true).show(dependencyId);
+ return DependencyInfo.getInstance();
+ }
+ VerticalPanel layout = new VerticalPanel();
+ layout.setWidth("100%");
+ Panel hPanel = new VerticalPanel();
+ matrix = new Dsm();
+ hPanel.add(matrix);
+ hPanel.add(DependencyInfo.getInstance());
+ layout.add(hPanel);
+ loadMatrix(resource);
+ return layout;
+ }
+
+ private void loadMatrix(Resource resource) {
+ Sonar.getInstance().find(ResourceQuery.createForMetrics(resource.getId().toString(), "dsm"), new AbstractCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ if (resource == null || resource.getMeasure(Metrics.DEPENDENCY_MATRIX) == null) {
+ matrix.displayNoData();
+
+ } else {
+ Measure dsm = resource.getMeasure(Metrics.DEPENDENCY_MATRIX);
+ matrix.display(DsmData.parse(dsm.getData()));
+ }
+ }
+ });
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/Dsm.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/Dsm.java
new file mode 100644
index 00000000000..2b7dba5bd01
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/Dsm.java
@@ -0,0 +1,366 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.page.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DoubleClickEvent;
+import com.google.gwt.event.dom.client.DoubleClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.ui.Icons;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Dsm extends Composite {
+
+ /* STYLES */
+ public static final String DSM = "dsm";
+
+ public static final String HEADER = "htable";
+ public static final String HEADER_TITLE = "ht";
+ public static final String HEADER_SELECTED_SUFFIX = "s";
+ public static final String HEADER_INDICATOR = "hi";
+ public static final String HEADER_HIGHER_INDICATOR_SUFFIX = "h";
+ public static final String HEADER_LOWER_INDICATOR_SUFFIX = "l";
+
+ public static final String GRID = "gtable";
+ public static final String GRID_CELL_BOTTOM_LEFT = "cbl";
+ public static final String GRID_CELL_TOP_RIGHT = "ctr";
+ public static final String GRID_CELL_DIAGONAL = "cd";
+ public static final String GRID_CELL_SELECTION1_SUFFIX = "s1";
+ public static final String GRID_CELL_SELECTION2_SUFFIX = "s2";
+ public static final String GRID_CELL_COMB1_SUFFIX = "c1";
+ public static final String GRID_CELL_COMB2_SUFFIX = "c2";
+ public static final String[] GRID_SUFFIXES = {GRID_CELL_SELECTION1_SUFFIX, GRID_CELL_SELECTION2_SUFFIX, GRID_CELL_COMB1_SUFFIX, GRID_CELL_COMB2_SUFFIX};
+
+
+ private VerticalPanel dsm = new VerticalPanel();
+ private DsmData.Rows data;
+ private Label[][] cells;
+ private Label[] titles;
+ private Label[] indicators;
+ private List<Label> highlightedCells = new LinkedList<Label>();
+
+ public Dsm() {
+ dsm.setStylePrimaryName(DSM);
+ initWidget(dsm);
+ }
+
+ private Widget createLegend() {
+ HorizontalPanel legend = new HorizontalPanel();
+ legend.getElement().setId("dsmlegend");
+ legend.add(new HTML("<div class='square gray'> </div>"));
+ legend.add(new Label(I18nConstants.INSTANCE.legendDependencies()));
+ legend.add(new HTML("<div class='space'></div>"));
+ legend.add(new HTML("<div class='square red'> </div> "));
+ legend.add(new Label(I18nConstants.INSTANCE.legendCycles()));
+ legend.add(new HTML(" <div class='space'></div> "));
+ legend.add(new HTML("<div class='square green'></div> "));
+ legend.add(new Label(I18nConstants.INSTANCE.legendUses()));
+ legend.add(new HTML("<div class='square blue'></div> "));
+ legend.add(new Label(I18nConstants.INSTANCE.legendUses()));
+ legend.add(new HTML(" <div class='square yellow'></div>"));
+ return legend;
+ }
+
+ public void displayNoData() {
+ dsm.clear();
+ dsm.add(new Label(I18nConstants.INSTANCE.noData()));
+ }
+
+ public void display(DsmData.Rows data) {
+ if (data == null) {
+ displayNoData();
+
+ } else {
+ this.data = data;
+ dsm.clear();
+ dsm.add(createHelp());
+ dsm.add(createLegend());
+ HorizontalPanel matrix = new HorizontalPanel();
+ matrix.add(createRowHeader());
+ matrix.add(createGrid());
+ dsm.add(matrix);
+ }
+ }
+
+ private Widget createHelp() {
+ HorizontalPanel help = new HorizontalPanel();
+ help.getElement().setId("dsmhelp");
+ Anchor link = new Anchor(I18nConstants.INSTANCE.linkToHelp(), "http://docs.codehaus.org/x/QQFhC", "docsonar");
+ help.add(Icons.get().help().createImage());
+ help.add(link);
+ return help;
+ }
+
+ private Grid createRowHeader() {
+ Grid header = new Grid(data.size(), 2);
+ header.setCellPadding(0);
+ header.setCellSpacing(0);
+ header.setStylePrimaryName(HEADER);
+
+ titles = new Label[data.size()];
+ indicators = new Label[data.size()];
+ for (int indexRow = 0; indexRow < data.size(); indexRow++) {
+ DsmData.Row row = data.get(indexRow);
+
+ HTML title = buildRowTitle(indexRow, row);
+ titles[indexRow] = title;
+ header.setWidget(indexRow, 0, title);
+
+ Label indicator = buildLabel("", HEADER_INDICATOR);
+ header.setWidget(indexRow, 1, indicator);
+ indicators[indexRow] = indicator;
+ }
+ return header;
+ }
+
+ private Grid createGrid() {
+ int rowsCount = data.size();
+ Grid grid = new Grid(rowsCount, rowsCount);
+ grid.setCellPadding(0);
+ grid.setCellSpacing(0);
+ grid.setStylePrimaryName(GRID);
+
+ return loadGridCells(grid, data);
+ }
+
+ private Grid loadGridCells(Grid grid, DsmData.Rows data) {
+ int size = data.size();
+ cells = new Label[size][size];
+ for (int row = 0; row < size; row++) {
+ DsmData.Row resource = data.get(row);
+ for (int col = 0; col < resource.size(); col++) {
+ Label cell = createGridCell(row, col, resource.getWeight(col));
+ grid.setWidget(row, col, cell);
+ cells[row][col] = cell;
+ }
+ }
+ return grid;
+ }
+
+
+ /* ---------------- ACTIONS -------------------- */
+
+ public void onCellClicked(int row, int col) {
+ cancelHighlighting();
+
+ highlightTitle(row);
+ highlightTitle(col);
+ highlightIndicator(row);
+ highlightIndicator(col);
+
+ for (int i = 0; i < cells.length; i++) {
+ for (int j = 0; j < cells.length; j++) {
+ Label cell = cells[i][j];
+ if (i == row && j == col) {
+ highlightCell(cell, GRID_CELL_SELECTION1_SUFFIX);
+
+ } else if (j == row && i == col) {
+ // opposite
+ highlightCell(cell, GRID_CELL_SELECTION1_SUFFIX);
+
+ } else if (j == col || i == col) {
+ highlightCell(cell, GRID_CELL_COMB1_SUFFIX);
+
+ } else if (i == row || j == row) {
+ highlightCell(cell, GRID_CELL_COMB2_SUFFIX);
+ }
+ }
+ }
+ }
+
+ private void displayDependencyInfo(int row, int col) {
+ DsmData.Cell cell = data.get(row).getCell(col);
+ DependencyInfo.getInstance().showOrPopup(cell.getDependencyId());
+ }
+
+ public void onTitleClicked(int row) {
+ cancelHighlighting();
+ highlightTitle(row);
+ highlightIndicator(row);
+
+ // highlight row
+ for (int col = 0; col < cells[row].length; col++) {
+ highlightCell(cells[row][col], GRID_CELL_SELECTION2_SUFFIX);
+ if (col < row && hasWeight(cells[row][col])) {
+ highlightIndicator(col, true);
+ }
+ }
+
+ // highlight column
+ for (int i = 0; i < cells.length; i++) {
+ if (i != row) {
+ highlightCell(cells[i][row], GRID_CELL_SELECTION2_SUFFIX);
+ if (i > row && hasWeight(cells[i][row])) {
+ highlightIndicator(i, false);
+ }
+ }
+ }
+ }
+
+ private boolean hasWeight(Label label) {
+ return label.getText().length() > 0;
+ }
+
+
+ /*--------- EFFECTS ----------*/
+ private void cancelHighlighting() {
+ cancelGridHighlighting();
+ cancelIndicatorsHighlighting();
+ cancelTitlesHighlighting();
+ }
+
+ private void cancelGridHighlighting() {
+ for (Label cell : highlightedCells) {
+ for (String suffix : GRID_SUFFIXES) {
+ cell.removeStyleDependentName(suffix);
+ }
+ }
+ highlightedCells.clear();
+ }
+
+ private void highlightCell(Label cell, String style) {
+ cell.addStyleDependentName(style);
+ highlightedCells.add(cell);
+ }
+
+ private void highlightTitle(int row) {
+ titles[row].addStyleDependentName(HEADER_SELECTED_SUFFIX);
+ }
+
+ private void cancelTitlesHighlighting() {
+ for (Label title : titles) {
+ title.removeStyleDependentName(HEADER_SELECTED_SUFFIX);
+ }
+ }
+
+ private void cancelIndicatorsHighlighting() {
+ for (Label indicator : indicators) {
+ indicator.removeStyleDependentName(HEADER_HIGHER_INDICATOR_SUFFIX);
+ indicator.removeStyleDependentName(HEADER_LOWER_INDICATOR_SUFFIX);
+ indicator.removeStyleDependentName(HEADER_SELECTED_SUFFIX);
+ }
+ }
+
+ private void highlightIndicator(int row) {
+ indicators[row].addStyleDependentName(HEADER_SELECTED_SUFFIX);
+ }
+
+ private void highlightIndicator(int row, boolean higher) {
+ indicators[row].addStyleDependentName(higher ? HEADER_HIGHER_INDICATOR_SUFFIX : HEADER_LOWER_INDICATOR_SUFFIX);
+ }
+
+
+ /* ---------- COMPONENTS ------------ */
+ private Label createGridCell(final int row, final int col, final int weight) {
+ Label cell;
+ if (row == col) {
+ cell = createDiagonalCell(row);
+
+ } else {
+ cell = createNonDiagonalCell(row, col, weight);
+ }
+ return cell;
+ }
+
+ private Label createNonDiagonalCell(final int row, final int col, int weight) {
+ Label cell;
+ cell = buildCell(row, col, weight, (col > row ? GRID_CELL_TOP_RIGHT : GRID_CELL_BOTTOM_LEFT));
+
+ if (weight > 0) {
+ String tooltip = data.get(col).getName() + " -> " + data.get(row).getName() + " (" + weight + "). " + I18nConstants.INSTANCE.cellTooltip();
+ cell.setTitle(tooltip);
+ }
+ return cell;
+ }
+
+ private Label createDiagonalCell(final int row) {
+ Label cell;
+ cell = buildLabel("-", GRID_CELL_DIAGONAL);
+ cell.addClickHandler(new ClickHandler() {
+ public void onClick(final ClickEvent event) {
+ onTitleClicked(row);
+ }
+ });
+ return cell;
+ }
+
+ private HTML buildRowTitle(final int indexRow, final DsmData.Row row) {
+ HTML title = new HTML(Icons.forQualifier(row.getQualifier()).getHTML() + " " + row.getName()) {
+ {
+ addDomHandler(new DoubleClickHandler() {
+ public void onDoubleClick(DoubleClickEvent pEvent) {
+ if (row.getId() != null) {
+ if (!"FIL".equals(row.getQualifier()) && !"CLA".equals(row.getQualifier())) {
+ Window.Location.assign(Links.urlForResourcePage(row.getId(), DesignPage.GWT_ID, null));
+ } else {
+ Links.openMeasurePopup(row.getId(), null);
+ }
+ }
+ }
+ }, DoubleClickEvent.getType());
+ }
+ };
+ title.setStylePrimaryName(HEADER_TITLE);
+ title.setTitle(I18nConstants.INSTANCE.rowTooltip());
+ final int finalIndexRow = indexRow;
+ title.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ onTitleClicked(finalIndexRow);
+ }
+ });
+ return title;
+ }
+
+ private static Label buildLabel(String text, String primaryStyle) {
+ Label label = new Label(text);
+ label.setStylePrimaryName(primaryStyle);
+ return label;
+ }
+
+ private Label buildCell(final int row, final int col, int weight, String primaryStyle) {
+ String text = "";
+ if (weight > 0) {
+ text = "<span>" + Integer.toString(weight) + "</span>";
+ }
+
+ HTML cell = new HTML(text) {
+ {
+ addDomHandler(new DoubleClickHandler() {
+ public void onDoubleClick(DoubleClickEvent pEvent) {
+ displayDependencyInfo(row, col);
+ }
+ }, DoubleClickEvent.getType());
+ }
+ };
+ cell.addClickHandler(new ClickHandler() {
+ public void onClick(final ClickEvent event) {
+ onCellClicked(row, col);
+ }
+ });
+ cell.setStylePrimaryName(primaryStyle);
+ return cell;
+ }
+
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DsmData.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DsmData.java
new file mode 100644
index 00000000000..42c362f268e
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/DsmData.java
@@ -0,0 +1,65 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.page.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public final class DsmData {
+
+ public static class Row extends JavaScriptObject {
+
+ // Overlay types always have protected, zero-arg ctors
+ protected Row() {
+ }
+
+ public final native String getId() /*-{ return this.i; }-*/;
+ public final native String getName() /*-{ return this.n; }-*/;
+ public final native String getQualifier() /*-{ return this.q; }-*/;
+ public final native Cell getCell(final int col) /*-{ return this.v[col]; }-*/;
+ public final native int size() /*-{ return this.v.length; }-*/;
+ public final int getWeight(final int col) {
+ Cell cell = getCell(col);
+ return (cell==null) ? 0 : cell.getWeight();
+ }
+ }
+
+ public static class Cell extends JavaScriptObject {
+
+ // Overlay types always have protected, zero-arg ctors
+ protected Cell() {
+ }
+
+ public final native String getDependencyId() /*-{ if(this.i) {return this.i;} else { return null;} }-*/;
+ public final native int getWeight() /*-{ return this.w || 0; }-*/;
+ }
+
+
+ public static class Rows extends JavaScriptObject {
+ protected Rows() {
+ }
+ public final native int size() /*-{ return this.length; }-*/;
+ public final native Row get(int i) /*-{ return this[i]; }-*/;
+ }
+
+ public static native Rows parse(String json) /*-{
+ return eval('(' + json + ')')
+ }-*/;
+
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/I18nConstants.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/I18nConstants.java
new file mode 100644
index 00000000000..63423385f23
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/page/client/I18nConstants.java
@@ -0,0 +1,51 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.page.client;
+
+import com.google.gwt.core.client.GWT;
+
+public interface I18nConstants extends com.google.gwt.i18n.client.Constants {
+
+ static I18nConstants INSTANCE = GWT.create(I18nConstants.class);
+
+ @DefaultStringValue("Dependency")
+ String legendDependencies();
+
+ @DefaultStringValue("Suspect dependency (cycle)")
+ String legendCycles();
+
+ @DefaultStringValue("- uses >")
+ String legendUses();
+
+ @DefaultStringValue("No data")
+ String noData();
+
+ @DefaultStringValue("New window")
+ String newWindow();
+
+ @DefaultStringValue("Click to highlight, double-click to display more details.")
+ String cellTooltip();
+
+ @DefaultStringValue("Click to highlight, double-click to zoom.")
+ String rowTooltip();
+
+ @DefaultStringValue("Help")
+ String linkToHelp();
+}
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/ChidamberKemererWidget.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/ChidamberKemererWidget.java
new file mode 100644
index 00000000000..d05806e1a34
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/ChidamberKemererWidget.java
@@ -0,0 +1,40 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.widgets;
+
+import org.sonar.api.web.AbstractRubyTemplate;
+import org.sonar.api.web.RubyRailsWidget;
+import org.sonar.api.web.UserRole;
+
+@UserRole(UserRole.USER)
+public final class ChidamberKemererWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ public String getId() {
+ return "ckjm";
+ }
+
+ public String getTitle() {
+ return "2_Chidamber & Kemerer";
+ }
+
+ @Override
+ protected String getTemplatePath() {
+ return "/org/sonar/plugins/design/ui/widgets/chidamber_kemerer.html.erb";
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/FileDesignWidget.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/FileDesignWidget.java
new file mode 100644
index 00000000000..fc946c5c19b
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/FileDesignWidget.java
@@ -0,0 +1,40 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.widgets;
+
+import org.sonar.api.web.AbstractRubyTemplate;
+import org.sonar.api.web.RubyRailsWidget;
+import org.sonar.api.web.UserRole;
+
+@UserRole(UserRole.USER)
+public final class FileDesignWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ public String getId() {
+ return "file-design";
+ }
+
+ public String getTitle() {
+ return "1_File design";
+ }
+
+ @Override
+ protected String getTemplatePath() {
+ return "/org/sonar/plugins/design/ui/widgets/file_design.html.erb";
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/PackageDesignWidget.java b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/PackageDesignWidget.java
new file mode 100644
index 00000000000..807cb5b5329
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/ui/widgets/PackageDesignWidget.java
@@ -0,0 +1,40 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.ui.widgets;
+
+import org.sonar.api.web.AbstractRubyTemplate;
+import org.sonar.api.web.RubyRailsWidget;
+import org.sonar.api.web.UserRole;
+
+@UserRole(UserRole.USER)
+public final class PackageDesignWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ public String getId() {
+ return "package-design";
+ }
+
+ public String getTitle() {
+ return "1_Package design";
+ }
+
+ @Override
+ protected String getTemplatePath() {
+ return "/org/sonar/plugins/design/ui/widgets/package_design.html.erb";
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/DependenciesTab.gwt.xml b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/DependenciesTab.gwt.xml
new file mode 100644
index 00000000000..c20b988ba44
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/DependenciesTab.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+
+ <stylesheet src='dependencies-tab.css'/>
+ <entry-point class="org.sonar.plugins.design.ui.dependencies.client.DependenciesTab"/>
+
+ <extend-property name="locale" values="en"/>
+ <extend-property name="locale" values="fr"/>
+
+</module>
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/client/I18nConstants_fr.properties b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..ab2e9eab623
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/client/I18nConstants_fr.properties
@@ -0,0 +1,7 @@
+afferentCouplings= Dépendances entrantes
+efferentCouplings= Dépendances sortantes
+classes=Classes
+dit=Profondeur d'héritage
+noc=Nombre d'enfants
+lcom4=Manque de cohésion des méthodes
+noData=Aucune information \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/dependencies-tab.css b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/dependencies-tab.css
new file mode 100644
index 00000000000..4bee7667fb1
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/dependencies-tab.css
@@ -0,0 +1,23 @@
+#deps {
+ padding: 10px;
+}
+
+#deps .data {
+ min-width: 380px;
+}
+#deps .col {
+ margin: 0 10px 0 0;
+ border-right: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
+#deps .col td {
+ padding: 3px 5px;
+}
+
+#deps .coltitle {
+ background-color: #efefef;
+ padding: 3px;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/test.html b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/test.html
new file mode 100644
index 00000000000..55cbc551234
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/dependencies/public/test.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Sources</title>
+ <link href="http://localhost:9000/stylesheets/sonar.css" media="all" rel="Stylesheet" type="text/css" />
+ <script src="http://localhost:9000/javascripts/sonar.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+var registeredTabs = [];
+var config = {
+ "sonar_url": "http://localhost:9000",
+ "viewer_resource_key" : "org.apache.struts:struts-core:org.apache.struts.action.ActionServlet"
+};
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="test.html#" onclick="javascript:$('error').hide();return false;">hide</a>]</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="test.html#" onclick="javascript:$('warning').hide();return false;">hide</a>]</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="test.html#" onclick="javascript:$('info').hide();return false;">hide</a>]</div>
+
+<div id="resource_viewers">
+ <div id='loading'></div>
+</div>
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.design.ui.dependencies.DependenciesTab.nocache.js"></script>
+
+<a href="test.html#" onclick="load_org_sonar_plugins_design_ui_dependencies_DependenciesTab();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/Lcom4Tab.gwt.xml b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/Lcom4Tab.gwt.xml
new file mode 100644
index 00000000000..2de9ebb7965
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/Lcom4Tab.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+
+ <stylesheet src="lcom4.css"/>
+ <entry-point class="org.sonar.plugins.design.ui.lcom4.client.Lcom4Tab"/>
+
+ <extend-property name="locale" values="en"/>
+ <extend-property name="locale" values="fr"/>
+
+</module>
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.css b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.css
new file mode 100644
index 00000000000..bbd35eb6237
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.css
@@ -0,0 +1,23 @@
+.lcom4blocks {
+ width: 100%;
+ margin-top: 10px;
+}
+.lcom4blocks td {
+ vertical-align: top;
+}
+.lcom4blocks .index {
+ font-size: 130%;
+ text-align: center;
+ width: 2em;
+ margin-top: 3px;
+}
+.lcom4block {
+ width: 100%;
+ border: 1px solid #ccc;
+ margin-bottom: 15px;
+ background-color: #eee;
+}
+.lcom4row {
+ padding: 2px 5px;
+ word-wrap:break-word;
+} \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.html b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.html
new file mode 100644
index 00000000000..8ae47cdd1c0
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/lcom4/public/lcom4.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <meta name="gwt:property" content="locale=fr_CH">
+
+ <title>LCOM4</title>
+ <link href="http://localhost:9000/stylesheets/sonar.css" media="all" rel="Stylesheet" type="text/css" />
+ <script src="http://localhost:9000/javascripts/sonar.js" type="text/javascript"></script>
+</head>
+
+<body>
+<h1>LCOM4</h1>
+<div id="gwtpage">
+</div>
+<script type="text/javascript">
+var config = {
+ "sonar_url": "http://localhost:9000",
+ "metric":"lcom4",
+ "resource":[{"id":11, "key":"org.apache.struts:struts-core:org.apache.struts.action.Action","scope": "FIL", "qualifier": "CLA", "name": "ActionServlet", "lang":"java"}]
+};
+ var modules = {};
+</script>
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.design.ui.lcom4.Lcom4Tab.nocache.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/LibrariesPage.gwt.xml b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/LibrariesPage.gwt.xml
new file mode 100644
index 00000000000..f19861a3644
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/LibrariesPage.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <stylesheet src="libraries.css"/>
+
+ <entry-point class="org.sonar.plugins.design.ui.libraries.client.LibrariesPage"/>
+
+ <extend-property name="locale" values="en"/>
+ <extend-property name="locale" values="fr"/>
+
+</module>
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/client/I18nConstants_fr.properties b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..52e1a94be75
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/client/I18nConstants_fr.properties
@@ -0,0 +1,6 @@
+filter: Filtre:
+displayTests: Afficher les librairies de test
+expand: Développer
+collapse: Réduire
+noLibraries: Aucune librairie
+usageLink: Utilisations \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/libraries.css b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/libraries.css
new file mode 100644
index 00000000000..191ade42a86
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/libraries.css
@@ -0,0 +1,22 @@
+.libs {
+ border: 1px solid #ccc;
+ padding: 5px 10px;
+ margin-top: 5px;
+}
+.libs .gwt-Tree img {
+ /* hack to align rows */
+ margin-left: 6px;
+}
+.libFilter input {
+ margin-right: 3px;
+}
+.libFilter {
+
+}
+.libFilter td {
+ padding-right: 15px;
+}
+.nolibs {
+ padding: 0 0 0 20px;
+ font-size: 93%;
+} \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/test.html b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/test.html
new file mode 100644
index 00000000000..1f5af2b1bbc
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/libraries/public/test.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <meta name="gwt:property" content="locale=fr_CH">
+
+ <title>Libraries</title>
+ <link href="http://localhost:9000/stylesheets/sonar.css" media="all" rel="Stylesheet" type="text/css" />
+ <script src="http://localhost:9000/javascripts/sonar.js" type="text/javascript"></script>
+</head>
+
+<body>
+<h1>Libraries</h1>
+<div id="gwtpage">
+</div>
+<script type="text/javascript">
+var config = {
+ "sonar_url": "http://localhost:9000",
+ "resource":[{"id":1141, "key":"org.apache.struts:struts-parent","scope": "PRJ", "qualifier": "TRK", "name": "Struts Core", "lang":"java"}]
+};
+ var modules = {};
+
+var rp = {
+ "page":"org.sonar.plugins.design.ui.libraries.LibrariesPage"
+ ,"filter2":"commons"
+ };
+
+</script>
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.design.ui.libraries.LibrariesPage.nocache.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/DesignPage.gwt.xml b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/DesignPage.gwt.xml
new file mode 100644
index 00000000000..7655704d886
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/DesignPage.gwt.xml
@@ -0,0 +1,12 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <stylesheet src="design.css"/>
+
+ <entry-point class="org.sonar.plugins.design.ui.page.client.DesignPage"/>
+
+ <extend-property name="locale" values="en"/>
+ <extend-property name="locale" values="fr"/>
+</module>
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/client/I18nConstants_fr.properties b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..b2e15b28ca0
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/client/I18nConstants_fr.properties
@@ -0,0 +1,8 @@
+legendDependencies=Dépendance
+legendCycles=Dépendance suspecte (cycle)
+legendUses=- dépend de >
+noData=Aucune information
+noWindow=Nouvelle fenêtre
+cellTooltip=Cliquez pour sélectionner, double-cliquez pour afficher plus de détails.
+rowTooltip=Cliquez pour sélectionner, double-cliquez pour zoomer.
+linkToHelp=Aide \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/design.css b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/design.css
new file mode 100644
index 00000000000..a5a8be74221
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/design.css
@@ -0,0 +1,227 @@
+.dsm {
+ border-collapse: separate;
+ background-color: #fff;
+ font-family: sans-serif;
+ font-size: 9pt;
+ color: #222;
+ margin-bottom: 10px;
+ padding: 5px 10px;
+}
+
+/*------- LEGEND */
+
+#dsmlegend {
+ padding: 5px;
+ margin: 0 0 5px 5px;
+}
+#dsmlegend td {
+ white-space: nowrap;
+}
+#dsmlegend .square {
+ width: 11px;
+ height: 14px;
+ display: inline-block;
+ vertical-align: bottom;
+ padding-left: 3px;
+ margin-bottom: 1px;
+ margin-right: 5px;
+}
+#dsmlegend .gwt-Label {
+ padding-right: 5px;
+}
+#dsmhelp {
+ margin: 0 0 5px 5px;
+}
+#dsmhelp img {
+ margin-right: 4px;
+}
+
+.dsm .gray {
+ background-color: #ddd;
+}
+.dsm .red {
+ background-color: #c13333;
+}
+.dsm .green {
+ background-color: #46c557;
+ background-image:url('green-stripes.png');
+}
+.dsm .blue {
+ background-color: #4B9FD5;
+}
+.dsm .yellow {
+ background-color: #d49e37;
+}
+.dsm .space {
+ display: inline-block;
+ width: 12px;
+}
+
+
+
+/*------- LEFT HEADER */
+
+/* header table */
+.dsm .htable {
+ padding: 1px;
+ border-top: 1px solid #676b9a;
+ border-right: 1px solid #676b9a;
+}
+
+/* header title */
+.dsm .ht {
+ background-color: #CAE3F2;
+ color: #434343;
+ height: 16px;
+ padding: 2px 0 3px 5px;
+ border-bottom: 1px solid #676b9a;
+ border-left: 1px solid #676b9a;
+ cursor: pointer;
+ white-space: nowrap;
+}
+.dsm .ht-s {
+ background-color: #4B9FD5;
+ color: #efefef;
+}
+.dsm .ht img {
+ vertical-align: text-bottom;
+}
+
+/* header indicator */
+.dsm .hi {
+ background-color: #CAE3F2;
+ width: 8px;
+ height: 21px;
+ border-bottom: 1px solid #676b9a;
+}
+.dsm .hi-s {
+ background-color: #4B9FD5;
+}
+.dsm .hi-h {
+ background-color: #46c557;
+ background-image:url('green-stripes.png');
+}
+.dsm .hi-l {
+ background-color: #d49e37;
+}
+
+
+
+/*------- GRID */
+
+.dsm .gtable {
+ padding: 1px;
+ border-top: 1px solid #ccc;
+
+}
+
+/* cell on bottom left */
+.dsm .cbl {
+ height: 16px;
+ width: 23px;
+ padding-top: 5px;
+ background-color: #fff;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ color: #434343;
+ text-align: center;
+ cursor: pointer;
+}
+.dsm .cbl span {
+ background-color: #ddd;
+ padding: 2px 2px 0 2px;
+}
+
+/*
+ cell on top right : has feedback edge
+ total height: height + padding-top + 2*border = 25px
+*/
+.dsm .ctr {
+ height: 16px;
+ width: 23px;
+ padding-top: 5px;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ font-weight: normal;
+ text-align: center;
+ cursor: pointer;
+}
+.dsm .ctr span {
+ background-color: #c13333;
+ color: #fff;
+ padding: 2px 2px 0 2px;
+}
+
+/* cell on diagonal */
+.dsm .cd {
+ height: 16px;
+ width: 23px;
+ padding-top: 5px;
+ background-color: #fff;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ color: #434343;
+ text-align: center;
+ cursor: pointer;
+}
+
+/* selected cell, mode 1 */
+.dsm .cbl-s1, .dsm .ctr-s1, .dsm .cd-s1 {
+ background-color: #b580c9;
+}
+
+/* selected cell, mode 2 */
+.dsm .cbl-s2 {
+ background-color: #CAE3F2;
+}
+.dsm .ctr-s2 {
+ background-color: #CAE3F2;
+}
+.dsm .cd-s2 {
+ background-color: #4B9FD5;
+ color: #efefef;
+}
+
+/* comb 1 */
+.dsm .cbl-c1, .dsm .ctr-c1, .dsm .cd-c1 {
+ background-color: #46c557;
+ background-image:url('green-stripes.png');
+}
+
+/* comb 2 */
+.dsm .cbl-c2, .dsm .ctr-c2, .dsm .cd-c2 {
+ background-color: #d49e37;
+}
+
+
+/*---------- DEPENDENCY INFO */
+.depInfo {
+ background-color: #fff;
+ border-top-width: 0;
+ border-bottom: 2px solid #ccc;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ width: 100%;
+}
+.depInfo td {
+ padding: 3px 2px;
+}
+.link {
+ text-decoration:underline;
+ cursor: pointer;
+}
+.newwindow {
+ text-decoration:underline;
+ cursor: pointer;
+ padding: 3px;
+ color: #777;
+}
+.depInfoHeader {
+ background-color: #efefef;
+ border-top: 2px solid #ccc;
+ border-bottom: 2px solid #ccc;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ padding: 5px;
+ width: 100%;
+}
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/green-stripes.png b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/green-stripes.png
new file mode 100644
index 00000000000..56509c4dab2
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/green-stripes.png
Binary files differ
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/test.html b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/test.html
new file mode 100644
index 00000000000..daf6bdeef7e
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/test.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <meta name="gwt:property" content="locale=fr_CH">
+
+ <title>DSM</title>
+ <link href="http://localhost:9000/stylesheets/sonar.css" media="all" rel="Stylesheet" type="text/css" />
+ <script src="http://localhost:9000/javascripts/sonar.js" type="text/javascript"></script>
+</head>
+
+<body>
+<h1>Design Structure Matrix</h1>
+<div id="gwtpage">
+</div>
+<script type="text/javascript">
+var config = {
+ "sonar_url": "http://localhost:9000",
+ "resource":[{"id":1, "key":"org.apache.struts:struts-parent","scope": "PRJ", "qualifier": "BRC", "name": "Struts Core", "lang":"java"}]
+};
+ var modules = {};
+</script>
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.design.ui.page.DesignPage.nocache.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/yellow-stripes.png b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/yellow-stripes.png
new file mode 100644
index 00000000000..fb4cccc1605
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/page/public/yellow-stripes.png
Binary files differ
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/chidamber_kemerer.html.erb b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/chidamber_kemerer.html.erb
new file mode 100644
index 00000000000..2f772d05ce7
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/chidamber_kemerer.html.erb
@@ -0,0 +1,50 @@
+<%
+lcom=measure('lcom4')
+rfc=measure('rfc')
+if lcom || rfc
+ lcom_distribution=measure('lcom4_distribution')
+ rfc_distribution=measure('rfc_distribution')
+ suspect_lcom4_density=measure('suspect_lcom4_density')
+ %>
+<table width="100%">
+ <tbody>
+ <tr>
+ <% if lcom %>
+ <td width="50%" valign="top">
+ <div class="dashbox">
+ <h3>LCOM4</h3>
+ <p><span class="big"><%= format_measure(lcom, :suffix => '', :default => '-', :url => url_for_drilldown('lcom4')) %> <%= tendency_icon(lcom, false) %></span> /class</p>
+ <p><%= format_measure(suspect_lcom4_density, :suffix => ' files having LCOM4>1', :url => url_for_drilldown('lcom4')) %> <%= tendency_icon(suspect_lcom4_density) %></p>
+ </div>
+ </td>
+ <% end %>
+ <% if rfc %>
+ <td width="50%" valign="top">
+ <div class="dashbox">
+ <h3>RFC</h3>
+ <p><span class="big"><%= format_measure(rfc, :suffix => '', :default => '-', :url => url_for_drilldown('rfc')) %> <%= tendency_icon(rfc, false) %></span> /class</p>
+ </div>
+ </td>
+ <% end %>
+ </tr>
+ <tr>
+ <td>
+ <%
+ if suspect_lcom4_density && suspect_lcom4_density.value>1.0 && lcom_distribution && !lcom_distribution.data.blank?
+ query="ck=distbar&c=777777&w=180&h=100&fs=8&bgc=ffffff&v=" + u(lcom_distribution.data)
+ %>
+ <a href="<%= url_for_drilldown('lcom4') -%>"><%= chart(query, :id => 'lcom4_distribution', :alt => '') -%></a>
+ <% end %>
+ </td>
+ <td>
+ <%
+ if rfc_distribution && !rfc_distribution.data.blank?
+ query="ck=distbar&c=777777&w=180&h=100&fs=8&bgc=ffffff&v=" + u(rfc_distribution.data)
+ %>
+ <a href="<%= url_for_drilldown('rfc') -%>"><%= chart(query, :id => 'rfc_distribution', :alt => '') -%></a>
+ <% end %>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<% end %>
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/file_design.html.erb b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/file_design.html.erb
new file mode 100644
index 00000000000..70041753975
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/file_design.html.erb
@@ -0,0 +1,32 @@
+<%
+ file_tangle_index=measure('file_tangle_index')
+ if file_tangle_index
+ file_cycles=measure('file_cycles')
+ file_feedback_edges=measure('file_feedback_edges')
+%>
+<table width="100%">
+ <tbody>
+ <tr>
+ <td valign="top">
+ <div class="dashbox">
+ <p class="title">File tangle index</p>
+ <p><span class="big"><%= format_measure(file_tangle_index, :suffix => '', :default => '-', :url => url_for_drilldown('file_tangle_index')) %> <%= tendency_icon(file_tangle_index, false) %></span></p>
+ <% if file_cycles %>
+ <p>> <%= format_measure(file_cycles, :suffix => ' cycles', :url => url_for_drilldown('file_cycles')) %></p>
+ <% end %>
+ </div>
+ </td>
+ <td valign="top">
+ <% if file_feedback_edges %>
+ <div class="dashbox">
+ <p class="title">Suspect file dependencies</p>
+ <p><span class="big"><%= format_measure(file_feedback_edges, :url => url_for_drilldown('file_feedback_edges')) %> <%= tendency_icon(file_feedback_edges) %></span></p>
+ </div>
+ <% end %>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<%
+ end
+%> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/package_design.html.erb b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/package_design.html.erb
new file mode 100644
index 00000000000..59136259ee1
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/main/resources/org/sonar/plugins/design/ui/widgets/package_design.html.erb
@@ -0,0 +1,38 @@
+<%
+ package_tangle_index=measure('package_tangle_index')
+ if package_tangle_index
+%>
+<table width="100%">
+ <tbody>
+ <tr>
+ <%
+ if package_tangle_index
+ package_cycles=measure('package_cycles')
+ package_feedback_edges=measure('package_feedback_edges')
+ package_tangles=measure('package_tangles')
+ %>
+ <td valign="top">
+ <div class="dashbox">
+ <p class="title">Package tangle index</p>
+ <p><span class="big"><%= format_measure(package_tangle_index, :suffix => '', :default => '-', :url => url_for_drilldown('package_tangle_index')) %> <%= tendency_icon(package_tangle_index, false) %></span></p>
+ <% if package_cycles %>
+ <p>> <%= format_measure(package_cycles, :suffix => ' cycles', :url => url_for_drilldown('package_cycles')) %></p>
+ <% end %>
+ </div>
+ </td>
+ <td valign="top">
+ <% if package_feedback_edges || package_tangles %>
+ <div class="dashbox">
+ <p class="title">Dependencies to cut</p>
+ <p><%= format_measure(package_feedback_edges, :suffix => ' between packages', :url => url_for_drilldown('package_feedback_edges')) %> <%= tendency_icon(package_feedback_edges) %></p>
+ <p><%= format_measure(package_tangles, :suffix => ' between files', :url => url_for_drilldown('package_tangles')) %> <%= tendency_icon(package_tangles) %></p>
+ </div>
+ <% end %>
+ </td>
+ <% end %>
+ </tr>
+ </tbody>
+</table>
+<%
+ end
+%> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/DsmSerializerTest.java b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/DsmSerializerTest.java
new file mode 100644
index 00000000000..8b42373ccad
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/DsmSerializerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.sonar.api.design.Dependency;
+import org.sonar.api.resources.JavaPackage;
+import org.sonar.api.resources.Resource;
+import org.sonar.graph.DirectedGraph;
+import org.sonar.graph.Dsm;
+import org.sonar.graph.DsmManualSorter;
+
+import java.io.IOException;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+public class DsmSerializerTest {
+ @Test
+ public void serializeEmptyDsm() throws IOException {
+ Dsm dsm = new Dsm(new DirectedGraph());
+ assertThat(DsmSerializer.serialize(dsm), is("[]"));
+ }
+
+ @Test
+ public void serialize() throws IOException {
+ Resource foo = new JavaPackage("org.foo").setId(7);
+ Resource bar = new JavaPackage("org.bar").setId(8);
+ Dependency dep = new Dependency(foo, bar).setId(30l).setWeight(1);
+
+ DirectedGraph<Resource, Dependency> graph = new DirectedGraph<Resource, Dependency>();
+ graph.addVertex(foo);
+ graph.addVertex(bar);
+ graph.addEdge(dep);
+
+
+ Dsm<Resource> dsm = new Dsm<Resource>(graph);
+ DsmManualSorter.sort(dsm, bar, foo); // for test reproductibility
+ String json = IOUtils.toString(getClass().getResourceAsStream("/org/sonar/plugins/design/batch/DsmSerializerTest/dsm.json"));
+ assertThat(DsmSerializer.serialize(dsm), is(json));
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/MavenDependenciesSensorTest.java b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/MavenDependenciesSensorTest.java
new file mode 100644
index 00000000000..3ab99fbc620
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/MavenDependenciesSensorTest.java
@@ -0,0 +1,27 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.junit.Ignore;
+
+@Ignore
+public class MavenDependenciesSensorTest {
+
+}
diff --git a/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/OldDependenciesPurgeTest.java b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/OldDependenciesPurgeTest.java
new file mode 100644
index 00000000000..300139b78e3
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/OldDependenciesPurgeTest.java
@@ -0,0 +1,40 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class OldDependenciesPurgeTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeOldDependencies() throws SQLException {
+ assertPurge("purgeOldDependencies");
+ }
+
+ private void assertPurge(String testName) {
+ setupData(testName);
+ new OldDependenciesPurge(getSession()).purge(null);
+ checkTables(testName, "dependencies");
+ }
+
+}
diff --git a/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecoratorTest.java b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecoratorTest.java
new file mode 100644
index 00000000000..28636be3167
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/java/org/sonar/plugins/design/batch/SuspectLcom4DensityDecoratorTest.java
@@ -0,0 +1,62 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.design.batch;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.JavaPackage;
+
+import java.util.Arrays;
+
+import static org.mockito.Mockito.*;
+
+public class SuspectLcom4DensityDecoratorTest {
+
+ @Test
+ public void doNotDecorateFiles() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.FILES)).thenReturn(new Measure(CoreMetrics.FILES, 1.0));
+ when(context.getMeasure(CoreMetrics.LCOM4)).thenReturn(newLcom4(3));
+
+ SuspectLcom4DensityDecorator decorator = new SuspectLcom4DensityDecorator();
+ decorator.decorate(new JavaFile("org.foo.Bar"), context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.SUSPECT_LCOM4_DENSITY), anyDouble());
+ }
+
+ @Test
+ public void decoratePackages() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.FILES)).thenReturn(new Measure(CoreMetrics.FILES, 4.0));
+ when(context.getChildrenMeasures(CoreMetrics.LCOM4)).thenReturn(Arrays.asList(newLcom4(1), newLcom4(3), newLcom4(5), newLcom4(1)));
+
+ SuspectLcom4DensityDecorator decorator = new SuspectLcom4DensityDecorator();
+ decorator.decorate(new JavaPackage("org.foo"), context);
+
+ verify(context).saveMeasure(CoreMetrics.SUSPECT_LCOM4_DENSITY, 50.0);
+ }
+
+ private Measure newLcom4(int lcom4) {
+ return new Measure(CoreMetrics.LCOM4, (double)lcom4);
+ }
+}
diff --git a/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/DsmSerializerTest/dsm.json b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/DsmSerializerTest/dsm.json
new file mode 100644
index 00000000000..d0b8861b439
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/DsmSerializerTest/dsm.json
@@ -0,0 +1 @@
+[{"i":8,"n":"org.bar","q":"PAC","v":[{},{"i":30,"w":1}]},{"i":7,"n":"org.foo","q":"PAC","v":[{},{}]}] \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/MavenDependenciesSensorTest/getEffectiveLibraries/pom.xml b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/MavenDependenciesSensorTest/getEffectiveLibraries/pom.xml
new file mode 100644
index 00000000000..1ff8aa38516
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/MavenDependenciesSensorTest/getEffectiveLibraries/pom.xml
@@ -0,0 +1,29 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>fake.group</groupId>
+ <artifactId>fake-artifact</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <name>Fake</name>
+ <dependencies>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.4</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.4</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.7</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies-result.xml b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies-result.xml
new file mode 100644
index 00000000000..6b0bf1b88c4
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies-result.xml
@@ -0,0 +1,102 @@
+<dataset>
+ <!-- first project with one package -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- second project with two packages -->
+ <projects long_name="[null]" id="3" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact2" name="project2"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="4" scope="DIR" qualifier="PAC" kee="mygroup:myartifact2:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="5" scope="DIR" qualifier="PAC" kee="mygroup:myartifact2:my.package2" name="package2"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+ <!-- old snapshots -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="3" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="5" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="5"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="3" status="P" islast="false"
+ path="[null]"/>
+
+ <!-- last snapshots -->
+ <snapshots depth="[null]" id="6" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="7" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="6" root_project_id="[null]" root_snapshot_id="6" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="8" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="9" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="8" root_project_id="[null]" root_snapshot_id="8" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="10" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="8" root_project_id="[null]" root_snapshot_id="8" status="P" islast="true"
+ path="[null]"/>
+
+
+ <!-- old dependencies -->
+ <!--<dependencies id="1" from_resource_id="3" from_snapshot_id="3" to_resource_id="1" to_snapshot_id="1"-->
+ <!--parent_dependency_id="[null]" project_snapshot_id="3"-->
+ <!--dep_usage="compile" dep_weight="1" from_scope="PRJ" to_scope="PRJ"/>-->
+
+ <!--<dependencies id="2" from_resource_id="4" from_snapshot_id="4" to_resource_id="5" to_snapshot_id="5"-->
+ <!--parent_dependency_id="[null]" project_snapshot_id="3"-->
+ <!--dep_usage="uses" dep_weight="3" from_scope="DIR" to_scope="DIR"/>-->
+
+
+ <!-- last dependencies -->
+ <dependencies id="3" from_resource_id="3" from_snapshot_id="8" to_resource_id="1" to_snapshot_id="6"
+ parent_dependency_id="[null]" project_snapshot_id="8"
+ dep_usage="compile" dep_weight="1" from_scope="PRJ" to_scope="PRJ"/>
+
+ <dependencies id="4" from_resource_id="4" from_snapshot_id="9" to_resource_id="5" to_snapshot_id="10"
+ parent_dependency_id="[null]" project_snapshot_id="8"
+ dep_usage="uses" dep_weight="3" from_scope="DIR" to_scope="DIR"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies.xml b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies.xml
new file mode 100644
index 00000000000..5ffbb85ac9f
--- /dev/null
+++ b/plugins/sonar-design-plugin/src/test/resources/org/sonar/plugins/design/batch/OldDependenciesPurgeTest/purgeOldDependencies.xml
@@ -0,0 +1,102 @@
+<dataset>
+ <!-- first project with one package -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- second project with two packages -->
+ <projects long_name="[null]" id="3" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact2" name="project2"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="4" scope="DIR" qualifier="PAC" kee="mygroup:myartifact2:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="5" scope="DIR" qualifier="PAC" kee="mygroup:myartifact2:my.package2" name="package2"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+ <!-- old snapshots -->
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="3" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="5" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="5"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="3" status="P" islast="false"
+ path="[null]"/>
+
+ <!-- last snapshots -->
+ <snapshots depth="[null]" id="6" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="7" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="6" root_project_id="[null]" root_snapshot_id="6" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="8" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="9" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="8" root_project_id="[null]" root_snapshot_id="8" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="10" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="8" root_project_id="[null]" root_snapshot_id="8" status="P" islast="true"
+ path="[null]"/>
+
+
+ <!-- old dependencies -->
+ <dependencies id="1" from_resource_id="3" from_snapshot_id="3" to_resource_id="1" to_snapshot_id="1"
+ parent_dependency_id="[null]" project_snapshot_id="3"
+ dep_usage="compile" dep_weight="1" from_scope="PRJ" to_scope="PRJ"/>
+
+ <dependencies id="2" from_resource_id="4" from_snapshot_id="4" to_resource_id="5" to_snapshot_id="5"
+ parent_dependency_id="[null]" project_snapshot_id="3"
+ dep_usage="uses" dep_weight="3" from_scope="DIR" to_scope="DIR"/>
+
+
+ <!-- last dependencies -->
+ <dependencies id="3" from_resource_id="3" from_snapshot_id="8" to_resource_id="1" to_snapshot_id="6"
+ parent_dependency_id="[null]" project_snapshot_id="8"
+ dep_usage="compile" dep_weight="1" from_scope="PRJ" to_scope="PRJ"/>
+
+ <dependencies id="4" from_resource_id="4" from_snapshot_id="9" to_resource_id="5" to_snapshot_id="10"
+ parent_dependency_id="[null]" project_snapshot_id="8"
+ dep_usage="uses" dep_weight="3" from_scope="DIR" to_scope="DIR"/>
+
+</dataset> \ No newline at end of file