diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2010-12-02 14:05:25 +0000 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2010-12-02 14:05:25 +0000 |
commit | 625da3dc6bc5099e7b9c22a31b005adb5318b6dc (patch) | |
tree | f3dfb9a18f1a44d5b9b40d2f58596a306f0efff8 /plugins/sonar-core-gwt | |
parent | 1a51c6bf53bf1f526c3c39aa86569b042f8be810 (diff) | |
download | sonarqube-625da3dc6bc5099e7b9c22a31b005adb5318b6dc.tar.gz sonarqube-625da3dc6bc5099e7b9c22a31b005adb5318b6dc.zip |
extract GWT components from sonar-core-plugin
Diffstat (limited to 'plugins/sonar-core-gwt')
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(" ", ""); + row.setValue2(" ", ""); + + 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(" ", "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() + "%'> </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(" "); + 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\"> </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> [<a href="#" + onclick="javascript:$('error').hide();return false;">hide</a>] +</div> +<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> [<a href="#" + onclick="javascript:$('warning').hide();return false;">hide</a>] +</div> +<div class="notice" id="info" style="display:none"><span id="infomsg"></span> [<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> [<a href="#" + onclick="javascript:$('error').hide();return false;">hide</a>] +</div> +<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> [<a href="#" + onclick="javascript:$('warning').hide();return false;">hide</a>] +</div> +<div class="notice" id="info" style="display:none"><span id="infomsg"></span> [<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> [<a href="#" + onclick="javascript:$('error').hide();return false;">hide</a>] +</div> +<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> [<a href="#" + onclick="javascript:$('warning').hide();return false;">hide</a>] +</div> +<div class="notice" id="info" style="display:none"><span id="infomsg"></span> [<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> [<a href="#" + onclick="javascript:$('error').hide();return false;">hide</a>] +</div> +<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> [<a href="#" + onclick="javascript:$('warning').hide();return false;">hide</a>] +</div> +<div class="notice" id="info" style="display:none"><span id="infomsg"></span> [<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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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> [<a href="#" + onclick="javascript:$('error').hide();return false;">hide</a>] +</div> +<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> [<a href="#" + onclick="javascript:$('warning').hide();return false;">hide</a>] +</div> +<div class="notice" id="info" style="display:none"><span id="infomsg"></span> [<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> [<a href="#" onclick="javascript:$('error').hide();return false;">hide</a>]</div> +<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> [<a href="#" onclick="javascript:$('warning').hide();return false;">hide</a>]</div> +<div class="notice" id="info" style="display:none"><span id="infomsg"></span> [<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> [<a href="#" + onclick="javascript:$('error').hide();return false;">hide</a>] +</div> +<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> [<a href="#" + onclick="javascript:$('warning').hide();return false;">hide</a>] +</div> +<div class="notice" id="info" style="display:none"><span id="infomsg"></span> [<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 |