From: David Gageot Date: Wed, 4 Jul 2012 16:02:37 +0000 (+0200) Subject: SONAR-3529 First baby step in providing property sets X-Git-Tag: 3.2~248 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b547f32a3de0381e41b2da6cfab1545f7447b315;p=sonarqube.git SONAR-3529 First baby step in providing property sets --- diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 86cb29c81a7..a9fb669fcd1 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -19,6 +19,8 @@ */ package org.sonar.plugins.core; +import org.sonar.plugins.core.widgets.DemoWidget; + import com.google.common.collect.ImmutableList; import org.sonar.api.CoreProperties; import org.sonar.api.Extension; @@ -336,6 +338,7 @@ public final class CorePlugin extends SonarPlugin { ReviewsMetricsWidget.class, TreemapWidget.class, FilterWidget.class, + DemoWidget.class, // dashboards DefaultDashboard.class, diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java index e1c8cdde530..274e77f926c 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/CustomMeasuresWidget.java @@ -21,20 +21,18 @@ package org.sonar.plugins.core.widgets; import org.sonar.api.web.*; -@WidgetProperties( - { - @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric4", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric5", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric6", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric7", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric8", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric9", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric10", type = WidgetPropertyType.METRIC) - } -) +@WidgetProperties({ + @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric4", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric5", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric6", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric7", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric8", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric9", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric10", type = WidgetPropertyType.METRIC) +}) public class CustomMeasuresWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "custom_measures"; @@ -48,4 +46,4 @@ public class CustomMeasuresWidget extends AbstractRubyTemplate implements RubyRa protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/custom_measures.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DemoWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DemoWidget.java new file mode 100644 index 00000000000..48549921bef --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/DemoWidget.java @@ -0,0 +1,60 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.core.widgets; + +import org.sonar.api.web.WidgetPropertySet; + +import org.sonar.api.web.AbstractRubyTemplate; +import org.sonar.api.web.RubyRailsWidget; +import org.sonar.api.web.WidgetCategory; +import org.sonar.api.web.WidgetProperties; +import org.sonar.api.web.WidgetProperty; +import org.sonar.api.web.WidgetPropertyType; +import org.sonar.api.web.WidgetScope; + +import static org.sonar.api.web.WidgetScope.*; + +@WidgetCategory("Global") +@WidgetScope(GLOBAL) +@WidgetProperties(sets = { + @WidgetPropertySet(key = "set1", + value = { + @WidgetProperty(key = "key1", type = WidgetPropertyType.STRING), + @WidgetProperty(key = "key2", type = WidgetPropertyType.STRING) + }), @WidgetPropertySet(key = "set2", + value = { + @WidgetProperty(key = "key3", type = WidgetPropertyType.STRING) + })}, value = { + @WidgetProperty(key = "key4", type = WidgetPropertyType.STRING) +}) +public class DemoWidget extends AbstractRubyTemplate implements RubyRailsWidget { + public String getId() { + return "demo"; + } + + public String getTitle() { + return "Demo"; + } + + @Override + protected String getTemplatePath() { + return "/org/sonar/plugins/core/widgets/demo.html.erb"; + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java index f69f6ad46bb..a4a447679d4 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMetricWidget.java @@ -26,14 +26,12 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "Hotspots" }) -@WidgetProperties( - { - @WidgetProperty(key = "title", type = WidgetPropertyType.STRING), - @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"), - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5") - } -) +@WidgetCategory("Hotspots") +@WidgetProperties({ + @WidgetProperty(key = "title", type = WidgetPropertyType.STRING), + @WidgetProperty(key = "metric", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"), + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5") +}) public class HotspotMetricWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "hotspot_metric"; @@ -47,4 +45,4 @@ public class HotspotMetricWidget extends AbstractRubyTemplate implements RubyRai protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/hotspots/hotspot_metric.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java index 6c78fb9c14c..383cfcecf16 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedResourcesWidget.java @@ -26,12 +26,10 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "Hotspots" }) -@WidgetProperties( - { - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5") - } -) +@WidgetCategory("Hotspots") +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5") +}) public class HotspotMostViolatedResourcesWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { @@ -46,4 +44,4 @@ public class HotspotMostViolatedResourcesWidget extends AbstractRubyTemplate imp protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/hotspots/hotspot_most_violated_resources.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java index b58ed0cd89a..7fff488beb6 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/HotspotMostViolatedRulesWidget.java @@ -22,12 +22,10 @@ package org.sonar.plugins.core.widgets; import org.sonar.api.web.*; @WidgetCategory("Hotspots") -@WidgetProperties( - { - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5"), - @WidgetProperty(key = "defaultSeverity", type = WidgetPropertyType.STRING, description = "Values: BLOCKER, CRITICAL, MAJOR, MINOR, INFO") - } -) +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5"), + @WidgetProperty(key = "defaultSeverity", type = WidgetPropertyType.STRING, description = "Values: BLOCKER, CRITICAL, MAJOR, MINOR, INFO") +}) public class HotspotMostViolatedRulesWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "hotspot_most_violated_rules"; @@ -41,4 +39,4 @@ public class HotspotMostViolatedRulesWidget extends AbstractRubyTemplate impleme protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/hotspots/hotspot_most_violated_rules.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java index 47f2f63c562..be8a76ab3d7 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java @@ -26,7 +26,7 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({"History"}) +@WidgetCategory("History") @WidgetProperties({ @WidgetProperty(key = "title", type = WidgetPropertyType.STRING), @WidgetProperty(key = "numberOfColumns", type = WidgetPropertyType.INTEGER, defaultValue = "3"), diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java index 7ecc27de672..6a2857d7839 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java @@ -26,17 +26,15 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "History" }) -@WidgetProperties( - { - @WidgetProperty(key = "chartTitle", type = WidgetPropertyType.STRING), - @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"), - @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC), - @WidgetProperty(key = "hideEvents", type = WidgetPropertyType.BOOLEAN), - @WidgetProperty(key = "chartHeight", type = WidgetPropertyType.INTEGER, defaultValue = "80") - } -) +@WidgetCategory("History") +@WidgetProperties({ + @WidgetProperty(key = "chartTitle", type = WidgetPropertyType.STRING), + @WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"), + @WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC), + @WidgetProperty(key = "hideEvents", type = WidgetPropertyType.BOOLEAN), + @WidgetProperty(key = "chartHeight", type = WidgetPropertyType.INTEGER, defaultValue = "80") +}) public class TimelineWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "timeline"; @@ -50,4 +48,4 @@ public class TimelineWidget extends AbstractRubyTemplate implements RubyRailsWid protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/timeline.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java index f973ab6bf36..6e527219c16 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/FalsePositiveReviewsWidget.java @@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "Reviews" }) -@WidgetProperties( - { - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", - description="Maximum number of reviews displayed at the same time.") - } -) +@WidgetCategory("Reviews") +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", + description = "Maximum number of reviews displayed at the same time.") +}) public class FalsePositiveReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "false_positive_reviews"; @@ -46,4 +44,4 @@ public class FalsePositiveReviewsWidget extends AbstractRubyTemplate implements protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/reviews/false_positive_reviews.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java index 0e3a385576a..ed95dddabeb 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/MyReviewsWidget.java @@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "Reviews" }) -@WidgetProperties( - { - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", - description="Maximum number of reviews displayed at the same time.") - } -) +@WidgetCategory("Reviews") +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", + description = "Maximum number of reviews displayed at the same time.") +}) public class MyReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "my_reviews"; @@ -46,4 +44,4 @@ public class MyReviewsWidget extends AbstractRubyTemplate implements RubyRailsWi protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/reviews/my_reviews.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java index eb1abe18dc0..2b80d72572e 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/PlannedReviewsWidget.java @@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "Action plans", "Reviews" }) -@WidgetProperties( - { - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", - description="Maximum number of reviews displayed at the same time.") - } -) +@WidgetCategory({"Action plans", "Reviews"}) +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", + description = "Maximum number of reviews displayed at the same time.") +}) public class PlannedReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "planned_reviews"; @@ -46,4 +44,4 @@ public class PlannedReviewsWidget extends AbstractRubyTemplate implements RubyRa protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/reviews/planned_reviews.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java index 1551e84233e..c0200af47ac 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/ProjectReviewsWidget.java @@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "Reviews" }) -@WidgetProperties( - { - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", - description="Maximum number of reviews displayed at the same time.") - } -) +@WidgetCategory("Reviews") +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", + description = "Maximum number of reviews displayed at the same time.") +}) public class ProjectReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "project_reviews"; @@ -46,4 +44,4 @@ public class ProjectReviewsWidget extends AbstractRubyTemplate implements RubyRa protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/reviews/project_reviews.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java index 83660e87af3..014fc25acf5 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/reviews/UnplannedReviewsWidget.java @@ -26,13 +26,11 @@ import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; import org.sonar.api.web.WidgetPropertyType; -@WidgetCategory({ "Action plans", "Reviews" }) -@WidgetProperties( - { - @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", - description="Maximum number of reviews displayed at the same time.") - } -) +@WidgetCategory({"Action plans", "Reviews"}) +@WidgetProperties({ + @WidgetProperty(key = "numberOfLines", type = WidgetPropertyType.INTEGER, defaultValue = "5", + description = "Maximum number of reviews displayed at the same time.") +}) public class UnplannedReviewsWidget extends AbstractRubyTemplate implements RubyRailsWidget { public String getId() { return "unplanned_reviews"; @@ -46,4 +44,4 @@ public class UnplannedReviewsWidget extends AbstractRubyTemplate implements Ruby protected String getTemplatePath() { return "/org/sonar/plugins/core/widgets/reviews/unplanned_reviews.html.erb"; } -} \ No newline at end of file +} diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/demo.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/demo.html.erb new file mode 100644 index 00000000000..197d45b202c --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/demo.html.erb @@ -0,0 +1,4 @@ +Key1: <%= widget_properties['key1'] -%> +Key2: <%= widget_properties['key2'] -%> +Key3: <%= widget_properties['key3'] -%> +Key4: <%= widget_properties['key4'] -%> diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/PropertyValidation.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/PropertyValidation.java new file mode 100644 index 00000000000..d49158d732e --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/PropertyValidation.java @@ -0,0 +1,30 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.web; + +public interface PropertyValidation { + boolean validate(String value); + + class None implements PropertyValidation { + public boolean validate(String value) { + return true; + } + }; +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java index 03b302cd395..f4fd18a874f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperties.java @@ -25,4 +25,6 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface WidgetProperties { WidgetProperty[] value() default {}; + + WidgetPropertySet[] sets() default {}; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java index 4fd661a7dfb..652076a1d48 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetProperty.java @@ -37,4 +37,6 @@ public @interface WidgetProperty { String description() default ""; boolean optional() default true; + + Class validation() default PropertyValidation.None.class; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetPropertySet.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetPropertySet.java new file mode 100644 index 00000000000..28de523e788 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/WidgetPropertySet.java @@ -0,0 +1,44 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.web; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface WidgetPropertySet { + String key(); + + WidgetProperty[] value(); + + static WidgetPropertySet DEFAULT = new WidgetPropertySet() { + public Class annotationType() { + return WidgetPropertySet.class; + } + + public WidgetProperty[] value() { + return null; + } + + public String key() { + return ""; + } + }; +} diff --git a/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java b/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java index ae3c1b29798..0770add2d14 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/ViewProxy.java @@ -20,7 +20,10 @@ package org.sonar.server.ui; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Maps; +import com.google.common.collect.SetMultimap; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.CompareToBuilder; @@ -43,6 +46,7 @@ import org.sonar.api.web.WidgetLayout; import org.sonar.api.web.WidgetLayoutType; import org.sonar.api.web.WidgetProperties; import org.sonar.api.web.WidgetProperty; +import org.sonar.api.web.WidgetPropertySet; import org.sonar.api.web.WidgetScope; import java.util.Collection; @@ -51,7 +55,8 @@ import java.util.Map; @SuppressWarnings("rawtypes") public class ViewProxy implements Comparable { - private V view; + private final V view; + private final boolean isWidget; private String[] sections = {NavigationSection.HOME}; private String[] userRoles = {}; private String[] resourceScopes = {}; @@ -60,16 +65,17 @@ public class ViewProxy implements Comparable { private String[] defaultForMetrics = {}; private String description = ""; private Map widgetPropertiesByKey = Maps.newHashMap(); + private SetMultimap widgetPropertiesBySet = LinkedHashMultimap.create(); private String[] widgetCategories = {}; private WidgetLayoutType widgetLayout = WidgetLayoutType.DEFAULT; private boolean isDefaultTab = false; - private boolean isWidget = false; private boolean isGlobal = false; private String[] mandatoryMeasures = {}; private String[] needOneOfMeasures = {}; - public ViewProxy(final V view) { + public ViewProxy(V view) { this.view = view; + this.isWidget = (view instanceof Widget); initUserRoles(view); initSections(view); @@ -83,8 +89,6 @@ public class ViewProxy implements Comparable { initWidgetLayout(view); initWidgetGlobal(view); initRequiredMeasures(view); - - isWidget = (view instanceof Widget); } private void initRequiredMeasures(V view) { @@ -95,14 +99,14 @@ public class ViewProxy implements Comparable { } } - private void initWidgetLayout(final V view) { + private void initWidgetLayout(V view) { WidgetLayout layoutAnnotation = AnnotationUtils.getAnnotation(view, WidgetLayout.class); if (layoutAnnotation != null) { widgetLayout = layoutAnnotation.value(); } } - private void initWidgetCategory(final V view) { + private void initWidgetCategory(V view) { WidgetCategory categAnnotation = AnnotationUtils.getAnnotation(view, WidgetCategory.class); if (categAnnotation != null) { widgetCategories = categAnnotation.value(); @@ -125,29 +129,35 @@ public class ViewProxy implements Comparable { } } - private void initWidgetProperties(final V view) { + private void initWidgetProperties(V view) { WidgetProperties propAnnotation = AnnotationUtils.getAnnotation(view, WidgetProperties.class); if (propAnnotation != null) { + for (WidgetPropertySet set : propAnnotation.sets()) { + for (WidgetProperty property : set.value()) { + widgetPropertiesBySet.put(set, property); + widgetPropertiesByKey.put(property.key(), property); + } + } for (WidgetProperty property : propAnnotation.value()) { + widgetPropertiesBySet.put(WidgetPropertySet.DEFAULT, property); widgetPropertiesByKey.put(property.key(), property); } } } - private void initDescription(final V view) { + private void initDescription(V view) { Description descriptionAnnotation = AnnotationUtils.getAnnotation(view, Description.class); if (descriptionAnnotation != null) { description = descriptionAnnotation.value(); } } - private void initDefaultTabInfo(final V view) { + private void initDefaultTabInfo(V view) { DefaultTab defaultTabAnnotation = AnnotationUtils.getAnnotation(view, DefaultTab.class); if (defaultTabAnnotation != null) { if (defaultTabAnnotation.metrics().length == 0) { isDefaultTab = true; defaultForMetrics = new String[0]; - } else { isDefaultTab = false; defaultForMetrics = defaultTabAnnotation.metrics(); @@ -155,35 +165,35 @@ public class ViewProxy implements Comparable { } } - private void initResourceLanguage(final V view) { + private void initResourceLanguage(V view) { ResourceLanguage languageAnnotation = AnnotationUtils.getAnnotation(view, ResourceLanguage.class); if (languageAnnotation != null) { resourceLanguages = languageAnnotation.value(); } } - private void initResourceQualifier(final V view) { + private void initResourceQualifier(V view) { ResourceQualifier qualifierAnnotation = AnnotationUtils.getAnnotation(view, ResourceQualifier.class); if (qualifierAnnotation != null) { resourceQualifiers = qualifierAnnotation.value(); } } - private void initResourceScope(final V view) { + private void initResourceScope(V view) { ResourceScope scopeAnnotation = AnnotationUtils.getAnnotation(view, ResourceScope.class); if (scopeAnnotation != null) { resourceScopes = scopeAnnotation.value(); } } - private void initSections(final V view) { + private void initSections(V view) { NavigationSection sectionAnnotation = AnnotationUtils.getAnnotation(view, NavigationSection.class); if (sectionAnnotation != null) { sections = sectionAnnotation.value(); } } - private void initUserRoles(final V view) { + private void initUserRoles(V view) { UserRole userRoleAnnotation = AnnotationUtils.getAnnotation(view, UserRole.class); if (userRoleAnnotation != null) { userRoles = userRoleAnnotation.value(); @@ -200,7 +210,7 @@ public class ViewProxy implements Comparable { public boolean isController() { String id = view.getId(); - return id !=null && id.length()>0 && id.charAt(0)=='/'; + return id != null && !id.isEmpty() && id.charAt(0) == '/'; } public String getTitle() { @@ -215,6 +225,10 @@ public class ViewProxy implements Comparable { return widgetPropertiesByKey.values(); } + public SetMultimap getWidgetPropertiesBySet() { + return ImmutableSetMultimap.copyOf(widgetPropertiesBySet); + } + public WidgetProperty getWidgetProperty(String propertyKey) { return widgetPropertiesByKey.get(propertyKey); } @@ -254,23 +268,24 @@ public class ViewProxy implements Comparable { public boolean supportsMetric(String metricKey) { return ArrayUtils.contains(defaultForMetrics, metricKey); } - + public boolean acceptsAvailableMeasures(String[] availableMeasures) { for (String mandatoryMeasure : mandatoryMeasures) { if (!ArrayUtils.contains(availableMeasures, mandatoryMeasure)) { return false; } } + if (needOneOfMeasures.length == 0) { return true; - } else { - for (String neededMeasure : needOneOfMeasures) { - if (ArrayUtils.contains(availableMeasures, neededMeasure)) { - return true; - } + } + + for (String neededMeasure : needOneOfMeasures) { + if (ArrayUtils.contains(availableMeasures, neededMeasure)) { + return true; } - return false; } + return false; } public boolean isWidget() { @@ -294,13 +309,20 @@ public class ViewProxy implements Comparable { } public boolean hasRequiredProperties() { - boolean requires = false; for (WidgetProperty property : getWidgetProperties()) { if (!property.optional() && StringUtils.isEmpty(property.defaultValue())) { - requires = true; + return true; } } - return requires; + return false; + } + + public boolean validate(WidgetProperty property, String value) { + try { + return property.validation().newInstance().validate(value); + } catch (Exception e) { + } + return true; } @Override @@ -336,7 +358,6 @@ public class ViewProxy implements Comparable { .append("languages", resourceLanguages) .append("metrics", defaultForMetrics) .toString(); - } public int compareTo(ViewProxy other) { @@ -344,6 +365,5 @@ public class ViewProxy implements Comparable { .append(getTitle(), other.getTitle()) .append(getId(), other.getId()) .toComparison(); - } } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb index 79225aa2e72..b14797cfcc3 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/dashboard/_widget_properties.html.erb @@ -13,16 +13,29 @@ <% end %> - <% widget.java_definition.getWidgetProperties().sort { |w1, w2| natural_comparison(w1.key, w2.key) }.each do |property_def| %> - - <%= property_def.key() -%><%= " *" unless property_def.optional() -%> - - <%= property_value_field(property_def, widget.property_text_value(property_def.key())) -%> -
- <%= message("widget." + widget.key + ".param." + property_def.key(), :default => property_def.description()) -%> -
- - + <% widget.java_definition.getWidgetPropertiesBySet().asMap().sort { |(s1,p1), (s2,p2)| natural_comparison(s1.key, s2.key) }.each do |set,properties| %> + <% if widget.java_definition.getWidgetPropertiesBySet().asMap().size > 1 %> + <% if set.key().empty? %> + +

other

+ + <% else %> + +

<%= set.key() -%>

+ + <% end %> + <% end %> + <% properties.sort { |w1, w2| natural_comparison(w1.key, w2.key) }.each do |property_def| %> + + <%= property_def.key() -%><%= " *" unless property_def.optional() -%> + + <%= property_value_field(property_def, widget.property_text_value(property_def.key())) -%> +
+ <%= message("widget." + widget.key + ".param." + property_def.key(), :default => property_def.description()) -%> +
+ + + <% end %> <% end %> diff --git a/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java b/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java index e3260630791..725592eb4ab 100644 --- a/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java +++ b/sonar-server/src/test/java/org/sonar/server/ui/ViewProxyTest.java @@ -19,6 +19,8 @@ */ package org.sonar.server.ui; +import org.sonar.api.web.WidgetPropertySet; + import org.junit.rules.ExpectedException; import org.junit.Rule; @@ -139,6 +141,15 @@ public class ViewProxyTest { assertThat(proxy.getWidgetProperties()).hasSize(2); } + @Test + public void should_support_property_sets() { + ViewProxy proxy = new ViewProxy(new EditableWidgetWithSets()); + + assertThat(proxy.getWidgetProperties()).hasSize(4); + assertThat(proxy.getWidgetPropertiesBySet().keySet()).hasSize(3); + assertThat(proxy.getWidgetPropertiesBySet().values()).hasSize(4); + } + @Test public void widget_should_not_be_global_by_default() { ViewProxy proxy = new ViewProxy(new EditableWidget()); @@ -158,7 +169,7 @@ public class ViewProxyTest { exception.expect(IllegalArgumentException.class); exception.expectMessage("INVALID"); exception.expectMessage("WidgetWithInvalidScope"); - + new ViewProxy(new WidgetWithInvalidScope()); } @@ -256,7 +267,6 @@ class FakeView implements View { @WidgetProperty(key = "bar", defaultValue = "30", type = WidgetPropertyType.INTEGER) }) class EditableWidget implements Widget { - public String getId() { return "w1"; } @@ -266,6 +276,29 @@ class EditableWidget implements Widget { } } +@WidgetProperties(sets = { + @WidgetPropertySet(key = "set1", + value = { + @WidgetProperty(key = "foo", optional = false), + @WidgetProperty(key = "bar", optional = false), + }), + @WidgetPropertySet(key = "set2", + value = { + @WidgetProperty(key = "qix", optional = false), + })}, + value = { + @WidgetProperty(key = "fizz", optional = false) + }) +class EditableWidgetWithSets implements Widget { + public String getId() { + return "w3"; + } + + public String getTitle() { + return "W3"; + } +} + @WidgetScope("GLOBAL") class GlobalWidget implements Widget { public String getId() { @@ -293,7 +326,6 @@ class WidgetWithInvalidScope implements Widget { @WidgetProperty(key = "bar") }) class WidgetWithOptionalProperties implements Widget { - public String getId() { return "w2"; }