aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/sonar-core-gwt
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2010-12-02 14:05:25 +0000
committersimonbrandhof <simon.brandhof@gmail.com>2010-12-02 14:05:25 +0000
commit625da3dc6bc5099e7b9c22a31b005adb5318b6dc (patch)
treef3dfb9a18f1a44d5b9b40d2f58596a306f0efff8 /plugins/sonar-core-gwt
parent1a51c6bf53bf1f526c3c39aa86569b042f8be810 (diff)
downloadsonarqube-625da3dc6bc5099e7b9c22a31b005adb5318b6dc.tar.gz
sonarqube-625da3dc6bc5099e7b9c22a31b005adb5318b6dc.zip
extract GWT components from sonar-core-plugin
Diffstat (limited to 'plugins/sonar-core-gwt')
-rw-r--r--plugins/sonar-core-gwt/pom.xml94
-rw-r--r--plugins/sonar-core-gwt/sonar-core-gwt.iml123
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/Clouds.java42
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.java119
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.java197
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java53
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.java50
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java146
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.java77
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.java40
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.java123
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.java64
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.java39
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.java113
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.java41
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java161
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java79
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.java41
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.java64
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.java69
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java124
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java133
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.java126
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.java127
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java149
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.java41
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.java168
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.java87
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java30
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java30
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.java94
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java156
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java224
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.java41
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.java39
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java159
-rw-r--r--plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java205
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml11
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css29
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/test.html39
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml10
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html41
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml10
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html26
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml13
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css48
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html43
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml10
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties16
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css102
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html36
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml13
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css88
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.pngbin0 -> 701 bytes
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.pngbin0 -> 666 bytes
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.pngbin0 -> 781 bytes
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.pngbin0 -> 786 bytes
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html41
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.gwt.xml12
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties2
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css25
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html41
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.gwt.xml12
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties5
-rw-r--r--plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html41
65 files changed, 4382 insertions, 0 deletions
diff --git a/plugins/sonar-core-gwt/pom.xml b/plugins/sonar-core-gwt/pom.xml
new file mode 100644
index 00000000000..308512c7f21
--- /dev/null
+++ b/plugins/sonar-core-gwt/pom.xml
@@ -0,0 +1,94 @@
+<?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.5-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <artifactId>sonar-core-gwt</artifactId>
+ <packaging>jar</packaging>
+ <name>Sonar :: Plugins :: Core GWT</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-gwt-api</artifactId>
+ <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>
+ <testResources>
+ <testResource>
+ <directory>${basedir}/src/main/resources</directory>
+ </testResource>
+ <testResource>
+ <directory>${basedir}/src/test/resources</directory>
+ </testResource>
+ </testResources>
+
+ <plugins>
+ <plugin>
+ <groupId>com.atlassian.maven.plugins</groupId>
+ <artifactId>maven-clover2-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <!-- GWT classes -->
+ <exclude>**/client/**/*.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>gwt-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <configuration>
+ <modules>
+ <module>org.sonar.plugins.core.ui.pageselector.PageSelector</module>
+ <module>org.sonar.plugins.core.clouds.GwtClouds</module>
+ <module>org.sonar.plugins.core.violationsviewer.ViolationsViewer</module>
+ <module>org.sonar.plugins.core.coverageviewer.CoverageViewer</module>
+ <module>org.sonar.plugins.core.defaultsourceviewer.GwtDefaultSourceViewer</module>
+ <module>org.sonar.plugins.core.duplicationsviewer.DuplicationsViewer</module>
+ <module>org.sonar.plugins.core.testdetailsviewer.TestsViewer</module>
+ <module>org.sonar.plugins.core.hotspots.GwtHotspots</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>
+ </plugins>
+ </build>
+</project> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/sonar-core-gwt.iml b/plugins/sonar-core-gwt/sonar-core-gwt.iml
new file mode 100644
index 00000000000..d34895789c8
--- /dev/null
+++ b/plugins/sonar-core-gwt/sonar-core-gwt.iml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="gwt" name="GWT">
+ <configuration>
+ <setting name="compilerMaxHeapSize" value="128" />
+ <setting name="gwtScriptOutputStyle" value="DETAILED" />
+ <setting name="gwtSdkType" value="maven" />
+ <setting name="gwtSdkUrl" value="file://$MAVEN_REPOSITORY$/com/google/gwt/gwt-dev/2.0.4/" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="sonar-plugin-api" />
+ <orderEntry type="module" module-name="sonar-check-api" />
+ <orderEntry type="module" module-name="sonar-colorizer" />
+ <orderEntry type="library" name="Maven: commons-io:commons-io:1.4" level="project" />
+ <orderEntry type="module" module-name="sonar-channel" />
+ <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.5.6" level="project" />
+ <orderEntry type="module" module-name="sonar-duplications" />
+ <orderEntry type="module" module-name="sonar-graph" />
+ <orderEntry type="library" name="Maven: commons-lang:commons-lang:2.5" level="project" />
+ <orderEntry type="library" name="Maven: com.google.collections:google-collections:1.0" level="project" />
+ <orderEntry type="module" module-name="sonar-squid" />
+ <orderEntry type="library" name="Maven: org.picocontainer:picocontainer:2.10.2" level="project" />
+ <orderEntry type="library" name="Maven: javax.annotation:jsr250-api:1.0" level="project" />
+ <orderEntry type="library" name="Maven: javax.inject:javax.inject:1" level="project" />
+ <orderEntry type="library" name="Maven: org.hibernate:hibernate-core:3.3.2.GA" level="project" />
+ <orderEntry type="library" name="Maven: antlr:antlr:2.7.6" level="project" />
+ <orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
+ <orderEntry type="library" name="Maven: dom4j:dom4j:1.6.1" level="project" />
+ <orderEntry type="library" name="Maven: xml-apis:xml-apis:1.3.03" level="project" />
+ <orderEntry type="library" name="Maven: org.hibernate:hibernate-annotations:3.4.0.GA" level="project" />
+ <orderEntry type="library" name="Maven: org.hibernate:ejb3-persistence:1.0.2.GA" level="project" />
+ <orderEntry type="library" name="Maven: org.hibernate:hibernate-commons-annotations:3.1.0.GA" level="project" />
+ <orderEntry type="library" name="Maven: org.hibernate:hibernate-entitymanager:3.4.0.GA" level="project" />
+ <orderEntry type="library" name="Maven: javassist:javassist:3.4.GA" level="project" />
+ <orderEntry type="library" name="Maven: geronimo-spec:geronimo-spec-jta:1.0-M1" level="project" />
+ <orderEntry type="library" name="Maven: commons-dbcp:commons-dbcp:1.3" level="project" />
+ <orderEntry type="library" name="Maven: commons-pool:commons-pool:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven.shared:maven-dependency-tree:1.2" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-project:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-settings:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-model:2.0.8" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-utils:1.4.9" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-container-default:1.0-alpha-9" level="project" />
+ <orderEntry type="library" name="Maven: junit:junit:4.8.1" level="project" />
+ <orderEntry type="library" name="Maven: classworlds:classworlds:1.1" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-profile:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-artifact-manager:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-repository-metadata:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-artifact:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven.wagon:wagon-provider-api:1.0-beta-2" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-plugin-registry:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven.shared:maven-common-artifact-filters:1.2" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-plugin-api:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven.shared:maven-plugin-testing-harness:1.1" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-core:2.0.7" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.maven.wagon:wagon-file:1.0-beta-2" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-plugin-parameter-documenter:2.0.7" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.maven.wagon:wagon-http-lightweight:1.0-beta-2" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.maven.wagon:wagon-http-shared:1.0-beta-2" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: jtidy:jtidy:4aug2000r7-dev" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven.reporting:maven-reporting-api:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven.doxia:doxia-sink-api:1.0-alpha-7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-error-diagnostics:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: commons-cli:commons-cli:1.0" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.maven.wagon:wagon-ssh-external:1.0-beta-2" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.maven.wagon:wagon-ssh-common:1.0-beta-2" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-interactivity-api:1.0-alpha-4" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-plugin-descriptor:2.0.7" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.maven:maven-monitor:2.0.7" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.maven.wagon:wagon-ssh:1.0-beta-2" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: com.jcraft:jsch:0.1.27" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-archiver:1.0-alpha-7" level="project" />
+ <orderEntry type="library" name="Maven: commons-configuration:commons-configuration:1.6" level="project" />
+ <orderEntry type="library" name="Maven: commons-digester:commons-digester:1.8" level="project" />
+ <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.7.0" level="project" />
+ <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils-core:1.8.0" level="project" />
+ <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.4" level="project" />
+ <orderEntry type="library" name="Maven: org.hibernate:hibernate-ehcache:3.3.2.GA" level="project" />
+ <orderEntry type="library" name="Maven: net.sf.ehcache:ehcache:1.2.3" level="project" />
+ <orderEntry type="library" name="Maven: jfree:jfreechart:1.0.9" level="project" />
+ <orderEntry type="library" name="Maven: jfree:jcommon:1.0.12" level="project" />
+ <orderEntry type="library" name="Maven: org.slf4j:jcl-over-slf4j:1.5.6" level="project" />
+ <orderEntry type="library" name="Maven: org.slf4j:log4j-over-slf4j:1.5.6" level="project" />
+ <orderEntry type="library" name="Maven: com.thoughtworks.xstream:xstream:1.3.1" level="project" />
+ <orderEntry type="library" name="Maven: xpp3:xpp3:1.1.3.3" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.woodstox:woodstox-core-lgpl:4.0.4" level="project" />
+ <orderEntry type="library" name="Maven: stax:stax-api:1.0.1" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.woodstox:stax2-api:3.0.1" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.staxmate:staxmate:2.0.0" level="project" />
+ <orderEntry type="library" name="Maven: xerces:xercesImpl:2.8.1" level="project" />
+ <orderEntry type="library" name="Maven: xalan:xalan:2.7.1" level="project" />
+ <orderEntry type="library" name="Maven: xalan:serializer:2.7.1" level="project" />
+ <orderEntry type="module" module-name="sonar-gwt-api" scope="PROVIDED" />
+ <orderEntry type="module" module-name="sonar-ws-client" scope="PROVIDED" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: com.googlecode.json-simple:json-simple:1.1" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.gwt:gwt-user:2.0.4" level="project" />
+ <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.gwt:gwt-incubator:2.0.1" level="project" />
+ <orderEntry type="module" module-name="sonar-testing-harness" scope="TEST" />
+ <orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-all:1.8.5" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-all:1.1" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: xmlunit:xmlunit:1.2" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: ch.qos.logback:logback-classic:0.9.15" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: ch.qos.logback:logback-core:0.9.15" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: org.dbunit:dbunit:2.4.5" level="project" />
+ <orderEntry type="library" scope="TEST" name="Maven: hsqldb:hsqldb:1.8.0.10" level="project" />
+ <orderEntry type="module" module-name="sonar-plugin-api" scope="TEST" />
+ <orderEntry type="module" module-name="sonar-core" scope="TEST" />
+ <orderEntry type="library" scope="TEST" name="Maven: org.codehaus.plexus:plexus-classworlds:2.2.3" level="project" />
+ </component>
+</module>
+
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/Clouds.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/Clouds.java
new file mode 100644
index 00000000000..f0b319604b2
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/Clouds.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.core.clouds;
+
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.GwtPage;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.ResourceScope;
+import org.sonar.api.web.UserRole;
+import org.sonar.plugins.core.clouds.client.GwtClouds;
+
+@NavigationSection(NavigationSection.RESOURCE)
+@ResourceScope({Resource.SCOPE_SET, Resource.SCOPE_SPACE})
+@UserRole(UserRole.USER)
+public class Clouds extends GwtPage {
+
+ public String getGwtId() {
+ return GwtClouds.GWT_ID;
+ }
+
+ public String getTitle() {
+ return "Clouds";
+ }
+
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.java
new file mode 100644
index 00000000000..5202b4feebc
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.java
@@ -0,0 +1,119 @@
+/*
+ * 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.core.clouds.client;
+
+import org.sonar.plugins.core.clouds.client.model.Color;
+
+public class Calculator {
+
+ private Float minValue;
+ private Float maxValue;
+ private Float minPercent;
+ private Float maxPercent;
+
+ public Calculator(Float minPercent, Float maxPercent) {
+ this.minPercent = minPercent;
+ this.maxPercent = maxPercent;
+ }
+
+ public void updateMaxAndMin(Float value){
+ updateMaxValue(value);
+ updateMinValue(value);
+ }
+
+ public Integer getFontSizePercent(Integer value) {
+ float divisor = getMaxValue() - getMinValue();
+ float size = getMinPercent();
+ if (divisor != 0) {
+ float multiplier = (getMaxPercent() - getMinPercent()) / divisor;
+ size = getMinPercent() +
+ ((getMaxValue() - (getMaxValue() - (value - getMinValue()))) * multiplier);
+ }
+ return Float.valueOf(size).intValue();
+ }
+
+ public String getFontColor(float value) {
+ float interval = (getMaxPercent() - getMinPercent()) / 2f;
+ float mean = (getMinPercent() + getMaxPercent()) / 2f;
+
+ Color minColor = new Color(191/255f, 0f, 21/255f); // red
+ Color meanColor = new Color(77/255f, 5/255f, 177/255f); // purple
+ Color maxColor = new Color(23/255f, 96/255f, 191/255f); // blue
+
+ Color color;
+ if (value > mean) {
+ float valuePercent = ((value - mean) / interval) * 100f;
+ color = mixColorWith(maxColor, meanColor, valuePercent);
+ } else {
+ float valuePercent = ((mean - value) / interval) * 100f;
+ color = mixColorWith(minColor, meanColor, valuePercent);
+ }
+
+ int r = Float.valueOf(color.getRed()* 255f).intValue();
+ int g = Float.valueOf(color.getGreen() * 255f).intValue();
+ int b = Float.valueOf(color.getBlue() * 255f).intValue();
+
+ return ("rgb("+ r +","+ g +","+ b +")");
+ }
+
+ private Color mixColorWith(Color currentColor, Color mask, float value){
+ float opacity = value / 100f;
+
+ float r = (currentColor.getRed() * opacity) + (mask.getRed() * (1f - opacity));
+ float g = (currentColor.getGreen() * opacity) + (mask.getGreen() * (1f - opacity));
+ float b = (currentColor.getBlue() * opacity) + (mask.getBlue() * (1f - opacity));
+
+ return new Color(r, g, b);
+ }
+
+
+ private void updateMaxValue(Float value) {
+ if (maxValue == null) {
+ maxValue = value;
+ } else if (value > maxValue) {
+ maxValue = value;
+ }
+ }
+
+ private void updateMinValue(Float value) {
+ if (minValue == null) {
+ minValue = value;
+ } else if (value < minValue) {
+ minValue = value;
+ }
+ }
+
+
+ public Float getMinValue() {
+ return minValue;
+ }
+
+ public Float getMaxValue() {
+ return maxValue;
+ }
+
+ public Float getMinPercent() {
+ return minPercent;
+ }
+
+ public Float getMaxPercent() {
+ return maxPercent;
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.java
new file mode 100644
index 00000000000..4770f3d64ba
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.java
@@ -0,0 +1,197 @@
+/*
+ * 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.core.clouds.client;
+
+import com.google.gwt.core.client.JavaScriptException;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.api.web.gwt.client.AbstractPage;
+import org.sonar.api.web.gwt.client.ResourceDictionary;
+import org.sonar.api.web.gwt.client.webservices.*;
+import org.sonar.api.web.gwt.client.webservices.WSMetrics.Metric;
+import org.sonar.api.web.gwt.client.webservices.WSMetrics.MetricsList;
+import org.sonar.api.web.gwt.client.widgets.LoadingLabel;
+import org.sonar.plugins.core.clouds.client.widget.ClassCloudsWidget;
+import org.sonar.plugins.core.clouds.client.widget.TabWidget;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class GwtClouds extends AbstractPage {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.clouds.GwtClouds";
+
+ private Panel cloudsPanel;
+ private ListBox metricsListBox;
+ private Label sizeAndColorLabel;
+ private TabWidget sizeTabs;
+ private Resources resources;
+
+ private final List<SizeMetric> SIZE_METRICS = Arrays.asList(
+ new SizeMetric("Quick Wins", WSMetrics.NCLOC),
+ new SizeMetric("Top risk", WSMetrics.FUNCTION_COMPLEXITY));
+
+ private final List<Metric> COLOR_METRICS = Arrays.asList(WSMetrics.COVERAGE, WSMetrics.VIOLATIONS_DENSITY);
+
+ public void onModuleLoad() {
+ cloudsPanel = new FlowPanel();
+ displayView(cloudsPanel);
+ loadClouds();
+ }
+
+ protected void loadClouds() {
+ String projectKey = ResourceDictionary.getResourceKey();
+ final List<Metric> metricsToGet = new ArrayList<Metric>();
+ for (SizeMetric size : SIZE_METRICS) {
+ metricsToGet.add(size.getSizeMetric());
+ }
+ for (Metric color : COLOR_METRICS) {
+ metricsToGet.add(color);
+ }
+ if (projectKey != null) {
+ cloudsPanel.add(new LoadingLabel());
+
+ Query<Resources> resourcesQuery = ResourcesQuery.get(projectKey).setDepth(-1).setScopes(Resource.SCOPE_ENTITY).setMetrics(metricsToGet);
+ QueryCallBack<Resources> resourcesCb = new BaseQueryCallback<Resources>() {
+ public void onResponse(Resources response, JavaScriptObject jsonRawResponse) {
+ resources = response;
+ }
+ };
+ Query<MetricsList> metrics = MetricsQuery.get().setUserManaged(false);
+ QueryCallBack<MetricsList> metricsCb = new BaseQueryCallback<MetricsList>() {
+ public void onResponse(MetricsList response, JavaScriptObject jsonRawResponse) {
+ // nothing to do WSMetrics.getUpdateMetricsFromServer will update the metrics labels
+ }
+ };
+ metricsCb = WSMetrics.getUpdateMetricsFromServer(metricsCb);
+
+ QueryCallBack<VoidResponse> updateCloudsCb = new BaseQueryCallback<VoidResponse>() {
+ public void onResponse(VoidResponse response, JavaScriptObject jsonRawResponse) {
+ updateClouds(resources);
+ }
+ };
+
+ SequentialQueries.get().add(resourcesQuery, resourcesCb).add(metrics, metricsCb).execute(updateCloudsCb);
+ }
+ }
+
+ private void updateClouds(Resources resources) {
+ cloudsPanel.clear();
+ Panel metricSelectPanel = getMetricColorSelectBox(resources);
+ sizeTabs = new TabWidget(new SelectionHandler<Integer>() {
+ public void onSelection(SelectionEvent<Integer> event) {
+ renderClassCloudsForCurrentMetric();
+ }
+ });
+ for (SizeMetric size : SIZE_METRICS) {
+ ClassCloudsWidget classCloudsTab = new ClassCloudsWidget(resources.getResources(), size.getSizeMetric());
+ sizeTabs.addTab(classCloudsTab, size.getTabName(), size.getTabNameId());
+ }
+
+ cloudsPanel.add(metricSelectPanel);
+ cloudsPanel.add(sizeTabs);
+ }
+
+ private Panel getMetricColorSelectBox(Resources resources) {
+ HTMLPanel metricSelectPanel = new HTMLPanel("<div id='select_metric' class='metricSelectBox small'> </div>");
+ sizeAndColorLabel = new InlineLabel();
+ sizeAndColorLabel.setStyleName("labelText gray");
+ metricSelectPanel.add(sizeAndColorLabel, "select_metric");
+ metricsListBox = new ListBox(false);
+ for (Metric color : COLOR_METRICS) {
+ if (resources.onceContainsMeasure(color)) {
+ metricsListBox.addItem(color.getName(), color.getKey());
+ }
+ }
+ metricSelectPanel.add(metricsListBox, "select_metric");
+
+ metricsListBox.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ renderClassCloudsForCurrentMetric();
+ }
+ });
+ return metricSelectPanel;
+ }
+
+ private void generateSizeAndColorLabel() {
+ sizeAndColorLabel.setText("Size : " + getCurrentSizeMetric().getName() + ", color : ");
+ }
+
+ private void renderClassCloudsForCurrentMetric() {
+ Widget widget = sizeTabs.getSelectedWidget();
+ if (widget instanceof ClassCloudsWidget) {
+ Metric current = getCurrentColorMetric();
+ ClassCloudsWidget classCloudsWidget = (ClassCloudsWidget) widget;
+ classCloudsWidget.generateCloud(current);
+ generateSizeAndColorLabel();
+ }
+ }
+
+ private Metric getCurrentColorMetric() {
+ String metricKey = metricsListBox.getValue(metricsListBox.getSelectedIndex());
+ for (Metric color : COLOR_METRICS) {
+ if (color.getKey().equals(metricKey)) {
+ return color;
+ }
+ }
+ throw new JavaScriptException("Unable to find metric " + metricKey);
+ }
+
+ private Metric getCurrentSizeMetric() {
+ String selectedTabId = sizeTabs.getSelectedTabId();
+ for (SizeMetric size : SIZE_METRICS) {
+ if (size.getTabNameId().equals(selectedTabId)) {
+ return size.sizeMetric;
+ }
+ }
+ throw new JavaScriptException("Unable to find metric for tab " + selectedTabId);
+ }
+
+
+ private class SizeMetric {
+
+ private String tabName;
+ private Metric sizeMetric;
+
+ public SizeMetric(String tabName, Metric sizeMetric) {
+ super();
+ this.tabName = tabName;
+ this.sizeMetric = sizeMetric;
+ }
+
+ public String getTabName() {
+ return tabName;
+ }
+
+ public Metric getSizeMetric() {
+ return sizeMetric;
+ }
+
+ public String getTabNameId() {
+ return tabName.toLowerCase().replace(' ', '_');
+ }
+ }
+
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java
new file mode 100644
index 00000000000..60f928d8f3e
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java
@@ -0,0 +1,53 @@
+/*
+ * 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.core.clouds.client.model;
+
+import org.sonar.api.web.gwt.client.webservices.Resource;
+
+
+public class CloudElement implements Comparable<CloudElement> {
+
+ private Integer fontSize;
+ private Float fontColor;
+ private Resource resource;
+
+ public CloudElement(Resource resource, Integer fontSize, Float fontColor) {
+ this.resource = resource;
+ this.fontSize = fontSize;
+ this.fontColor = fontColor;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public Integer getFontSize() {
+ return fontSize;
+ }
+
+ public Float getFontColor() {
+ return fontColor;
+ }
+
+ public int compareTo(CloudElement cloudElement) {
+ return resource.getName().compareTo(cloudElement.getResource().getName());
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.java
new file mode 100644
index 00000000000..598c470f207
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.java
@@ -0,0 +1,50 @@
+/*
+ * 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.core.clouds.client.model;
+
+public class Color {
+
+ private float red;
+ private float green;
+ private float blue;
+
+ public Color(float red, float green, float blue) {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ }
+
+ public float getRed() {
+ return red;
+ }
+
+ public float getGreen() {
+ return green;
+ }
+
+ public float getBlue() {
+ return blue;
+ }
+
+ @Override
+ public String toString() {
+ return ("red : "+ red + ", green : "+ green + ", blue : "+ blue );
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java
new file mode 100644
index 00000000000..49c28d87656
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java
@@ -0,0 +1,146 @@
+/*
+ * 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.core.clouds.client.widget;
+
+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.api.web.gwt.client.Utils;
+import org.sonar.api.web.gwt.client.webservices.Measure;
+import org.sonar.api.web.gwt.client.webservices.Resource;
+import org.sonar.api.web.gwt.client.webservices.WSMetrics.Metric;
+import org.sonar.api.web.gwt.client.widgets.LoadingLabel;
+import org.sonar.plugins.core.clouds.client.Calculator;
+import org.sonar.plugins.core.clouds.client.GwtClouds;
+import org.sonar.plugins.core.clouds.client.model.CloudElement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ClassCloudsWidget extends Composite {
+
+ private Panel main;
+ private Metric sizeMetric;
+ private List<Resource> resources;
+ private float minSizePercent = 60f;
+ private float maxSizePercent = 240f;
+
+ private Calculator sizeCalculator = new Calculator(minSizePercent, maxSizePercent);
+ private Calculator colorCalculator = new Calculator(0f, 100f);
+
+ public ClassCloudsWidget(List<Resource> resources, Metric sizeMetric) {
+ this.sizeMetric = sizeMetric;
+ this.main = new FlowPanel();
+ this.resources = resources;
+ initWidget(main);
+ }
+
+ public Metric getSizeMetric() {
+ return sizeMetric;
+ }
+
+ public void generateCloud(Metric colorMetric) {
+ main.clear();
+ LoadingLabel loading = new LoadingLabel();
+ main.add(loading);
+ if (colorMetric.equals(colorMetric)) {
+ List<CloudElement> cloudElements = getCloudElements(resources, colorMetric);
+ createClouds(cloudElements, colorMetric);
+ }
+ main.remove(loading);
+ }
+
+ private List<CloudElement> getCloudElements(List<Resource> resources, Metric colorMetric) {
+ List<CloudElement> tagList = new ArrayList<CloudElement>();
+ for (Resource resource : resources) {
+ Measure sizeMeasure = getMeasure(resource, sizeMetric);
+ Measure colorMeasure = getMeasure(resource, colorMetric);
+
+ if (sizeMeasure != null && colorMeasure != null) {
+ Integer size = getMeasureValue(sizeMeasure.getValue());
+ float color = colorMeasure.getValue().floatValue();
+ tagList.add(new CloudElement(resource, size, color));
+ sizeCalculator.updateMaxAndMin(Float.valueOf(size.toString()));
+ }
+ }
+ Collections.sort(tagList);
+ return tagList;
+ }
+
+ private Integer getMeasureValue(Double value) {
+ Float floatValue = (value.floatValue() * 100.0f);
+ return floatValue.intValue();
+ }
+
+ private Measure getMeasure(Resource project, Metric metricToFind) {
+ return project.getMeasure(metricToFind);
+ }
+
+ private void createClouds(List<CloudElement> cloudElements, Metric colorMetric) {
+ for (CloudElement tag : cloudElements) {
+ HTML className = new HTML(
+ "<span style=\"font-size:" + Integer.toString(sizeCalculator.getFontSizePercent(tag.getFontSize())) +
+ "%; color:" + colorCalculator.getFontColor(tag.getFontColor()) + "\" >" +
+ tag.getResource().getName() + "</span>\n");
+ className.setStyleName("inline");
+
+ Hyperlink link = createLink(tag, colorMetric);
+ link.setHTML(className.getHTML());
+ main.add(link);
+ }
+ }
+
+ private Hyperlink createLink(CloudElement tag, final Metric colorMetric) {
+ Hyperlink link = new Hyperlink();
+ link.setStyleName("tag inline");
+ String tooltip = getTooltip(tag.getResource(), colorMetric);
+ link.getElement().setAttribute("title", tooltip);
+ link.getElement().setAttribute("rel", tooltip);
+
+ String sizeCss = Float.toString(maxSizePercent / 100f) + "em";
+ link.setHeight(sizeCss);
+ final Resource clickResource = tag.getResource();
+ link.addClickHandler(new ClickHandler() {
+ public void onClick(final ClickEvent event) {
+ if (clickResource.getCopy() != null) {
+ Window.Location.assign(Utils.getServerUrl() + "/plugins/resource/" + clickResource.getCopy() + "?page=" + GwtClouds.GWT_ID);
+ } else {
+ Utils.openResourcePopup(clickResource, colorMetric.getKey());
+ }
+ }
+ });
+
+ return link;
+ }
+
+ private String getTooltip(Resource resource, Metric colorMetric) {
+ Measure sizeMeasure = getMeasure(resource, sizeMetric);
+ String sizeMetricName = sizeMetric.getName();
+ String sizeMetricValue = sizeMeasure.getFormattedValue();
+
+ Measure colorMeasure = getMeasure(resource, colorMetric);
+ String colorMetricName = colorMetric.getName();
+ String colorMetricValue = colorMeasure.getFormattedValue();
+
+ return resource.getName(true) + ", " + sizeMetricName + " : " + sizeMetricValue + ", " + colorMetricName + " : " + colorMetricValue;
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.java
new file mode 100644
index 00000000000..7e9b571a4e0
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.java
@@ -0,0 +1,77 @@
+/*
+ * 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.core.clouds.client.widget;
+
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.TabPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+public class TabWidget extends Composite {
+
+ private TabPanel tab = new TabPanel();
+
+ private Integer nbTab;
+ private final Integer defaultSelectedTabPosition = 0;
+ private String selectedTabId;
+ private int selectedIndex;
+
+ public TabWidget(final SelectionHandler<Integer> selectionListener) {
+ nbTab = 0;
+ initWidget(tab);
+ tab.setWidth("100%");
+
+ tab.addSelectionHandler(new SelectionHandler<Integer>() {
+ public void onSelection(SelectionEvent<Integer> event) {
+ selectedTabId = tab.getWidget(event.getSelectedItem()).getElement().getId().replace("_tab_content", "");
+ selectedIndex = event.getSelectedItem();
+ selectionListener.onSelection(event);
+ }
+ });
+
+ }
+
+ public String getSelectedTabId() {
+ return selectedTabId;
+ }
+
+ public Widget getSelectedWidget() {
+ return tab.getWidget(selectedIndex);
+ }
+
+ public void addTab(Widget widget, String tabName, String id) {
+ widget.getElement().setId(id + "_tab_content");
+ tab.add(widget, createTabLabel(tabName, id));
+ if (nbTab.equals(defaultSelectedTabPosition)) {
+ tab.selectTab(defaultSelectedTabPosition);
+ }
+ nbTab++;
+ }
+
+ private Label createTabLabel(String tabName, String id) {
+ Label tabLabel = new Label(tabName);
+ tabLabel.getElement().setId(id + "_tab_title");
+ tabLabel.addStyleName("tab_title");
+ return tabLabel;
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.java
new file mode 100644
index 00000000000..ba9231e491d
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.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.core.coverageviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+
+@ResourceQualifier(Resource.QUALIFIER_CLASS)
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.COVERAGE_KEY, CoreMetrics.LINES_TO_COVER_KEY, CoreMetrics.UNCOVERED_LINES_KEY, CoreMetrics.LINE_COVERAGE_KEY, CoreMetrics.CONDITIONS_TO_COVER_KEY, CoreMetrics.UNCOVERED_CONDITIONS_KEY, CoreMetrics.BRANCH_COVERAGE_KEY})
+@UserRole(UserRole.CODEVIEWER)
+public class CoverageViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Coverage";
+ }
+
+ public String getGwtId() {
+ return "org.sonar.plugins.core.coverageviewer.CoverageViewer";
+ }
+}
+
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.java
new file mode 100644
index 00000000000..fabda63343e
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.java
@@ -0,0 +1,123 @@
+/*
+ * 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.core.coverageviewer.client;
+
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.SourcePanel;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CoveragePanel extends SourcePanel {
+
+ private Map<Integer, String> hitsByLine = new HashMap<Integer, String>();
+ private Map<Integer, String> branchHitsByLine = new HashMap<Integer, String>();
+
+
+ public CoveragePanel(Resource resource) {
+ super(resource);
+ loadCoverageHits(resource);
+ }
+
+ private void loadCoverageHits(Resource resource) {
+ ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.COVERAGE_LINE_HITS_DATA, Metrics.BRANCH_COVERAGE_HITS_DATA);
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ handleResponse(resource, Metrics.COVERAGE_LINE_HITS_DATA, hitsByLine);
+ handleResponse(resource, Metrics.BRANCH_COVERAGE_HITS_DATA, branchHitsByLine);
+ setStarted();
+ }
+ });
+ }
+
+ private void handleResponse(Resource resource, String metric, Map<Integer, String> values) {
+ if (resource==null || resource.getMeasure(metric)==null) {
+ return;
+ }
+
+ values.clear();
+ String linesValue = resource.getMeasure(metric).getData();
+ String[] lineWithValueArray;
+ if (linesValue.contains(",")) {
+ // deprecated - format before 1.9
+ lineWithValueArray = linesValue.split(",");
+ } else {
+ lineWithValueArray = linesValue.split(";");
+ }
+ for (String lineWithValue : lineWithValueArray) {
+ String[] elt = lineWithValue.split("=");
+ if (elt != null && elt.length == 2) {
+ values.put(Integer.parseInt(elt[0]), elt[1]);
+ }
+ }
+ }
+
+
+ @Override
+ protected boolean shouldDecorateLine(int index) {
+ return index > 0;
+ }
+
+ @Override
+ protected List<Row> decorateLine(int index, String source) {
+ Row row = new Row().setLineIndex(index, "");
+
+ String hits = hitsByLine.get(index);
+ String branchHits = branchHitsByLine.get(index);
+ boolean hasLineCoverage = (null != hits);
+ boolean hasBranchCoverage = (null != branchHits);
+ boolean lineIsCovered = (hasLineCoverage && Integer.parseInt(hits) > 0);
+ boolean branchIsCovered = (hasBranchCoverage && "100%".equals(branchHits));
+
+ row.setSource(source, "");
+ row.setValue("&nbsp;", "");
+ row.setValue2("&nbsp;", "");
+
+ if (lineIsCovered) {
+ if (branchIsCovered) {
+ row.setValue(hits, "green");
+ row.setValue2(branchHits, "green");
+ } else if (hasBranchCoverage) {
+ row.setValue(hits, "orange");
+ row.setValue2(branchHits, "orange");
+ row.setSource(source, "orange");
+ } else {
+ row.setValue(hits, "green");
+ }
+ } else if (hasLineCoverage) {
+ row.setValue(hits, "red");
+ row.setSource(source, "red");
+ if (hasBranchCoverage) {
+ row.setValue2(branchHits, "red");
+ } else {
+ row.setValue2("&nbsp;", "red");
+ }
+ }
+ return Arrays.asList(row);
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.java
new file mode 100644
index 00000000000..64752ddd7c0
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.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.core.coverageviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public class CoverageViewer extends Page {
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel panel = new FlowPanel();
+ panel.setWidth("100%");
+ panel.add(new CoverageHeader(resource));
+ panel.add(new CoveragePanel(resource));
+ return panel;
+ }
+
+ private static class CoverageHeader extends ViewerHeader {
+ public CoverageHeader(Resource resource) {
+ super(resource, new String[]{Metrics.COVERAGE, Metrics.LINE_COVERAGE, Metrics.UNCOVERED_LINES, Metrics.BRANCH_COVERAGE, Metrics.UNCOVERED_CONDITIONS});
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ HorizontalPanel panel = new HorizontalPanel();
+ header.add(panel);
+
+ Measure measure = resource.getMeasure(Metrics.COVERAGE);
+ if (measure == null) {
+ addBigCell(panel, "-");
+ } else {
+ addBigCell(panel, measure.getFormattedValue());
+ }
+
+ addCell(panel, resource.getMeasure(Metrics.LINE_COVERAGE));
+ addCell(panel, resource.getMeasure(Metrics.UNCOVERED_LINES));
+ addCell(panel, resource.getMeasure(Metrics.BRANCH_COVERAGE));
+ addCell(panel, resource.getMeasure(Metrics.UNCOVERED_CONDITIONS));
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.java
new file mode 100644
index 00000000000..f400727cc55
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.core.defaultsourceviewer;
+
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.defaultsourceviewer.client.GwtDefaultSourceViewer;
+
+@ResourceScope(Resource.SCOPE_ENTITY)
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab
+@UserRole(UserRole.CODEVIEWER)
+public class DefaultSourceViewer extends GwtPage {
+ public String getTitle() {
+ return "Sources";
+ }
+
+ public String getGwtId() {
+ return GwtDefaultSourceViewer.GWT_ID;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.java
new file mode 100644
index 00000000000..fd9a929ae58
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.java
@@ -0,0 +1,113 @@
+/*
+ * 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.core.defaultsourceviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.DefaultSourcePanel;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Resource;
+
+public class GwtDefaultSourceViewer extends Page {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.defaultsourceviewer.GwtDefaultSourceViewer";
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel panel = new FlowPanel();
+ panel.setWidth("100%");
+ panel.add(new SimpleHeader(resource));
+ panel.add(new DefaultSourcePanel(resource));
+ return panel;
+ }
+
+ private static class SimpleHeader extends ViewerHeader {
+ public SimpleHeader(Resource resource) {
+ super(resource, new String[]{
+ Metrics.LINES,
+ Metrics.NCLOC,
+ Metrics.FUNCTIONS,
+ Metrics.ACCESSORS,
+ Metrics.PARAGRAPHS,
+
+ Metrics.STATEMENTS,
+ Metrics.COMPLEXITY,
+ Metrics.FUNCTION_COMPLEXITY,
+ Metrics.PARAGRAPH_COMPLEXITY,
+
+ Metrics.COMMENT_LINES_DENSITY,
+ Metrics.COMMENT_LINES,
+ Metrics.COMMENTED_OUT_CODE_LINES,
+ Metrics.COMMENT_BLANK_LINES,
+
+ Metrics.PUBLIC_DOCUMENTED_API_DENSITY,
+ Metrics.PUBLIC_UNDOCUMENTED_API,
+ Metrics.PUBLIC_API,
+
+ Metrics.CLASSES,
+ Metrics.NUMBER_OF_CHILDREN,
+ Metrics.DEPTH_IN_TREE,
+ Metrics.RFC
+ }
+ );
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ HorizontalPanel panel = new HorizontalPanel();
+ addCell(panel,
+ resource.getMeasure(Metrics.LINES),
+ resource.getMeasure(Metrics.NCLOC),
+ resource.getMeasure(Metrics.FUNCTIONS),
+ resource.getMeasure(Metrics.ACCESSORS),
+ resource.getMeasure(Metrics.PARAGRAPHS));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.STATEMENTS),
+ resource.getMeasure(Metrics.COMPLEXITY),
+ resource.getMeasure(Metrics.FUNCTION_COMPLEXITY),
+ resource.getMeasure(Metrics.PARAGRAPH_COMPLEXITY));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.COMMENT_LINES_DENSITY),
+ resource.getMeasure(Metrics.COMMENT_LINES),
+ resource.getMeasure(Metrics.COMMENTED_OUT_CODE_LINES),
+ resource.getMeasure(Metrics.COMMENT_BLANK_LINES));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.PUBLIC_DOCUMENTED_API_DENSITY),
+ resource.getMeasure(Metrics.PUBLIC_UNDOCUMENTED_API),
+ resource.getMeasure(Metrics.PUBLIC_API));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.CLASSES),
+ resource.getMeasure(Metrics.NUMBER_OF_CHILDREN),
+ resource.getMeasure(Metrics.DEPTH_IN_TREE),
+ resource.getMeasure(Metrics.RFC));
+
+ if (panel.getWidgetCount() > 0) {
+ header.add(panel);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.java
new file mode 100644
index 00000000000..b679a144f76
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.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.core.duplicationsviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.duplicationsviewer.client.DuplicationsViewer;
+
+@ResourceQualifier({Resource.QUALIFIER_CLASS,Resource.QUALIFIER_FILE})
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.DUPLICATED_LINES_KEY, CoreMetrics.DUPLICATED_BLOCKS_KEY, CoreMetrics.DUPLICATED_FILES_KEY, CoreMetrics.DUPLICATED_LINES_DENSITY_KEY})
+@UserRole(UserRole.CODEVIEWER)
+public class DuplicationsViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Duplications";
+ }
+
+ public String getGwtId() {
+ return DuplicationsViewer.GWT_ID;
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java
new file mode 100644
index 00000000000..17f34ffa94b
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java
@@ -0,0 +1,161 @@
+/*
+ * 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.core.duplicationsviewer.client;
+
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.DefaultSourcePanel;
+import org.sonar.gwt.ui.ExpandCollapseLink;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.gwt.ui.SourcePanel;
+
+import com.google.gwt.gen2.table.override.client.FlexTable;
+import com.google.gwt.gen2.table.override.client.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.xml.client.Document;
+import com.google.gwt.xml.client.Element;
+import com.google.gwt.xml.client.NodeList;
+import com.google.gwt.xml.client.XMLParser;
+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 DuplicationsPanel extends Composite {
+
+ private final Panel panel;
+ private Loading loading;
+
+ public DuplicationsPanel(Resource resource) {
+ panel = new VerticalPanel();
+ loading = new Loading();
+ panel.add(loading);
+ initWidget(panel);
+ setStyleName("gwt-DuplicationsPanel");
+ getElement().setId("gwt-DuplicationsPanel");
+
+ loadDuplications(resource);
+ }
+
+ public void loadDuplications(Resource resource) {
+ ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.DUPLICATIONS_DATA);
+ Sonar.getInstance().find(query, new DuplicationCallback());
+ }
+
+ private class DuplicationCallback extends AbstractCallback<Resource> {
+
+ public DuplicationCallback() {
+ super(loading);
+ }
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ loading.removeFromParent();
+ String duplications = null;
+ if (resource != null) {
+ Measure data = resource.getMeasure(Metrics.DUPLICATIONS_DATA);
+ if (data != null) {
+ duplications = data.getData();
+ }
+ }
+ if (duplications != null) {
+ processDuplications(duplications, resource);
+ }
+ }
+
+ private void processDuplications(String duplicationXMLData, Resource resource) {
+ Document parsed = XMLParser.parse(duplicationXMLData);
+ NodeList duplicationsXML = parsed.getElementsByTagName("duplication");
+
+ FlexTable table = getDuplicationsTable();
+
+ panel.add(table);
+ int rowCounter = 1;
+ for (int i = 0; i < duplicationsXML.getLength(); i++) {
+ Element duplicationXML = (Element) duplicationsXML.item(i);
+ String lines = duplicationXML.getAttribute("lines");
+ String startLine = duplicationXML.getAttribute("start");
+ String targetStartLine = duplicationXML.getAttribute("target-start");
+ String targetResourceKey = duplicationXML.getAttribute("target-resource");
+ renderDuplication(rowCounter, i, table, lines, startLine, targetStartLine, targetResourceKey, resource);
+ rowCounter+=2;
+ }
+ }
+
+ private FlexTable getDuplicationsTable() {
+ FlexTable table = new FlexTable();
+ table.setStylePrimaryName("duplicationsTable");
+ table.setText(0, 0, "");
+ table.setText(0, 1, "Nb lines");
+ table.setText(0, 2, "From line");
+ table.setText(0, 3, "File");
+ table.setText(0, 4, "From line");
+
+ table.getCellFormatter().getElement(0, 0).setId("expandCollapseCol");
+ table.getCellFormatter().getElement(0, 1).setId("nbLineCol");
+ table.getCellFormatter().getElement(0, 2).setId("lineFromCol");
+ table.getCellFormatter().getElement(0, 3).setId("fileCol");
+
+ setRowStyle(0, table, "header", false);
+ return table;
+ }
+
+ private void renderDuplication(int row, int duplicationCounter, FlexTable table, String lines, String startLine, String targetStartLine, String targetResourceKey, final Resource resource) {
+ String style = (duplicationCounter % 2 == 0) ? "odd" : "even";
+
+ SourcePanel src = new DefaultSourcePanel(resource, new Integer(startLine), new Integer(lines));
+ src.getElement().setId("source-panel-" + targetResourceKey.replace('.', '_'));
+ src.setVisible(false);
+
+ ExpandCollapseLink link = new ExpandCollapseLink(src);
+
+ table.setWidget(row, 0, link);
+ table.setText(row, 1, lines);
+ table.setText(row, 2, startLine);
+ if (targetResourceKey.equals(resource.getKey())) {
+ targetResourceKey = "Same file";
+ }
+ if (targetResourceKey.contains(":")) {
+ targetResourceKey = targetResourceKey.substring(targetResourceKey.lastIndexOf(':') + 1);
+ }
+ table.setText(row, 3, targetResourceKey);
+ table.setText(row, 4, targetStartLine);
+ setRowStyle(row, table, style, false);
+
+ FlexCellFormatter frmt = (FlexCellFormatter)table.getCellFormatter();
+ frmt.setColSpan(row + 1, 1, 4);
+ table.setWidget(row + 1, 1, src);
+ setRowStyle(row + 1, table, style, true);
+
+ }
+
+ private void setRowStyle(int row, FlexTable table, String style, boolean isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 0, style);
+ table.getCellFormatter().setStyleName(row, 1, style);
+ if (!isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 2, style);
+ table.getCellFormatter().setStyleName(row, 3, style);
+ table.getCellFormatter().setStyleName(row, 4, style);
+ }
+ }
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java
new file mode 100644
index 00000000000..742e0975dc7
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java
@@ -0,0 +1,79 @@
+/*
+ * 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.core.duplicationsviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public class DuplicationsViewer extends Page {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.duplicationsviewer.DuplicationsViewer";
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel panel = new FlowPanel();
+ panel.setWidth("100%");
+ panel.add(new DuplicationsHeader(resource));
+ panel.add(new DuplicationsPanel(resource));
+ return panel;
+ }
+
+ private static class DuplicationsHeader extends ViewerHeader {
+ public DuplicationsHeader(Resource resource) {
+ super(resource, new String[]{Metrics.DUPLICATED_LINES_DENSITY, Metrics.LINES, Metrics.DUPLICATED_LINES, Metrics.DUPLICATED_BLOCKS});
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ Panel panel = new HorizontalPanel();
+ header.add(panel);
+
+ Measure measure = resource.getMeasure(Metrics.DUPLICATED_LINES_DENSITY);
+ if (measure == null) {
+ addBigCell(panel, "0");
+ } else {
+ addBigCell(panel, measure.getFormattedValue());
+ }
+
+ addCell(panel, getDefaultMeasure(resource, Metrics.LINES, "lines"));
+ addCell(panel, getDefaultMeasure(resource, Metrics.DUPLICATED_LINES, "Duplicated lines"));
+ addCell(panel, getDefaultMeasure(resource, Metrics.DUPLICATED_BLOCKS, "Duplicated blocks"));
+ }
+
+ private Measure getDefaultMeasure(Resource resource, String metric, String label) {
+ Measure measure = resource.getMeasure(metric);
+ if (measure == null || measure.getValue() == null) {
+ measure = new Measure();
+ measure.setMetricName(label);
+ measure.setValue(0.0);
+ measure.setFormattedValue("0");
+ }
+ return measure;
+ }
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.java
new file mode 100644
index 00000000000..35ec96f4e93
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.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.core.hotspots;
+
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.GwtPage;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.ResourceScope;
+import org.sonar.api.web.UserRole;
+
+@NavigationSection(NavigationSection.RESOURCE)
+@ResourceScope({Resource.SCOPE_SET, Resource.SCOPE_SPACE})
+@UserRole(UserRole.USER)
+public class Hotspots extends GwtPage {
+
+ public String getTitle() {
+ return "Hotspots";
+ }
+
+ public String getGwtId() {
+ return "org.sonar.plugins.core.hotspots.GwtHotspots";
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.java
new file mode 100644
index 00000000000..5f9f3edae0c
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.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.core.hotspots.client;
+
+import com.google.gwt.gen2.table.override.client.Grid;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.plugins.core.hotspots.client.widget.MetricHotspot;
+import org.sonar.plugins.core.hotspots.client.widget.MostBadlyDesignedFiles;
+import org.sonar.plugins.core.hotspots.client.widget.MostViolatedResources;
+import org.sonar.plugins.core.hotspots.client.widget.MostViolatedRules;
+import org.sonar.wsclient.services.Resource;
+
+public class GwtHotspots extends Page {
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ Grid grid = new Grid(1, 2);
+ grid.setStylePrimaryName("gwt-Hotspots");
+ loadHotspots(grid, resource);
+ return grid;
+ }
+
+
+ private void loadHotspots(Grid grid, Resource resource) {
+ VerticalPanel column1 = new VerticalPanel();
+ column1.setStyleName("hotspotcol");
+ VerticalPanel column2 = new VerticalPanel();
+ column2.setStyleName("hotspotcol");
+
+ column1.add(new MostViolatedRules(resource));
+ column1.add(new MetricHotspot(resource, Metrics.TEST_EXECUTION_TIME, I18nConstants.INSTANCE.titleLongestTests()));
+ column1.add(new MetricHotspot(resource, Metrics.COMPLEXITY, I18nConstants.INSTANCE.titleMostComplexResources()));
+ column1.add(new MetricHotspot(resource, Metrics.DUPLICATED_LINES, I18nConstants.INSTANCE.titleMostDuplicatedResources()));
+ column1.add(new MostBadlyDesignedFiles(resource));
+
+ column2.add(new MostViolatedResources(resource));
+ column2.add(new MetricHotspot(resource, Metrics.UNCOVERED_LINES, I18nConstants.INSTANCE.titleLessTested()));
+ column2.add(new MetricHotspot(resource, Metrics.FUNCTION_COMPLEXITY, I18nConstants.INSTANCE.titleMostComplexMethods()));
+ column2.add(new MetricHotspot(resource, Metrics.PUBLIC_UNDOCUMENTED_API, I18nConstants.INSTANCE.titleMostUndocumentedAPI()));
+
+ grid.setWidget(0, 0, column1);
+ grid.setWidget(0, 1, column2);
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.java
new file mode 100644
index 00000000000..799b8267175
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.java
@@ -0,0 +1,69 @@
+/*
+ * 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.core.hotspots.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("Most violated rules")
+ String titleMostViolatedRules();
+
+ @DefaultStringValue("Most violated")
+ String titleMostViolatedResources();
+
+ @DefaultStringValue("Longest unit tests")
+ String titleLongestTests();
+
+ @DefaultStringValue("Highest complexity")
+ String titleMostComplexResources();
+
+ @DefaultStringValue("Highest duplications")
+ String titleMostDuplicatedResources();
+
+ @DefaultStringValue("Highest untested lines")
+ String titleLessTested();
+
+ @DefaultStringValue("Highest average method complexity")
+ String titleMostComplexMethods();
+
+ @DefaultStringValue("Most undocumented APIs")
+ String titleMostUndocumentedAPI();
+
+ @DefaultStringValue("No measures")
+ String noMeasures();
+
+ @DefaultStringValue("Any priority")
+ String anyPriority();
+
+ @DefaultStringValue("more")
+ String moreDetails();
+
+ @DefaultStringValue("Lack of Cohesion of Methods")
+ String lcom4();
+
+ @DefaultStringValue("Response for class")
+ String rfc();
+
+ @DefaultStringValue("Highest")
+ String designTitle();
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java
new file mode 100644
index 00000000000..f214e9f58d9
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java
@@ -0,0 +1,124 @@
+/*
+ * 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.core.hotspots.client.widget;
+
+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.Links;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public abstract class AbstractHotspot extends Composite {
+
+ private Panel hotspot;
+ private Panel data;
+ private Resource resource;
+
+ public static final int LIMIT = 5;
+
+ protected AbstractHotspot(String id, Resource resource) {
+ this.resource = resource;
+ hotspot = new VerticalPanel();
+ hotspot.getElement().setId(id);
+ hotspot.setStyleName("gwt-HotspotPanel");
+ initWidget(hotspot);
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ @Override
+ public void onLoad() {
+ hotspot.add(createHeader());
+ data = new SimplePanel();
+ hotspot.add(data);
+ loadData();
+ }
+
+ protected void loadData() {
+ data.clear();
+ data.add(new Loading());
+ doLoadData();
+ }
+
+ abstract Widget createHeader();
+
+ abstract void doLoadData();
+
+ protected void render(Widget widget) {
+ data.clear();
+ data.add(widget);
+ }
+
+ protected void renderEmptyResults() {
+ Grid grid = new Grid(1, 1);
+ grid.setWidget(0, 0, new HTML(I18nConstants.INSTANCE.noMeasures()));
+ grid.getCellFormatter().setStyleName(0, 0, getRowCssClass(0) + " emptyResultsCell");
+ grid.setStyleName("gwt-Hotspot");
+ render(grid);
+ }
+
+ protected void renderNameCell(Grid hotspotGrid, final Resource resource, final String metricKey, int row, int column) {
+ Anchor link = new Anchor(resource.getName());
+ link.getElement().setAttribute("title", resource.getName(true));
+ link.getElement().setAttribute("rel", resource.getName(true));
+ link.addClickHandler(new ClickHandler() {
+ public void onClick(final ClickEvent event) {
+ if (resource.getCopy() != null) {
+ Window.Location.assign(Links.baseUrl() + "/plugins/resource/" + resource.getCopy() + "?page=org.sonar.plugins.core.hotspots.GwtHotspots");
+ } else {
+ Links.openMeasurePopup(resource.getKey(), metricKey);
+ }
+ }
+ });
+ hotspotGrid.setWidget(row, column, link);
+ hotspotGrid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " resourceCell");
+ }
+
+ protected void renderValueCell(Grid hotspotGrid, Measure measure, int row, int column) {
+ hotspotGrid.setHTML(row, column, measure.getFormattedValue());
+ hotspotGrid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " resultCell");
+ }
+
+ protected void renderGraphCell(Grid hotspotGrid, Measure measure, Measure firstMeasure, int row, int column) {
+ Double value = Double.valueOf(measure.getValue());
+ Double upperValue = Double.valueOf(firstMeasure.getValue());
+ Double percentPonderated = getPercentPonderatedValue(value, 0d, upperValue);
+ String graph = "<span style='width:100%'><ul class='hbar' style='float: right;'><li style='background-color: rgb(119, 119, 119); width: " + percentPonderated.intValue() + "%'>&nbsp;</li></ul></span>";
+ hotspotGrid.setHTML(row, column, graph);
+ hotspotGrid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " graphCell");
+ }
+
+ protected String getRowCssClass(int row) {
+ return row % 2 == 0 ? "even" : "odd";
+ }
+
+ protected double getPercentPonderatedValue(Double value, Double lower, Double upper) {
+ if (value < lower) return 0;
+ if (value > upper) return 100;
+ double percentIncrement = (upper - lower) / 100d;
+ return (value - lower) / percentIncrement;
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java
new file mode 100644
index 00000000000..688cca9a08a
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java
@@ -0,0 +1,133 @@
+/*
+ * 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.core.hotspots.client.widget;
+
+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.Links;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MetricHotspot extends AbstractHotspot {
+
+ private String metric;
+ private String title;
+
+ public MetricHotspot(Resource resource, String metric, String title) {
+ super(metric + "-hotspot", resource);
+ this.metric = metric;
+ this.title = title;
+ }
+
+ @Override
+ Widget createHeader() {
+ final Label label = new Label(title);
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-" + metric);
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/measures/" + getResource().getKey() + "?metric=" + metric);
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(List<Resource> resources) {
+ List<HotspotMeasure> measures = new ArrayList<HotspotMeasure>();
+ for (Resource resource : resources) {
+ for (Measure measure : resource.getMeasures()) {
+ measures.add(new HotspotMeasure(resource, measure));
+ }
+ }
+
+ if (measures.isEmpty()) {
+ renderEmptyResults();
+
+ } else {
+ final Grid grid = new Grid(measures.size(), 3);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ HotspotMeasure firstMeasure = measures.get(0);
+ for (HotspotMeasure measure : measures) {
+ renderNameCell(grid, measure.getResource(), metric, row, 0);
+ renderValueCell(grid, measure.getMeasure(), row, 1);
+ renderGraphCell(grid, measure.getMeasure(), firstMeasure.getMeasure(), row, 2);
+ row++;
+ }
+
+ render(grid);
+ }
+ }
+ });
+ }
+
+ protected ResourceQuery getResourceQuery() {
+ return ResourceQuery.createForResource(getResource(), metric)
+ .setScopes(Resource.SCOPE_ENTITY)
+ .setDepth(ResourceQuery.DEPTH_UNLIMITED)
+ .setLimit(LIMIT);
+ }
+
+ public static class HotspotMeasure {
+ private Resource resource;
+ private Measure measure;
+
+ public HotspotMeasure(Resource resource, Measure measure) {
+ super();
+ this.resource = resource;
+ this.measure = measure;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public Measure getMeasure() {
+ return measure;
+ }
+
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.java
new file mode 100644
index 00000000000..e0400fa9eb9
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.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.core.hotspots.client.widget;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+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.Links;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.List;
+
+public class MostBadlyDesignedFiles extends AbstractHotspot {
+
+ private ListBox metricSelectBox;
+
+ public MostBadlyDesignedFiles(Resource resource) {
+ super("design-hotspot", resource);
+ }
+
+ @Override
+ Widget createHeader() {
+ metricSelectBox = new ListBox(false);
+ metricSelectBox.addItem(I18nConstants.INSTANCE.lcom4(), "lcom4");
+ metricSelectBox.addItem(I18nConstants.INSTANCE.rfc(), "rfc");
+ metricSelectBox.setStyleName("small");
+ metricSelectBox.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ loadData();
+ }
+ });
+
+ final Label label = new Label(I18nConstants.INSTANCE.designTitle());
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-design");
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ final String metric = getSelectedMetric();
+ Window.Location.assign(Links.baseUrl() + "/drilldown/measures/" + getResource().getId() + "?metric=" + metric);
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(metricSelectBox);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(metricSelectBox, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(List<Resource> resources) {
+ final Grid grid = new Grid(resources.size(), 3);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ Measure firstMeasure = null;
+ for (Resource resource : resources) {
+ if (resource.getMeasures().size() == 1) {
+ if (firstMeasure == null) {
+ firstMeasure = resource.getMeasures().get(0);
+ }
+ renderNameCell(grid, resource, firstMeasure.getMetricKey(), row, 0);
+ renderValueCell(grid, resource.getMeasures().get(0), row, 1);
+ renderGraphCell(grid, resource.getMeasures().get(0), firstMeasure, row, 2);
+ row++;
+ }
+ }
+
+ if (firstMeasure == null) {
+ renderEmptyResults();
+ } else {
+ render(grid);
+ }
+ }
+ });
+ }
+
+ public ResourceQuery getResourceQuery() {
+ return ResourceQuery.createForResource(getResource(), getSelectedMetric())
+ .setDepth(-1)
+ .setQualifiers(Resource.QUALIFIER_CLASS)
+ .setLimit(LIMIT);
+ }
+
+ private String getSelectedMetric() {
+ return metricSelectBox.getValue(metricSelectBox.getSelectedIndex());
+ }
+}
+
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.java
new file mode 100644
index 00000000000..e79398ba063
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.java
@@ -0,0 +1,127 @@
+/*
+ * 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.core.hotspots.client.widget;
+
+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.Links;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.List;
+import java.util.Map;
+
+public class MostViolatedResources extends AbstractHotspot {
+
+ public MostViolatedResources(Resource resource) {
+ super("violated-files-hotspot", resource);
+ }
+
+ @Override
+ Widget createHeader() {
+ final Label label = new Label(I18nConstants.INSTANCE.titleMostViolatedResources());
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-violated-resources");
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/measures/" + getResource().getId() + "?metric=" + Metrics.WEIGHTED_VIOLATIONS);
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(List<Resource> resources) {
+ Grid grid = new Grid(resources.size(), 11);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ for (Resource resource : resources) {
+ if (resource.getMeasures().size() > 0) {
+ renderNameCell(grid, resource, Metrics.WEIGHTED_VIOLATIONS, row, 0);
+ renderPriorities(grid, resource, row);
+ row++;
+ }
+ }
+ if (row == 0) {
+ renderEmptyResults();
+ } else {
+ render(grid);
+ }
+ }
+ });
+ }
+
+
+ private void renderPriorities(Grid grid, Resource resource, int row) {
+ Measure debt = resource.getMeasures().get(0);
+ if (debt != null && debt.getData() != null) {
+ Map<String, String> map = debt.getDataAsMap(";");
+ renderPriority(grid, row, map, 1, "BLOCKER");
+ renderPriority(grid, row, map, 3, "CRITICAL");
+ renderPriority(grid, row, map, 5, "MAJOR");
+ renderPriority(grid, row, map, 7, "MINOR");
+ renderPriority(grid, row, map, 9, "INFO");
+ }
+ }
+
+ private void renderPriority(Grid grid, int row, Map<String, String> map, int column, String priority) {
+ grid.setWidget(row, column, Icons.forPriority(priority).createImage());
+ grid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " small right");
+
+ if (map.containsKey(priority)) {
+ grid.setWidget(row, column + 1, new HTML(map.get(priority)));
+ } else {
+ grid.setWidget(row, column + 1, new HTML("0"));
+ }
+ grid.getCellFormatter().setStyleName(row, column + 1, getRowCssClass(row) + " small left");
+ }
+
+ private ResourceQuery getResourceQuery() {
+ return ResourceQuery.createForResource(getResource(), Metrics.WEIGHTED_VIOLATIONS)
+ .setScopes(Resource.SCOPE_ENTITY)
+ .setQualifiers(Resource.QUALIFIER_CLASS, Resource.QUALIFIER_FILE, Resource.QUALIFIER_PROJECT)
+ .setDepth(ResourceQuery.DEPTH_UNLIMITED)
+ .setLimit(LIMIT);
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java
new file mode 100644
index 00000000000..1255c1e5a63
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java
@@ -0,0 +1,149 @@
+/*
+ * 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.core.hotspots.client.widget;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+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.Links;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+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 MostViolatedRules extends AbstractHotspot {
+
+ private ListBox priorities;
+
+ public MostViolatedRules(Resource resource) {
+ super("rules-hotspot", resource);
+ }
+
+ @Override
+ Widget createHeader() {
+ priorities = new ListBox(false);
+ priorities.addItem(I18nConstants.INSTANCE.anyPriority(), "");
+ priorities.addItem("Blocker", "BLOCKER");
+ priorities.addItem("Critical", "CRITICAL");
+ priorities.addItem("Major", "MAJOR");
+ priorities.addItem("Minor", "MINOR");
+ priorities.addItem("Info", "INFO");
+ priorities.setStyleName("small");
+ priorities.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ loadData();
+ }
+ });
+
+ final Label label = new Label(I18nConstants.INSTANCE.titleMostViolatedRules());
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-rules");
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/violations/" + getResource().getId());
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(priorities);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(priorities, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ if (resource.getMeasures().isEmpty()) {
+ renderEmptyResults();
+ } else {
+ renderGrid(resource);
+ }
+ }
+ });
+ }
+
+ private void renderGrid(Resource resource) {
+ final Grid grid = new Grid(resource.getMeasures().size(), 4);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ Measure firstMeasure = resource.getMeasures().get(0);
+ for (Measure measure : resource.getMeasures()) {
+ renderRule(grid, measure, row);
+ renderValueCell(grid, measure, row, 2);
+ renderGraphCell(grid, measure, firstMeasure, row, 3);
+ row++;
+ }
+ render(grid);
+ }
+
+ protected void renderRule(final Grid grid, final Measure measure, final int row) {
+ Anchor drillDown = new Anchor(measure.getRuleName());
+ drillDown.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/violations/" + getResource().getId() + "?rule=" + measure.getRuleKey());
+ }
+ });
+
+ grid.setWidget(row, 0, new HTML("<a id=\"rule" + row + "\" href=\"" + Links.urlForRule(measure.getRuleKey(), false) + "\" onclick=\"window.open(this.href,'rule','height=800,width=900,scrollbars=1,resizable=1');return false;\" title=\"" + measure.getRuleKey() + "\">" + Icons.forPriority(measure.getRulePriority()).getHTML() + "</a>"));
+ grid.setWidget(row, 1, drillDown);
+ grid.getCellFormatter().setStyleName(row, 0, getRowCssClass(row) + "");
+ grid.getCellFormatter().setStyleName(row, 1, getRowCssClass(row) + " resourceCell");
+ }
+
+ public ResourceQuery getResourceQuery() {
+ ResourceQuery query = ResourceQuery.createForResource(getResource(), Metrics.VIOLATIONS)
+ .setDepth(0)
+ .setExcludeRules(false)
+ .setLimit(LIMIT);
+ String priority = getSelectedPriority();
+ if (priority!=null) {
+ query.setRulePriorities(priority);
+ }
+ return query;
+ }
+
+ private String getSelectedPriority() {
+ String priority = priorities.getValue(priorities.getSelectedIndex());
+ if ("".equals(priority) || priority == null) {
+ return null;
+ }
+ return priority;
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.java
new file mode 100644
index 00000000000..0cca72a7d7b
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.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.core.testdetailsviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.testdetailsviewer.client.TestsViewer;
+
+@ResourceQualifier(Resource.QUALIFIER_UNIT_TEST_CLASS)
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.TESTS_KEY, CoreMetrics.TEST_EXECUTION_TIME_KEY, CoreMetrics.TEST_SUCCESS_DENSITY_KEY, CoreMetrics.TEST_FAILURES_KEY, CoreMetrics.TEST_ERRORS_KEY, CoreMetrics.SKIPPED_TESTS_KEY})
+@UserRole(UserRole.CODEVIEWER)
+public class TestsViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Tests";
+ }
+
+ public String getGwtId() {
+ return TestsViewer.GWT_ID;
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.java
new file mode 100644
index 00000000000..ace2bb747a6
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.java
@@ -0,0 +1,168 @@
+/*
+ * 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.core.testdetailsviewer.client;
+
+import com.google.gwt.gen2.table.override.client.FlexTable;
+import com.google.gwt.gen2.table.override.client.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.*;
+import com.google.gwt.xml.client.Document;
+import com.google.gwt.xml.client.Element;
+import com.google.gwt.xml.client.NodeList;
+import com.google.gwt.xml.client.XMLParser;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.ExpandCollapseLink;
+import org.sonar.gwt.ui.Loading;
+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 TestsPanel extends Composite {
+
+ private final Panel panel;
+ private Loading loading;
+
+ public TestsPanel(Resource resource) {
+ panel = new VerticalPanel();
+ loading = new Loading();
+ panel.add(loading);
+ initWidget(panel);
+ setStyleName("gwt-TestDetailsPanel");
+ getElement().setId("gwt-TestDetailsPanel");
+ loadData(resource);
+ }
+
+ private void loadData(Resource resource) {
+ ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.TEST_DATA);
+ Sonar.getInstance().find(query, new TestDetailsMeasureHandler());
+ }
+
+ private class TestDetailsMeasureHandler extends AbstractCallback<Resource> {
+
+ public TestDetailsMeasureHandler() {
+ super(loading);
+ }
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ loading.removeFromParent();
+ if (resource != null) {
+ Measure measure = resource.getMeasure(Metrics.TEST_DATA);
+ processTestDetails(measure.getData());
+ }
+ }
+
+ private void processTestDetails(String testXMLData) {
+ Document parsed = XMLParser.parse(testXMLData);
+ NodeList testcasesXML = parsed.getElementsByTagName("testcase");
+
+ FlexTable table = new FlexTable();
+ table.setStylePrimaryName("detailsTable");
+ table.setText(0, 0, "");
+ table.setText(0, 1, "");
+ table.setText(0, 2, "Duration");
+ table.setText(0, 3, "Unit test name");
+ table.getCellFormatter().getElement(0, 1).setId("iCol");
+ table.getCellFormatter().getElement(0, 2).setId("dCol");
+ setRowStyle(0, table, "header", false);
+
+ int rowCounter = 1;
+ for (int i = 0; i < testcasesXML.getLength(); i++) {
+ Element testcaseXML = (Element) testcasesXML.item(i);
+ String time = testcaseXML.getAttribute("time");
+ String name = testcaseXML.getAttribute("name");
+ String status = testcaseXML.getAttribute("status");
+ Element error = getFirstElement("error", testcaseXML);
+ Element failure = getFirstElement("failure", testcaseXML);
+ Element stackTrace = status.equals("error") ? error : failure;
+ renderTestDetails(rowCounter, i, status, stackTrace, name, time, table);
+ rowCounter += 2;
+ }
+ panel.add(table);
+ }
+
+ private Element getFirstElement(String elementName, Element node) {
+ NodeList elements = node.getElementsByTagName(elementName);
+ return elements.getLength() > 0 ? (Element) elements.item(0) : null;
+ }
+
+ private void renderTestDetails(int row, int testCounter, String testCaseStatus, Element stackTrace, String name, String timeMS, FlexTable table) {
+
+ HTML icon = new HTML("&nbsp;");
+ icon.setStyleName(testCaseStatus);
+ table.setWidget(row, 1, icon);
+ table.setText(row, 2, timeMS + " ms");
+
+ table.setText(row, 3, name);
+ String style = (testCounter % 2 == 0) ? "odd" : "even";
+ setRowStyle(row, table, style, false);
+
+ if (stackTrace != null) {
+ Panel stackPanel = new SimplePanel();
+ stackPanel.setStyleName("stackPanel");
+ stackPanel.getElement().setId("stack-panel" + name);
+ stackPanel.setVisible(false);
+ fillStackPanel(stackPanel, stackTrace);
+
+ FlexCellFormatter frmt = (FlexCellFormatter) table.getCellFormatter();
+ frmt.setColSpan(row + 1, 1, 3);
+ table.setWidget(row + 1, 1, stackPanel);
+ table.setWidget(row, 0, new ExpandCollapseLink(stackPanel));
+ table.getCellFormatter().getElement(row, 0).setId("expandCollapseCol");
+ setRowStyle(row + 1, table, style, true);
+ }
+ }
+
+ private void setRowStyle(int row, FlexTable table, String style, boolean isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 0, style);
+ table.getCellFormatter().setStyleName(row, 1, style);
+ if (!isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 2, style);
+ table.getCellFormatter().setStyleName(row, 3, style);
+ }
+ table.getCellFormatter().getElement(row, 0).setId("noLinkExpandCollapseCol");
+ }
+ }
+
+ private void fillStackPanel(Panel p, Element stackElement) {
+ String message = stackElement.getAttribute("message");
+ if (message.length() > 0) {
+ p.getElement().setInnerHTML(escapeHtml(message) + "<br/>" + stackLineBreaks(stackElement.getFirstChild().getNodeValue()));
+ } else {
+ p.getElement().setInnerHTML(stackLineBreaks(stackElement.getFirstChild().getNodeValue()));
+ }
+ }
+
+ private String escapeHtml(String maybeHtml) {
+ com.google.gwt.dom.client.Element div = DOM.createDiv();
+ div.setInnerText(maybeHtml);
+ return div.getInnerHTML();
+ }
+
+ private String stackLineBreaks(String s) {
+ StringBuilder stack = new StringBuilder(256);
+ for (String el : s.split("\n")) {
+ stack.append(el.trim()).append("<br/>");
+ }
+ return stack.toString();
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.java
new file mode 100644
index 00000000000..e6dfc2545e1
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.java
@@ -0,0 +1,87 @@
+/*
+ * 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.core.testdetailsviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public class TestsViewer extends Page {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.testdetailsviewer.TestsViewer";
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel flowPanel = new FlowPanel();
+ flowPanel.add(new UnitTestsHeader(resource));
+ flowPanel.add(new TestsPanel(resource));
+ return flowPanel;
+ }
+
+ private static class UnitTestsHeader extends ViewerHeader {
+ public UnitTestsHeader(Resource resource) {
+
+ super(resource, new String[]{
+ Metrics.TEST_ERRORS,
+ Metrics.TEST_FAILURES,
+ Metrics.TEST_SUCCESS_DENSITY,
+ Metrics.TESTS,
+ Metrics.SKIPPED_TESTS,
+ Metrics.TEST_EXECUTION_TIME}
+ );
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ HorizontalPanel panel = new HorizontalPanel();
+ header.add(panel);
+
+ Measure measure = resource.getMeasure(Metrics.TEST_SUCCESS_DENSITY);
+ if (measure == null) {
+ addBigCell(panel, "100%"); // best value
+ } else {
+ addBigCell(panel, measure.getFormattedValue());
+ }
+
+ String skippedHtml = "";
+ Measure skipped = resource.getMeasure(Metrics.SKIPPED_TESTS);
+ if (skipped != null && skipped.getValue() > 0.0) {
+ skippedHtml += " (+" + skipped.getFormattedValue() + " skipped)";
+ }
+ addCell(panel,
+ "Tests: ",
+ resource.getMeasureFormattedValue(Metrics.TESTS, "-") + skippedHtml);
+
+ addCell(panel,
+ "Failures/Errors: ",
+ resource.getMeasureFormattedValue(Metrics.TEST_FAILURES, "0") + "/" + resource.getMeasureFormattedValue(Metrics.TEST_ERRORS, "0"));
+
+ addCell(panel,
+ "Duration: ",
+ resource.getMeasureFormattedValue(Metrics.TEST_EXECUTION_TIME, "-"));
+ }
+ }
+
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java
new file mode 100644
index 00000000000..ccebcbf588c
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java
@@ -0,0 +1,30 @@
+/*
+ * 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.core.ui.pageselector;
+
+import org.sonar.api.web.GwtExtension;
+import org.sonar.plugins.core.ui.pageselector.client.PageSelector;
+
+public class GwtPageSelector implements GwtExtension {
+
+ public String getGwtId() {
+ return PageSelector.GWT_ID;
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java
new file mode 100644
index 00000000000..29e5d451445
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java
@@ -0,0 +1,30 @@
+/*
+ * 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.core.ui.pageselector.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("New window")
+ String newWindow();
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.java
new file mode 100644
index 00000000000..048096e182f
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.java
@@ -0,0 +1,94 @@
+/*
+ * 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.core.ui.pageselector.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class PageDef extends JavaScriptObject {
+ // Overlay types always have protected, zero-arg ctors
+
+ protected PageDef() {
+ }
+
+ public final native boolean isGwt() /*-{ return this.gwt; }-*/;
+
+ public final native boolean isDefaultTab() /*-{ return this.d; }-*/;
+
+ public final native String getId() /*-{ return this.id; }-*/;
+
+ public final native String getUrl() /*-{ return this.url; }-*/;
+
+ public final native String getName() /*-{ return this.name; }-*/;
+
+ public final native StringArray getMetrics() /*-{ return this.m || []; }-*/;
+
+ public final native StringArray getLanguages() /*-{ return this.l || []; }-*/;
+
+ public final native StringArray getScopes() /*-{ return this.s || []; }-*/;
+
+ public final native StringArray getQualifiers() /*-{ return this.q || []; }-*/;
+
+ public final boolean acceptLanguage(String language) {
+ return hasValue(getLanguages(), language);
+ }
+
+ public final boolean acceptScope(String scope) {
+ return hasValue(getScopes(), scope);
+ }
+
+ public final boolean acceptQualifier(String qualifier) {
+ return hasValue(getQualifiers(), qualifier);
+ }
+
+ public final boolean acceptMetric(String metric) {
+ StringArray metrics = getMetrics();
+ for (int index = 0; index < metrics.length(); index++) {
+ if (metric.equals(metrics.get(index))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private boolean hasValue(StringArray array, String value) {
+ if (array == null || array.length() == 0) {
+ return true;
+ }
+ if (value != null) {
+ for (int index = 0; index < array.length(); index++) {
+ if (value.equals(array.get(index))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+}
+
+class StringArray extends JavaScriptObject {
+ protected StringArray() {
+ }
+
+ public final native int length() /*-{ return this.length; }-*/;
+
+ public final native String get(int i) /*-{ return this[i]; }-*/;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java
new file mode 100644
index 00000000000..6c28f8c4d6c
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java
@@ -0,0 +1,156 @@
+/*
+ * 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.core.ui.pageselector.client;
+
+import com.google.gwt.http.client.*;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import org.sonar.api.web.gwt.client.Utils;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.ui.Loading;
+
+public class PagePanel extends SimplePanel {
+
+ private PageDef def;
+ private String rootPanelId;
+ private String currentResourceId = null;
+
+ public PagePanel(PageDef def) {
+ this.def = def;
+ rootPanelId = "gwtpage-" + def.getId();
+ add(new HTML("<div id=\"" + rootPanelId + "\"> </div>"));
+ }
+
+ public void display() {
+ String resourceId = Configuration.getResourceId();
+ if (resourceId != null && !resourceId.equals(currentResourceId)) {
+ currentResourceId = resourceId;
+ if (def.isGwt()) {
+ loadGwt(Links.baseUrl(), Configuration.getSonarVersion(), def.getId());
+ } else {
+ loadEmbeddedPage(resourceId);
+ }
+ }
+ }
+
+ private native void loadGwt(final String serverUrl, final String sonarVersion, final String gwtId) /*-{
+ if ($wnd.modules[gwtId]!=null) {
+ $wnd.modules[gwtId]();
+ return;
+ }
+
+ // Create the script tag to be used for importing the GWT script loader.
+ var script = $doc.createElement('script');
+ script.type = 'text/javascript';
+ script.src = serverUrl + '/deploy/gwt/' + gwtId + '/' + gwtId + '.nocache.js?' + sonarVersion;
+
+ // The default GWT script loader calls document.write() twice which prevents loading scripts
+ // on demand, after the document has been loaded. To overcome this we have to overwrite the document.write()
+ // method before the GWT script loader is executed and restore it after.
+ // NOTE: The GWT script loader uses document.write() to compute the URL from where it is loaded.
+ var counter = 0;
+ var limit = 2;
+ var oldWrite = $doc.write;
+ var newWrite = function(html) {
+ if (counter < limit) {
+ counter++;
+ // Fail silently if the script element hasn't been attached to the document.
+ if (!script.parentNode) {
+ return;
+ }
+ // Create a DIV and put the HTML inside.
+ var div = $doc.createElement('div');
+ // We have to replace all the script tags because otherwise IE drops them.
+ div.innerHTML = html.replace(/<script\b([\s\S]*?)<\/script>/gi, "<pre script=\"script\"$1</pre>");
+ // Move DIV contents after the GWT script loader.
+ var nextSibling = script.nextSibling;
+ while(div.firstChild) {
+ var child = div.firstChild;
+ // Recover the script tags.
+ if (child.nodeName.toLowerCase() == 'pre' && child.getAttribute('script') == 'script') {
+ var pre = child;
+ pre.removeAttribute('script');
+ // Create the script tag.
+ child = $doc.createElement('script');
+ // Copy all the attributes.
+ for (var i = 0; i < pre.attributes.length; i++) {
+ var attrNode = pre.attributes[i];
+ // In case of IE we have to copy only the specified attributes.
+ if (typeof attrNode.specified == 'undefined'
+ || (typeof attrNode.specified == 'boolean' && attrNode.specified)) {
+ child.setAttribute(attrNode.nodeName, attrNode.nodeValue);
+ }
+ }
+ // Copy the script text.
+ child.text = typeof pre.innerText == 'undefined' ? pre.textContent : pre.innerText;
+ // Don't forget to remove the placeholder.
+ div.removeChild(pre);
+ }
+ if (nextSibling) {
+ script.parentNode.insertBefore(child, nextSibling);
+ } else {
+ script.parentNode.appendChild(child);
+ }
+ }
+ }
+ if (counter >= limit) {
+ $doc.write = oldWrite;
+ oldWrite = undefined;
+ script = undefined;
+ counter = undefined;
+ }
+ };
+
+ // Append the script tag to the head.
+ var heads = $doc.getElementsByTagName('head');
+ if (heads.length > 0) {
+ $doc.write = newWrite;
+ heads[0].appendChild(script);
+ }
+ }-*/;
+
+ private void loadEmbeddedPage(String resourceId) {
+ final RootPanel panel = RootPanel.get(rootPanelId);
+ panel.add(new Loading());
+ String url = def.getUrl();
+ if (url == null) {
+ url = "/plugins/resource/" + resourceId + "?page=" + def.getId() + "&layout=false&hd=false";
+ } else {
+ url += resourceId;
+ }
+ RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(Links.baseUrl() + url));
+ try {
+ builder.sendRequest(null, new RequestCallback() {
+ public void onError(Request request, Throwable exception) {
+ Utils.showError("Can not load the page " + request.toString());
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ panel.clear();
+ panel.add(new HTML(response.getText()));
+ }
+ });
+ } catch (RequestException e) {
+ Utils.showError("Can not connect to server: " + url);
+ }
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java
new file mode 100644
index 00000000000..76b6226fc58
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java
@@ -0,0 +1,224 @@
+/*
+ * 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.core.ui.pageselector.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Utils;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.wsclient.gwt.Callback;
+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 PageSelector implements EntryPoint {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.ui.pageselector.PageSelector";
+ public static final String HTML_ROOT_ID = "pageselector";
+
+ private VerticalPanel container = null;
+ private String currentResourceId = null;
+ private PageDefs pageDefs = null;
+
+ public void onModuleLoad() {
+ pageDefs = loadPageDefs();
+ exportNativeJavascript(this);
+ if (Configuration.getResourceId() != null) {
+ selectResource(Configuration.getResourceId());
+ }
+ }
+
+ private VerticalPanel createContainer() {
+ if (container == null) {
+ container = new VerticalPanel();
+ container.getElement().setId("rvs");
+ RootPanel.get(HTML_ROOT_ID).add(container);
+ }
+ return container;
+ }
+
+ public static native void exportNativeJavascript(Object obj) /*-{
+ $wnd.sr=function(resourceIdOrKey) {
+ obj.@org.sonar.plugins.core.ui.pageselector.client.PageSelector::selectResource(Ljava/lang/String;)(resourceIdOrKey);
+ };
+ }-*/;
+
+ public void selectResource(final String resourceIdOrKey) {
+ createContainer().add(new Loading());
+ currentResourceId = resourceIdOrKey;
+ Sonar.getInstance().find(new ResourceQuery(resourceIdOrKey), new Callback<Resource>() {
+
+ public void onResponse(Resource resource, JavaScriptObject json) {
+ if (resourceIdOrKey != null && resourceIdOrKey.equals(currentResourceId)) {
+ if (resource == null) {
+ displayResourceNotFound();
+ } else {
+ saveResource(resource.getId().toString(), json);
+ displayResource(resource);
+ }
+ } // else too late, user has selected another resource
+ }
+
+ public void onTimeout() {
+ Utils.showError("Can not load data (timeout)");
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ Utils.showError("Can not load data: error " + errorCode + ", message: " + errorMessage);
+ }
+ });
+ }
+
+ private void displayResource(final Resource resource) {
+ List<PageDef> pages = selectPages(resource);
+
+ PageDef selectedPage = selectPage(pages);
+
+ Title title = new Title(resource);
+ final TabPanel tabs = new TabPanel();
+ tabs.setWidth("100%");
+
+ int selectedTabIndex = -1;
+ for (int tabIndex = 0; tabIndex < pages.size(); tabIndex++) {
+ PageDef page = pages.get(tabIndex);
+ tabs.add(new PagePanel(page), page.getName());
+ if (page == selectedPage) {
+ selectedTabIndex = tabIndex;
+ }
+ }
+
+ container.clear(); // remove the loading icon
+ container.add(title);
+ container.add(tabs);
+
+ tabs.addSelectionHandler(new SelectionHandler<Integer>() {
+ public void onSelection(SelectionEvent<Integer> tabId) {
+ ((PagePanel) tabs.getWidget(tabId.getSelectedItem())).display();
+ }
+ });
+
+ if (selectedTabIndex > -1) {
+ tabs.selectTab(selectedTabIndex);
+ }
+ }
+
+ private PageDef selectPage(List<PageDef> pages) {
+ String pageId = Configuration.getRequestParameter("page");
+ if (pageId != null) {
+ for (PageDef page : pages) {
+ if (pageId.equals(page.getId())) {
+ return page;
+ }
+ }
+ }
+ String metric = Configuration.getParameter("metric");
+ if (metric != null) {
+ for (PageDef page : pages) {
+ if (page.acceptMetric(metric)) {
+ return page;
+ }
+ }
+ }
+
+ for (PageDef page : pages) {
+ if (page.isDefaultTab()) {
+ return page;
+ }
+ }
+
+ return null;
+ }
+
+ private native void saveResource(String resourceId, JavaScriptObject json) /*-{
+ $wnd.config['resource_key']=resourceId;
+ $wnd.config['resource']=json;
+ }-*/;
+
+
+ /**
+ * Never return null.
+ */
+ private List<PageDef> selectPages(final Resource resource) {
+ List<PageDef> pages = new ArrayList<PageDef>();
+ for (int index = 0; index < pageDefs.length(); index++) {
+ PageDef page = pageDefs.get(index);
+ if (page.acceptLanguage(resource.getLanguage()) &&
+ page.acceptQualifier(resource.getQualifier()) &&
+ page.acceptScope(resource.getScope())) {
+ pages.add(page);
+ }
+ }
+ return pages;
+ }
+
+ private void displayResourceNotFound() {
+ container.clear(); // remove the loading icon
+ }
+
+
+ static class Title extends Composite {
+ Title(final Resource resource) {
+ Grid grid = new Grid(1, 2);
+ grid.getElement().setId("rvstitle");
+ grid.setHTML(0, 0, Icons.forQualifier(resource.getQualifier()).getHTML() + " <span class='name'>" + resource.getName(true) + "</span>");
+
+ if (!"true".equals(Configuration.getParameter("popup"))) {
+ Hyperlink newWindow = new Hyperlink();
+ newWindow.setText(I18nConstants.INSTANCE.newWindow());
+ newWindow.setStyleName("command");
+ newWindow.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent clickEvent) {
+ Links.openMeasurePopup(resource.getKey(), Configuration.getParameter("metric"));
+ }
+ });
+ grid.setWidget(0, 1, newWindow);
+ }
+ grid.getColumnFormatter().setStyleName(1, "right");
+ initWidget(grid);
+ }
+ }
+
+
+ private native PageDefs loadPageDefs() /*-{
+ return $wnd.pages;
+ }-*/;
+
+ // An overlay type
+
+ static class PageDefs extends JavaScriptObject {
+ protected PageDefs() {
+ }
+
+ public final native int length() /*-{ return this.length; }-*/;
+
+ public final native PageDef get(int i) /*-{ return this[i]; }-*/;
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.java
new file mode 100644
index 00000000000..6b56ecc510c
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.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.core.violationsviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.violationsviewer.client.ViolationsViewer;
+
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.VIOLATIONS_DENSITY_KEY, CoreMetrics.WEIGHTED_VIOLATIONS_KEY, CoreMetrics.VIOLATIONS_KEY, CoreMetrics.BLOCKER_VIOLATIONS_KEY, CoreMetrics.CRITICAL_VIOLATIONS_KEY, CoreMetrics.MAJOR_VIOLATIONS_KEY, CoreMetrics.MINOR_VIOLATIONS_KEY, CoreMetrics.INFO_VIOLATIONS_KEY})
+@ResourceQualifier({Resource.QUALIFIER_CLASS,Resource.QUALIFIER_FILE})
+@UserRole(UserRole.CODEVIEWER)
+public class ViolationsViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Violations";
+ }
+
+ public String getGwtId() {
+ return ViolationsViewer.GWT_ID;
+ }
+}
+
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.java
new file mode 100644
index 00000000000..5a1767f0eb0
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.java
@@ -0,0 +1,39 @@
+/*
+ * 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.core.violationsviewer.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("No filters")
+ String noFilters();
+
+ @DefaultStringValue("Expand:")
+ String expand();
+
+ @DefaultStringValue("Loading...")
+ String loading();
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java
new file mode 100644
index 00000000000..fc911727fa8
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java
@@ -0,0 +1,159 @@
+/*
+ * 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.core.violationsviewer.client;
+
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Utils;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.SourcePanel;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.Violation;
+import org.sonar.wsclient.services.ViolationQuery;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ViolationsPanel extends SourcePanel {
+ private boolean expand = false;
+ private List<Violation> violations;
+ private Map<Integer, List<Violation>> filteredViolationsByLine = new HashMap<Integer, List<Violation>>();
+
+ public ViolationsPanel(Resource resource, String filter) {
+ super(resource);
+ loadViolations(resource, filter);
+ }
+
+ protected void loadViolations(final Resource resource, final String filter) {
+ Sonar.getInstance().findAll(ViolationQuery.createForResource(resource), new AbstractListCallback<Violation>() {
+
+ @Override
+ protected void doOnResponse(List<Violation> violations) {
+ ViolationsPanel.this.violations = violations;
+ filter(filter);
+ setStarted();
+ }
+ });
+ }
+
+ public boolean isExpand() {
+ return expand;
+ }
+
+ public void setExpand(boolean expand) {
+ this.expand = expand;
+ }
+
+ public void filter(String filter) {
+ filteredViolationsByLine.clear();
+ for (Violation violation : violations) {
+ if (filter == null || filter.equals("") || violation.getRuleKey().equals(filter) || violation.getPriority().equals(filter)) {
+ Integer line = 0;
+ if (violation.getLine() != null) {
+ line = violation.getLine();
+ }
+ List<Violation> lineViolations = filteredViolationsByLine.get(line);
+ if (lineViolations == null) {
+ lineViolations = new ArrayList<Violation>();
+ filteredViolationsByLine.put(line, lineViolations);
+ }
+ lineViolations.add(violation);
+ }
+ }
+ }
+
+ @Override
+ public boolean shouldDecorateLine(int index) {
+ if (expand) {
+ return true;
+ }
+ for (int i = index - 5; i < index + 5; i++) {
+ if (hasViolations(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected List<Row> decorateLine(int index, String source) {
+ List<Row> rows = new ArrayList<Row>();
+ List<Violation> lineViolations = filteredViolationsByLine.get(index);
+ boolean hasViolations = lineViolations != null && !lineViolations.isEmpty();
+
+ if (index > 0) {
+ String style = (hasViolations ? "red" : "");
+ Row row = new Row().setLineIndex(index, style).unsetValue().setSource(source, style);
+ rows.add(row);
+ }
+
+ if (hasViolations) {
+ for (Violation violation : lineViolations) {
+ rows.add(new ViolationRow(violation));
+ }
+ }
+ return rows;
+ }
+
+ public static class ViolationRow extends Row {
+ private Violation violation;
+
+ public ViolationRow(Violation violation) {
+ this.violation = violation;
+ }
+
+ @Override
+ public String getColumn1() {
+ return "<div class=\"bigln\">&nbsp;</div>";
+ }
+
+ @Override
+ public String getColumn2() {
+ return "";
+ }
+
+ @Override
+ public String getColumn3() {
+ return "";
+ }
+
+ @Override
+ public String getColumn4() {
+ return "<div class=\"warn\">" + Icons.forPriority(violation.getPriority()).getHTML() + "</img> "
+ + Utils.formatDateTime(violation.getCreatedAt())
+ + " <a href=\"" + Links.urlForRule(violation.getRuleKey(), false)
+ + "\" onclick=\"window.open(this.href,'rule','height=800,width=900,scrollbars=1,resizable=1');return false;\" title=\""
+ + violation.getRuleKey() + "\"><b>"
+ + Utils.escapeHtml(violation.getRuleName()) + "</b></a> : "
+ + Utils.escapeHtml(violation.getMessage()) + "</div>";
+ }
+ }
+
+ private boolean hasViolations(int lineIndex) {
+ if (lineIndex < 0) {
+ return false;
+ }
+ List<Violation> list = filteredViolationsByLine.get(lineIndex);
+ return list != null && !list.isEmpty();
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java
new file mode 100644
index 00000000000..2ad7caa8bbb
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java
@@ -0,0 +1,205 @@
+/*
+ * 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.core.violationsviewer.client;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.gen2.table.override.client.Grid;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Metrics;
+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.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class ViolationsViewer extends Page {
+ public static final String GWT_ID = "org.sonar.plugins.core.violationsviewer.ViolationsViewer";
+
+ private Resource resource;
+ private final Panel mainPanel = new VerticalPanel();
+ private final Loading loading = new Loading(I18nConstants.INSTANCE.loading());
+
+ // header
+ private Grid header = null;
+ private ListBox filterBox = null;
+ private CheckBox expandCheckbox = null;
+ private String defaultFilter;
+
+ // source
+ private ViolationsPanel sourcePanel;
+
+ private boolean resourceHasViolations = false;
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ this.resource = resource;
+ mainPanel.clear();
+ mainPanel.add(loading);
+ mainPanel.setWidth("100%");
+ mainPanel.setStyleName("gwt-Violations");
+
+ header = new Grid(1, 5);
+ header.setWidth("100%");
+ header.setStylePrimaryName("gwt-ViewerHeader");
+ header.getCellFormatter().setStyleName(0, 0, "thin left");
+
+ initDefaultFilter();
+ sourcePanel = new ViolationsPanel(resource, defaultFilter);
+
+ header.setHTML(0, 1, "<div class='cell'><span class='note'>" + I18nConstants.INSTANCE.filter() + "</span></div>");
+ header.getCellFormatter().setStyleName(0, 1, "right");
+
+ filterBox = new ListBox();
+ filterBox.addItem(I18nConstants.INSTANCE.noFilters(), "");
+ filterBox.setStyleName("small");
+
+ filterBox.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ String filter = filterBox.getValue(filterBox.getSelectedIndex());
+ loadSources();
+ sourcePanel.filter(filter);
+ sourcePanel.refresh();
+ }
+ });
+
+ header.setWidget(0, 2, filterBox);
+ header.getCellFormatter().setStyleName(0, 2, "thin cell right");
+
+ header.setHTML(0, 3, "<div class='note'>" + I18nConstants.INSTANCE.expand() + "</div>");
+ header.getCellFormatter().setStyleName(0, 3, "thin right");
+
+ expandCheckbox = new CheckBox();
+ expandCheckbox.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ loadSources();
+ sourcePanel.setExpand(expandCheckbox.getValue());
+ sourcePanel.refresh();
+ }
+ });
+ header.setWidget(0, 4, expandCheckbox);
+ header.getCellFormatter().setStyleName(0, 4, "thin cell left");
+
+ loadRulePriorities();
+ return mainPanel;
+ }
+
+ private void initDefaultFilter() {
+ defaultFilter = Configuration.getRequestParameter("rule");
+ if (defaultFilter == null) {
+ defaultFilter = Configuration.getRequestParameter("priority");
+ }
+ }
+
+ private void loadRulePriorities() {
+ final ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.VIOLATIONS)
+ .setExcludeRulePriorities(false);
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>(loading) {
+ @Override
+ protected void doOnResponse(Resource resource) {
+ setResourceHasViolations(resource);
+ displayRulePriorities(resource);
+ loadRules(resource);
+ }
+ });
+ }
+
+ private void displayRulePriorities(Resource resource) {
+ final Grid grid = new Grid(1, 10);
+ header.setWidget(0, 0, grid);
+
+ List<Measure> measures = resource.getMeasures();
+ displayRulePriority(grid, 0, "BLOCKER", measures);
+ displayRulePriority(grid, 2, "CRITICAL", measures);
+ displayRulePriority(grid, 4, "MAJOR", measures);
+ displayRulePriority(grid, 6, "MINOR", measures);
+ displayRulePriority(grid, 8, "INFO", measures);
+ }
+
+ private void displayRulePriority(final Grid grid, final int column, final String priority, final List<Measure> measures) {
+ String value = "0";
+ for (Measure measure : measures) {
+ if (priority.equals(measure.getRulePriority())) {
+ value = measure.getFormattedValue();
+ filterBox.addItem(priority + " (" + value + ")", priority);
+ if (priority.equals(defaultFilter)) {
+ filterBox.setSelectedIndex(filterBox.getItemCount() - 1);
+ }
+ continue;
+ }
+ }
+ grid.setHTML(0, column, Icons.forPriority(priority).getHTML());
+ grid.setHTML(0, column + 1, value);
+ grid.getCellFormatter().setStyleName(0, column, "thin metric right");
+ grid.getCellFormatter().setStyleName(0, column + 1, "thin left value");
+ }
+
+ private void loadRules(Resource resource) {
+ final ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.VIOLATIONS)
+ .setExcludeRules(false);
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>(loading) {
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ setResourceHasViolations(resource);
+ displayRules(resource);
+ loadSources();
+ }
+ });
+ }
+
+ private void setResourceHasViolations(Resource resource) {
+ resourceHasViolations = resource != null && resource.getMeasure(Metrics.VIOLATIONS) != null;
+ }
+
+ private void displayRules(Resource resource) {
+ Collections.sort(resource.getMeasures(), new Comparator<Measure>() {
+ public int compare(Measure m1, Measure m2) {
+ return m1.getRuleName().compareTo(m2.getRuleName());
+ }
+ });
+ filterBox.addItem("", "");
+ for (Measure measure : resource.getMeasures()) {
+ filterBox.addItem(measure.getRuleName() + " (" + measure.getFormattedValue() + ")", measure.getRuleKey());
+ if (measure.getRuleKey().equals(defaultFilter)) {
+ filterBox.setSelectedIndex(filterBox.getItemCount() - 1);
+ }
+ }
+ loading.removeFromParent();
+ mainPanel.add(header);
+ }
+
+ private void loadSources() {
+ mainPanel.remove(sourcePanel);
+ if (resourceHasViolations || expandCheckbox.getValue()) {
+ mainPanel.add(sourcePanel);
+ }
+ }
+}
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml
new file mode 100644
index 00000000000..cde524c0f66
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml
@@ -0,0 +1,11 @@
+<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.api.web.gwt.Sonar"/>
+
+ <stylesheet src="clouds.css"/>
+
+ <entry-point class="org.sonar.plugins.core.clouds.client.GwtClouds"/>
+
+</module>
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css
new file mode 100644
index 00000000000..c2790c3c95c
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css
@@ -0,0 +1,29 @@
+.tag {
+ padding: 0px;
+ cursor: pointer;
+}
+
+.inline{
+ display: inline;
+}
+
+a {
+ border-bottom: 0 none;
+}
+
+.tab_title {
+ white-space: nowrap;
+}
+
+.metricSelectBox {
+ float: right;
+}
+
+.metricSelectBox .labelText {
+ padding-top: 2px;
+ padding-right: 5px;
+
+}
+
+
+
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/test.html
new file mode 100644
index 00000000000..c4292e18121
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/clouds/public/test.html
@@ -0,0 +1,39 @@
+<!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>Clouds xxx</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "resource_key" : "org.codehaus.sonar:sonar-plugin-api",
+ "permalink_url_base" : "http://localhost:9000/dev/views/project/org.codehaus.sonar:sonar/org.sonar.plugins.core.clouds.GwtClouds?foo=bar"
+ };
+</script>
+
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ 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="#"
+ 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="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="gwtpage">
+</div>
+
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.core.clouds.GwtClouds.nocache.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml
new file mode 100644
index 00000000000..b14b0e2f735
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml
@@ -0,0 +1,10 @@
+<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"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <entry-point class="org.sonar.plugins.core.coverageviewer.client.CoverageViewer"/>
+
+</module>
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html
new file mode 100644
index 00000000000..181a04a6a72
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html
@@ -0,0 +1,41 @@
+<!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>Code coverage</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "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="#"
+ 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="#"
+ 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="#"
+ 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.core.coverageviewer.CoverageViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_coverageviewer_CoverageViewer();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml
new file mode 100644
index 00000000000..be36aa7ef89
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml
@@ -0,0 +1,10 @@
+<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"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <entry-point class="org.sonar.plugins.core.defaultsourceviewer.client.GwtDefaultSourceViewer"/>
+
+</module>
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html
new file mode 100644
index 00000000000..02a9f684766
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html
@@ -0,0 +1,26 @@
+<!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>
+<div id="gwtpage"></div>
+<script type="text/javascript">
+var config = {
+ "sonar_url": "http://localhost:9000",
+ "resource":[{"id":97, "key":"org.apache.struts:struts-core:org.apache.struts.config.ConfigRuleSet","scope": "FIL", "qualifier": "CLA", "name": "Struts Core", "lang":"java"}]
+};
+var modules = {};
+var rp = {};
+
+</script>
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.core.defaultsourceviewer.GwtDefaultSourceViewer.nocache.js"></script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml
new file mode 100644
index 00000000000..b480cc0b418
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.xml.XML"/>
+ <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"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <stylesheet src="DuplicationsViewer.css"/>
+
+ <entry-point class="org.sonar.plugins.core.duplicationsviewer.client.DuplicationsViewer"/>
+
+</module>
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css
new file mode 100644
index 00000000000..2cc0ced131c
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css
@@ -0,0 +1,48 @@
+.gwt-DuplicationsPanel {
+ width: 100%;
+}
+
+.duplicationsTable {
+ border: 1px solid #C0C0C0;
+ width: 100%;
+ vertical-align: top;
+}
+
+.duplicationsTable td {
+ vertical-align: top;
+ text-align: left;
+ padding: 3px;
+ white-space: nowrap;
+}
+
+.duplicationsTable #expandCollapseCol {
+ width: 60px;
+}
+
+.duplicationsTable #nbLineCol {
+ width: 80px;
+}
+
+.duplicationsTable #lineFromCol {
+ width: 80px;
+}
+
+.duplicationsTable #fileCol {
+ width: 80px;
+}
+
+.duplicationsTable .header {
+ background-color: #EFEFEF;
+ font-weight: bold;
+ color: #333;
+ vertical-align: top;
+}
+
+.expandCollapseLink {
+ margin-left: 5px;
+}
+
+.gwt-SourcePanel table.sources td {
+ vertical-align: top;
+ padding: 0px;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html
new file mode 100644
index 00000000000..37b1bb43253
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html
@@ -0,0 +1,43 @@
+<!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>Duplications</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "viewer_resource_key" : "org.sonar.tests:reference:org.sonar.samples.duplicated_lines_within_same_class.DuplicatedLinesInSameClass"
+ };
+
+
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ 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="#"
+ 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="#"
+ 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.core.duplicationsviewer.DuplicationsViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_duplicationsviewer_DuplicationsViewer();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml
new file mode 100644
index 00000000000..ad7ecd8c11f
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml
@@ -0,0 +1,10 @@
+<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"/>
+ <inherits name="com.google.gwt.i18n.I18N"/>
+ <stylesheet src="hotspots.css"/>
+ <entry-point class="org.sonar.plugins.core.hotspots.client.GwtHotspots"/>
+ <extend-property name="locale" values="fr"/>
+</module> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..efd8a8c42d6
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties
@@ -0,0 +1,16 @@
+# This file must use UTF-8 encoding
+
+titleMostViolatedRules=Règles les moins respectées
+titleMostViolatedResources=Les moins respectueux des règles
+titleLongestTests=Les plus long tests
+titleMostComplexResources=Les plus complexes
+titleMostDuplicatedResources=Les plus dupliqués
+titleLessTested=Le plus de lignes non testées
+titleMostComplexMethods=Avec les méthodes les plus complexes
+titleMostUndocumentedAPI=Les API les moins documentées
+noMeasures=Aucune mesure
+anyPriority=Toutes les priorités
+moreDetails=Détails
+lcom4=Manque de cohésion entre méthodes
+rfc=Response for class
+designTitle= \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css
new file mode 100644
index 00000000000..6ae0b1d2747
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css
@@ -0,0 +1,102 @@
+.gwt-Hotspots {
+ width: 780px;
+}
+
+.gwt-Hotspots td {
+ width: 380px;
+ vertical-align: top;
+}
+
+.gwt-HotspotPanel {
+ width: 100%;
+}
+
+.gwt-HotspotPanel .header {
+ color: #333;
+ font-size: 93%;
+ font-weight: bold;
+ padding: 3px 0px 3px 5px;
+ white-space: nowrap;
+}
+.hotspotcol {
+ vertical-align: top;
+}
+
+.gwt-Hotspot {
+ border: 1px solid #ccc;
+ border-top-width: 2px;
+ border-bottom-width: 2px;
+ margin-left: 5px;
+ margin-right: 5px;
+ margin-bottom: 10px;
+ width: 390px;
+}
+
+.gwt-Hotspot td {
+ padding: 3px 4px;
+ line-height: 18px;
+}
+
+.gwt-Hotspot td.small {
+ padding: 3px 1px;
+}
+
+.gwt-Hotspot .header {
+ color: #333;
+ font-size: 93%;
+ font-weight: bold;
+ padding: 4px 0 3px 9px;
+}
+
+.gwt-Hotspot .resourceCell {
+ width: 99%;
+}
+
+.gwt-Hotspot .ccResourceCell {
+ width: 60%;
+}
+
+.gwt-Hotspot .resultCell {
+ width: 1%;
+ white-space: nowrap;
+ text-align: right;
+ border-right: none;
+}
+
+.gwt-Hotspot .ccResultCell {
+ border-right: 1px solid #C0C0C0;
+ width: 1%;
+ white-space: nowrap;
+ text-align: right;
+}
+
+.gwt-Hotspot .graphCell {
+ width: 1%;
+ white-space: nowrap;
+ text-align: right;
+ border-left: none;
+}
+
+.gwt-Hotspot .header.ccHeaderResource {
+ width: 60%;
+}
+
+.gwt-Hotspot .header.headerResource {
+ width: 70%;
+}
+
+.gwt-Hotspot .header.headerEmptyResults {
+ width: 100%;
+}
+
+.gwt-Hotspot .header.headerResult {
+ width: 20%;
+}
+
+.gwt-Hotspot .header.ccHeaderResult {
+ width: 30%;
+}
+
+.gwt-Hotspot .header.headerGraph {
+ width: 10%;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html
new file mode 100644
index 00000000000..d66011fb116
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html
@@ -0,0 +1,36 @@
+<!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>Hotspots</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 config = {
+ "sonar_url": "http://localhost:9000",
+ "resource_key" : "org.apache.struts:struts-parent"
+ };
+</script>
+
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ 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="#"
+ 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="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="gwtpage">
+</div>
+
+<script type="text/javascript" language="javascript"
+ src="org.sonar.plugins.core.hotspots.GwtHotspots.nocache.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml
new file mode 100644
index 00000000000..c5e1c33bf6d
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.xml.XML"/>
+ <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"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <stylesheet src="TestsViewer.css"/>
+
+ <entry-point class="org.sonar.plugins.core.testdetailsviewer.client.TestsViewer"/>
+
+</module> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css
new file mode 100644
index 00000000000..0ce428402b6
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css
@@ -0,0 +1,88 @@
+.gwt-TestDetailsPanel {
+ width: 100%;
+}
+
+.gwt-TestDetailsPanel .error {
+ background-image: url("images/error.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ background-color: transparent;
+ border: none;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.gwt-TestDetailsPanel .failure {
+ background-image: url("images/failure.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.gwt-TestDetailsPanel .skipped {
+ background-image: url("images/skipped.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.gwt-TestDetailsPanel .ok {
+ background-image: url("images/ok.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.detailsTable {
+ border: 1px solid #C0C0C0;
+ width: 100%;
+ vertical-align: top;
+}
+
+.detailsTable td {
+ vertical-align: top;
+ text-align: left;
+ padding: 3px;
+}
+
+.detailsTable .header {
+ background-color: #EFEFEF;
+ font-weight: bold;
+ color: #333;
+ vertical-align: top;
+}
+
+.detailsTable #iCol {
+ width: 25px;
+}
+
+.detailsTable #dCol {
+ width: 70px;
+}
+
+.detailsTable #expandCollapseCol {
+ width: 60px;
+ text-align: right;
+}
+
+.detailsTable #noLinkExpandCollapseCol {
+ width: 0px;
+ padding: 0px;
+}
+
+.stackPanel {
+ padding: 5px;
+ border: 1px solid #C0C0C0;
+}
+
+.expandCollapseLink {
+ text-align: right;
+ margin-right: 5px;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.png b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.png
new file mode 100644
index 00000000000..c37bd062e60
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.png
Binary files differ
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.png b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.png
new file mode 100644
index 00000000000..628cf2dae3d
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.png
Binary files differ
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.png b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.png
new file mode 100644
index 00000000000..89c8129a490
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.png
Binary files differ
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.png b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.png
new file mode 100644
index 00000000000..5c870176d4d
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.png
Binary files differ
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html
new file mode 100644
index 00000000000..6ec71b51686
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html
@@ -0,0 +1,41 @@
+<!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>TestDetails</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "viewer_resource_key" : "org.sonar.tests.test-failures:moduleA:ch.hortis.sonar.samples.testFailures.moduleA.FailTest"
+ };
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ 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="#"
+ 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="#"
+ 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.core.testdetailsviewer.TestsViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_testdetailsviewer_TestsViewer();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.gwt.xml
new file mode 100644
index 00000000000..8299ca591d0
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.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.api.web.gwt.Sonar"/>
+ <inherits name="org.sonar.Sonar"/>
+ <stylesheet src="pageselector.css" />
+
+ <entry-point class="org.sonar.plugins.core.ui.pageselector.client.PageSelector"/>
+
+ <extend-property name="locale" values="fr"/>
+</module> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..067f7231c81
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties
@@ -0,0 +1,2 @@
+# This file must use UTF-8 encoding
+newWindow=Nouvelle fenêtre \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css
new file mode 100644
index 00000000000..367e5632efb
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css
@@ -0,0 +1,25 @@
+/* #rvs is "resource viewer selector" */
+#rvs {
+ width: 100%;
+ margin-top: 10px;
+ border: 1px solid #CCC;
+}
+#rvstitle {
+ width: 100%;
+ margin: 5px;
+}
+#rvstitle .name {
+}
+#rvstitle .right {
+ float: none;
+ text-align: right;
+}
+#rvs .command {
+ text-align: right;
+ cursor: pointer;
+ margin-right: 15px;
+}
+#rvs .command a {
+ color: #444;
+ text-decoration: underline;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html
new file mode 100644
index 00000000000..0f1e49dd8f9
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html
@@ -0,0 +1,41 @@
+<!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>Page selector</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 config = {
+ "sonar_url": "http://localhost:9000",
+ "version": "2.0",
+ "metric": "ncloc",
+ "resource" : null
+ };
+ var pages = [
+ {"id": "org.sonar.plugins.foo.Foo", "gwt": false, "url": "http://localhost:9000/project/index/1", "metrics":[], "scopes":["PRJ"], "qualifiers": ["TRK", "BRC"], "langs": ["java"]},
+ {"id": "org.sonar.plugins.core.clouds.GwtClouds", "gwt": true, "metrics":["lcom4","noc"], "scopes":["PRJ"], "qualifiers": ["TRK", "BRC"], "langs": ["java"]},
+ {"id": "org.sonar.plugins.Overview", "gwt": true, "metrics":[], "scopes":[], "qualifiers": [], "langs": []},
+ {"id": "org.sonar.plugins.Rails", "gwt": false, "metrics":["clirr"], "scopes":[], "qualifiers": ["FIL"], "langs": []}
+
+ ];
+ var entrypoints = {}
+
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#" 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="#" 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="#" onclick="javascript:$('info').hide();return false;">hide</a>]</div>
+
+<a href="#" onclick="sr('org.apache.struts:struts-parent');">load struts</a>
+
+<div id="pageselector"> </div>
+
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.core.ui.pageselector.PageSelector.nocache.js"></script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.gwt.xml b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.gwt.xml
new file mode 100644
index 00000000000..fed31ca288e
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.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"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+ <inherits name="com.google.gwt.i18n.I18N"/>
+
+ <entry-point class="org.sonar.plugins.core.violationsviewer.client.ViolationsViewer"/>
+
+ <extend-property name="locale" values="fr"/>
+</module>
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..73a4d8909a0
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties
@@ -0,0 +1,5 @@
+# This file must use UTF-8 encoding
+filter=Filtre:
+noFilters=Aucun filtre
+expand=Développer:
+loading=En cours de chargement... \ No newline at end of file
diff --git a/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html
new file mode 100644
index 00000000000..7c0a06d111e
--- /dev/null
+++ b/plugins/sonar-core-gwt/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html
@@ -0,0 +1,41 @@
+<!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>Violations</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "viewer_resource_key": "org.sonar.tests:reference:org.sonar.samples.PrivateClass"
+ };
+ var request_parameters = {"priority": "BLOCKER"}
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ 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="#"
+ 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="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="resource_viewers">
+</div>
+<script type="text/javascript" language="javascript"
+ src="org.sonar.plugins.core.violationsviewer.ViolationsViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_violationsviewer_ViolationsViewer();">load</a>
+</body>
+</html> \ No newline at end of file