From f6a1da456075b5307537c9f736eecb656c5d3a2d Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 16 Mar 2012 11:46:48 +0100 Subject: SONAR-1378 validate property types + update type of core properties --- .../sonar/plugins/checkstyle/CheckstylePlugin.java | 3 +- .../java/org/sonar/plugins/core/CorePlugin.java | 342 ++++++++++----------- .../main/java/org/sonar/plugins/cpd/CpdPlugin.java | 16 +- .../sonar/plugins/dbcleaner/DbCleanerPlugin.java | 7 +- .../sonar/plugins/dbcleaner/DefaultPurgeTask.java | 3 +- .../org/sonar/plugins/findbugs/FindbugsPlugin.java | 3 +- .../main/resources/org/sonar/l10n/core.properties | 6 + .../java/org/sonar/plugins/squid/SquidPlugin.java | 10 +- .../src/main/java/org/sonar/api/Property.java | 9 +- .../src/main/java/org/sonar/api/PropertyType.java | 24 ++ .../main/java/org/sonar/api/config/AesCipher.java | 3 +- .../org/sonar/api/config/PropertyDefinition.java | 158 ++++++++++ .../org/sonar/api/config/PropertyDefinitions.java | 29 +- .../java/org/sonar/api/config/package-info.java | 23 ++ .../java/org/sonar/api/rules/RulesCategory.java | 7 - .../sonar/api/config/PropertyDefinitionTest.java | 153 +++++++++ .../sonar/api/config/PropertyDefinitionsTest.java | 16 +- .../java/org/sonar/api/config/SettingsTest.java | 28 +- .../sonar/api/platform/ComponentContainerTest.java | 8 +- .../sonar/server/plugins/UpdateCenterClient.java | 3 +- .../WEB-INF/app/controllers/project_controller.rb | 10 +- .../WEB-INF/app/controllers/settings_controller.rb | 52 ++-- .../src/main/webapp/WEB-INF/app/models/property.rb | 15 + .../WEB-INF/app/views/project/settings.html.erb | 2 +- .../WEB-INF/app/views/settings/_plugins.html.erb | 128 -------- .../app/views/settings/_properties.html.erb | 85 +++++ .../WEB-INF/app/views/settings/_settings.html.erb | 69 +++++ .../WEB-INF/app/views/settings/_sidebar.html.erb | 0 .../settings/_type_SINGLE_SELECT_LIST.html.erb | 2 +- .../WEB-INF/app/views/settings/index.html.erb | 2 +- 30 files changed, 803 insertions(+), 413 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/PropertyType.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/config/package-info.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java delete mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/settings/_plugins.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb create mode 100644 sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb diff --git a/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstylePlugin.java b/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstylePlugin.java index 515b8d15e04..9bba008989b 100644 --- a/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstylePlugin.java +++ b/plugins/sonar-checkstyle-plugin/src/main/java/org/sonar/plugins/checkstyle/CheckstylePlugin.java @@ -21,6 +21,7 @@ package org.sonar.plugins.checkstyle; import org.sonar.api.Properties; import org.sonar.api.Property; +import org.sonar.api.PropertyType; import org.sonar.api.SonarPlugin; import java.util.Arrays; @@ -35,7 +36,7 @@ import java.util.List; + " See Checkstyle configuration page to get more information on those filters.", project = false, global = true, - type = Property.Type.TEXT)}) + type = PropertyType.TEXT)}) public final class CheckstylePlugin extends SonarPlugin { public List getExtensions() { 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 b0d273c9fe6..71c09d25209 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 @@ -20,11 +20,9 @@ package org.sonar.plugins.core; import com.google.common.collect.Lists; -import org.sonar.api.CoreProperties; -import org.sonar.api.Properties; -import org.sonar.api.Property; -import org.sonar.api.SonarPlugin; +import org.sonar.api.*; import org.sonar.api.checks.NoSonarFilter; +import org.sonar.api.PropertyType; import org.sonar.api.resources.Java; import org.sonar.plugins.core.batch.ExcludedResourceFilter; import org.sonar.plugins.core.batch.IndexProjectPostJob; @@ -50,177 +48,177 @@ import org.sonar.plugins.core.widgets.reviews.*; import java.util.List; @Properties({ - @Property( - key = CoreProperties.SERVER_BASE_URL, - defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE, - name = "Server base URL", - description = "HTTP URL of this Sonar server, such as http://yourhost.yourdomain/sonar. This value is used i.e. to create links in emails.", - project = false, - global = true, - category = CoreProperties.CATEGORY_GENERAL), - @Property( - key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY, - defaultValue = "cobertura", - name = "Code coverage plugin", - description = "Key of the code coverage plugin to use.", - project = true, - global = true, - category = CoreProperties.CATEGORY_CODE_COVERAGE), - @Property( - key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY, - defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE, - name = "Import sources", - description = "Set to false if sources should not be displayed, e.g. for security reasons.", - project = true, - module = true, - global = true, - category = CoreProperties.CATEGORY_SECURITY, - type = Property.Type.BOOLEAN), - @Property( - key = CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY, - defaultValue = "" + CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE, - name = "Tendency period", - description = TendencyDecorator.PROP_DAYS_DESCRIPTION, - project = false, - global = true, - category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS, - type = Property.Type.INTEGER), - @Property( - key = CoreProperties.SKIP_TENDENCIES_PROPERTY, - defaultValue = "" + CoreProperties.SKIP_TENDENCIES_DEFAULT_VALUE, - name = "Skip tendencies", - description = "Skip calculation of measure tendencies", - project = true, - module = false, - global = true, - category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS, - type = Property.Type.BOOLEAN), - @Property( - key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY, - name = "Exclude modules", - description = "Maven artifact ids of modules to exclude (comma-separated).", - project = true, - global = false, - category = CoreProperties.CATEGORY_GENERAL), - @Property( - key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, - defaultValue = "" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE, - name = "Force user authentication", - description = "Forcing user authentication stops un-logged users to access Sonar.", - project = false, - global = true, - category = CoreProperties.CATEGORY_SECURITY, - type = Property.Type.BOOLEAN), - @Property( - key = CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY, - defaultValue = "" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE, - name = "Allow users to sign up online", - description = "Users can sign up online.", - project = false, - global = true, - category = CoreProperties.CATEGORY_SECURITY, - type = Property.Type.BOOLEAN), - @Property( - key = CoreProperties.CORE_DEFAULT_GROUP, - defaultValue = CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE, - name = "Default user group", - description = "Any new users will automatically join this group.", - project = false, - global = true, - category = CoreProperties.CATEGORY_SECURITY), - @Property( - key = CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY, - defaultValue = "en", - name = "Locale used for violation messages", - description = "Locale to be used when generating violation messages. It's up to each rule engine to support this global internationalization property", - project = true, - global = true, - category = CoreProperties.CATEGORY_L10N), - @Property( - key = "sonar.timemachine.period1", - name = "Period 1", - description = "Period used to compare measures and track new violations. Values are : " + - "Changing this property only take effect after subsequent project inspections.", - project = false, - global = true, - defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1, - category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), - @Property( - key = "sonar.timemachine.period2", - name = "Period 2", - description = "See the property 'Period 1'", - project = false, - global = true, - defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2, - category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), - @Property( - key = "sonar.timemachine.period3", - name = "Period 3", - description = "See the property 'Period 1'", - project = false, - global = true, - defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3, - category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), - @Property( - key = "sonar.timemachine.period4", - name = "Period 4", - description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : " + - "" + - "Changing this property only take effect after subsequent project inspection.", - project = true, - global = false, - defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4, - category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), - @Property( - key = "sonar.timemachine.period5", - name = "Period 5", - description = "See the property 'Period 4'", - project = true, - global = false, - defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5, - category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), + @Property( + key = CoreProperties.SERVER_BASE_URL, + defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE, + name = "Server base URL", + description = "HTTP URL of this Sonar server, such as http://yourhost.yourdomain/sonar. This value is used i.e. to create links in emails.", + project = false, + global = true, + category = CoreProperties.CATEGORY_GENERAL), + @Property( + key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY, + defaultValue = "cobertura", + name = "Code coverage plugin", + description = "Key of the code coverage plugin to use.", + project = true, + global = true, + category = CoreProperties.CATEGORY_CODE_COVERAGE), + @Property( + key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY, + defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE, + name = "Import sources", + description = "Set to false if sources should not be displayed, e.g. for security reasons.", + project = true, + module = true, + global = true, + category = CoreProperties.CATEGORY_SECURITY, + type = PropertyType.BOOLEAN), + @Property( + key = CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY, + defaultValue = "" + CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE, + name = "Tendency period", + description = TendencyDecorator.PROP_DAYS_DESCRIPTION, + project = false, + global = true, + category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS, + type = PropertyType.INTEGER), + @Property( + key = CoreProperties.SKIP_TENDENCIES_PROPERTY, + defaultValue = "" + CoreProperties.SKIP_TENDENCIES_DEFAULT_VALUE, + name = "Skip tendencies", + description = "Skip calculation of measure tendencies", + project = true, + module = false, + global = true, + category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS, + type = PropertyType.BOOLEAN), + @Property( + key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY, + name = "Exclude modules", + description = "Maven artifact ids of modules to exclude (comma-separated).", + project = true, + global = false, + category = CoreProperties.CATEGORY_GENERAL), + @Property( + key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, + defaultValue = "" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE, + name = "Force user authentication", + description = "Forcing user authentication stops un-logged users to access Sonar.", + project = false, + global = true, + category = CoreProperties.CATEGORY_SECURITY, + type = PropertyType.BOOLEAN), + @Property( + key = CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY, + defaultValue = "" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE, + name = "Allow users to sign up online", + description = "Users can sign up online.", + project = false, + global = true, + category = CoreProperties.CATEGORY_SECURITY, + type = PropertyType.BOOLEAN), + @Property( + key = CoreProperties.CORE_DEFAULT_GROUP, + defaultValue = CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE, + name = "Default user group", + description = "Any new users will automatically join this group.", + project = false, + global = true, + category = CoreProperties.CATEGORY_SECURITY), + @Property( + key = CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY, + defaultValue = "en", + name = "Locale used for violation messages", + description = "Locale to be used when generating violation messages. It's up to each rule engine to support this global internationalization property", + project = true, + global = true, + category = CoreProperties.CATEGORY_L10N), + @Property( + key = "sonar.timemachine.period1", + name = "Period 1", + description = "Period used to compare measures and track new violations. Values are : " + + "Changing this property only take effect after subsequent project inspections.", + project = false, + global = true, + defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1, + category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), + @Property( + key = "sonar.timemachine.period2", + name = "Period 2", + description = "See the property 'Period 1'", + project = false, + global = true, + defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2, + category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), + @Property( + key = "sonar.timemachine.period3", + name = "Period 3", + description = "See the property 'Period 1'", + project = false, + global = true, + defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3, + category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), + @Property( + key = "sonar.timemachine.period4", + name = "Period 4", + description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : " + + "" + + "Changing this property only take effect after subsequent project inspection.", + project = true, + global = false, + defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4, + category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), + @Property( + key = "sonar.timemachine.period5", + name = "Period 5", + description = "See the property 'Period 4'", + project = true, + global = false, + defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5, + category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS), - // SERVER-SIDE TECHNICAL PROPERTIES + // SERVER-SIDE TECHNICAL PROPERTIES - @Property( - key = "sonar.security.realm", - name = "Security Realm", - project = false, - global = false - ), - @Property( - key = "sonar.security.savePassword", - name = "Save external password", - project = false, - global = false - ), - @Property( - key = "sonar.authenticator.downcase", - name = "Downcase login", - description = "Downcase login during user authentication, typically for Active Directory", - project = false, - global = false, - defaultValue = "false", - type = Property.Type.BOOLEAN), - @Property( - key = CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS, - name = "Create user accounts", - description = "Create accounts when authenticating users via an external system", - project = false, - global = false, - defaultValue = "true", - type = Property.Type.BOOLEAN), - @Property( - key = CoreProperties.CORE_AUTHENTICATOR_IGNORE_STARTUP_FAILURE, - name = "Ignore failures during authenticator startup", - defaultValue = "false", - project = false, - global = false, - type = Property.Type.BOOLEAN) + @Property( + key = "sonar.security.realm", + name = "Security Realm", + project = false, + global = false + ), + @Property( + key = "sonar.security.savePassword", + name = "Save external password", + project = false, + global = false + ), + @Property( + key = "sonar.authenticator.downcase", + name = "Downcase login", + description = "Downcase login during user authentication, typically for Active Directory", + project = false, + global = false, + defaultValue = "false", + type = PropertyType.BOOLEAN), + @Property( + key = CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS, + name = "Create user accounts", + description = "Create accounts when authenticating users via an external system", + project = false, + global = false, + defaultValue = "true", + type = PropertyType.BOOLEAN), + @Property( + key = CoreProperties.CORE_AUTHENTICATOR_IGNORE_STARTUP_FAILURE, + name = "Ignore failures during authenticator startup", + defaultValue = "false", + project = false, + global = false, + type = PropertyType.BOOLEAN) }) public final class CorePlugin extends SonarPlugin { diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java index ddefdb4fa4c..2708f843d17 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java @@ -19,10 +19,8 @@ */ package org.sonar.plugins.cpd; -import org.sonar.api.CoreProperties; -import org.sonar.api.Properties; -import org.sonar.api.Property; -import org.sonar.api.SonarPlugin; +import org.sonar.api.*; +import org.sonar.api.PropertyType; import org.sonar.plugins.cpd.decorators.DuplicationDensityDecorator; import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator; import org.sonar.plugins.cpd.index.IndexFactory; @@ -43,7 +41,7 @@ import java.util.List; module = true, global = true, category = CoreProperties.CATEGORY_DUPLICATIONS, - type = Property.Type.SINGLE_SELECT_LIST, + type = PropertyType.SINGLE_SELECT_LIST, options = {"sonar", "pmd"}), @Property( key = CoreProperties.CPD_CROSS_RPOJECT, @@ -56,7 +54,7 @@ import java.util.List; module = true, global = true, category = CoreProperties.CATEGORY_DUPLICATIONS, - type = Property.Type.BOOLEAN), + type = PropertyType.BOOLEAN), @Property( key = CoreProperties.CPD_MINIMUM_TOKENS_PROPERTY, defaultValue = CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE + "", @@ -67,7 +65,7 @@ import java.util.List; module = true, global = true, category = CoreProperties.CATEGORY_DUPLICATIONS, - type = Property.Type.INTEGER), + type = PropertyType.INTEGER), @Property( key = CoreProperties.CPD_IGNORE_LITERALS_PROPERTY, defaultValue = CoreProperties.CPD_IGNORE_LITERALS_DEFAULT_VALUE + "", @@ -79,7 +77,7 @@ import java.util.List; module = true, global = true, category = CoreProperties.CATEGORY_DUPLICATIONS, - type = Property.Type.BOOLEAN), + type = PropertyType.BOOLEAN), @Property( key = CoreProperties.CPD_IGNORE_IDENTIFIERS_PROPERTY, defaultValue = CoreProperties.CPD_IGNORE_IDENTIFIERS_DEFAULT_VALUE + "", @@ -90,7 +88,7 @@ import java.util.List; module = true, global = true, category = CoreProperties.CATEGORY_DUPLICATIONS, - type = Property.Type.BOOLEAN) + type = PropertyType.BOOLEAN) }) public final class CpdPlugin extends SonarPlugin { diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java index 15a74fa8768..4507771468f 100644 --- a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DbCleanerPlugin.java @@ -21,6 +21,7 @@ package org.sonar.plugins.dbcleaner; import org.sonar.api.Properties; import org.sonar.api.Property; +import org.sonar.api.PropertyType; import org.sonar.api.SonarPlugin; import org.sonar.plugins.dbcleaner.api.DbCleanerConstants; import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner; @@ -35,20 +36,20 @@ import java.util.List; + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true, - type = Property.Type.INTEGER), + type = PropertyType.INTEGER), @Property(key = DbCleanerConstants.WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH, defaultValue = "52", name = "Number of weeks before starting to keep only one snapshot by month", description = "After this number of weeks, if there are several snapshots during the same month, " + "the DbCleaner keeps the first one and fully delete the other ones.", global = true, project = true, - type = Property.Type.INTEGER), + type = PropertyType.INTEGER), @Property(key = DbCleanerConstants.WEEKS_BEFORE_DELETING_ALL_SNAPSHOTS, defaultValue = "260", name = "Number of weeks before starting to delete all remaining snapshots", description = "After this number of weeks, all snapshots are fully deleted.", global = true, project = true, - type = Property.Type.INTEGER) + type = PropertyType.INTEGER) } ) public final class DbCleanerPlugin extends SonarPlugin { diff --git a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DefaultPurgeTask.java b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DefaultPurgeTask.java index 6fe63815b53..afee357487a 100644 --- a/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DefaultPurgeTask.java +++ b/plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/DefaultPurgeTask.java @@ -23,6 +23,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.Properties; import org.sonar.api.Property; +import org.sonar.api.PropertyType; import org.sonar.api.config.Settings; import org.sonar.api.resources.Scopes; import org.sonar.core.purge.PurgeDao; @@ -41,7 +42,7 @@ import org.sonar.plugins.dbcleaner.period.DefaultPeriodCleaner; global = true, project = true, module = false, - type = Property.Type.BOOLEAN) + type = PropertyType.BOOLEAN) }) public class DefaultPurgeTask implements PurgeTask { private static final Logger LOG = LoggerFactory.getLogger(DefaultPurgeTask.class); diff --git a/plugins/sonar-findbugs-plugin/src/main/java/org/sonar/plugins/findbugs/FindbugsPlugin.java b/plugins/sonar-findbugs-plugin/src/main/java/org/sonar/plugins/findbugs/FindbugsPlugin.java index 115feb801d2..070dfbee8bd 100644 --- a/plugins/sonar-findbugs-plugin/src/main/java/org/sonar/plugins/findbugs/FindbugsPlugin.java +++ b/plugins/sonar-findbugs-plugin/src/main/java/org/sonar/plugins/findbugs/FindbugsPlugin.java @@ -20,6 +20,7 @@ package org.sonar.plugins.findbugs; import org.sonar.api.*; +import org.sonar.api.PropertyType; import java.util.ArrayList; import java.util.List; @@ -43,7 +44,7 @@ import java.util.List; project = true, module = true, global = true, - type = Property.Type.INTEGER), + type = PropertyType.INTEGER), @Property( key = FindbugsConstants.EXCLUDES_FILTERS_PROPERTY, name = "Excludes Filters", diff --git a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties index 7ff09ae2a5e..2f3e06c1be6 100644 --- a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -549,6 +549,12 @@ property.category.duplications=Duplications property.category.localization=Localization property.category.server_id=Server ID +property.error.notBoolean=Valid options are "true" and "false" +property.error.notInteger=Only digits are allowed +property.error.notFloat=Not a floating point number +property.error.notInOptions=Not a valid option + + #------------------------------------------------------------------------------ # diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java index 6e06dd8fde6..7932d233545 100644 --- a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java +++ b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java @@ -19,10 +19,8 @@ */ package org.sonar.plugins.squid; -import org.sonar.api.CoreProperties; -import org.sonar.api.Properties; -import org.sonar.api.Property; -import org.sonar.api.SonarPlugin; +import org.sonar.api.*; +import org.sonar.api.PropertyType; import org.sonar.plugins.squid.decorators.*; import java.util.Arrays; @@ -37,7 +35,7 @@ import java.util.List; project = true, global = true, category = CoreProperties.CATEGORY_JAVA, - type = Property.Type.BOOLEAN), + type = PropertyType.BOOLEAN), @Property(key = SquidPluginProperties.FIELDS_TO_EXCLUDE_FROM_LCOM4_COMPUTATION, defaultValue = SquidPluginProperties.FIELDS_TO_EXCLUDE_FROM_LCOM4_COMPUTATION_DEFAULT_VALUE, name = "List of fields to exclude from LCOM4 computation", @@ -55,7 +53,7 @@ import java.util.List; project = true, global = true, category = CoreProperties.CATEGORY_JAVA, - type = Property.Type.BOOLEAN) + type = PropertyType.BOOLEAN) }) public final class SquidPlugin extends SonarPlugin { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Property.java b/sonar-plugin-api/src/main/java/org/sonar/api/Property.java index b48b0f5ec5a..c7820f15d3d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/Property.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/Property.java @@ -45,13 +45,6 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) public @interface Property { - /** - * @since 2.15 - */ - public static enum Type { - STRING, TEXT, PASSWORD, BOOLEAN, INTEGER, FLOAT, SINGLE_SELECT_LIST - } - /** * Unique key within all plugins. It's recommended to prefix the key by 'sonar.' and the plugin name. Examples : * 'sonar.cobertura.reportPath' and 'sonar.cpd.minimumTokens'. @@ -87,7 +80,7 @@ public @interface Property { /** * @since 2.15 */ - Type type() default Type.STRING; + PropertyType type() default PropertyType.STRING; /** * Options for *_LIST types diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/PropertyType.java b/sonar-plugin-api/src/main/java/org/sonar/api/PropertyType.java new file mode 100644 index 00000000000..356e4c1a65c --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/PropertyType.java @@ -0,0 +1,24 @@ +/* + * 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; + +public enum PropertyType { + STRING, TEXT, PASSWORD, BOOLEAN, INTEGER, FLOAT, SINGLE_SELECT_LIST +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/AesCipher.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/AesCipher.java index 1b5ee8511ef..4413b6353bf 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/AesCipher.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/AesCipher.java @@ -27,6 +27,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; +import javax.annotation.Nullable; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -92,7 +93,7 @@ final class AesCipher extends Cipher { } @VisibleForTesting - Key loadSecretFileFromFile(String path) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, InvalidKeyException { + Key loadSecretFileFromFile(@Nullable String path) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, InvalidKeyException { if (StringUtils.isBlank(path)) { throw new IllegalStateException("Secret key not found. Please set the property " + CoreProperties.ENCRYPTION_SECRET_KEY_FILE); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java new file mode 100644 index 00000000000..ef98057d6b5 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinition.java @@ -0,0 +1,158 @@ +/* + * 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.config; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.math.NumberUtils; +import org.sonar.api.Property; +import org.sonar.api.PropertyType; + +import javax.annotation.Nullable; + +/** + * @since 2.15 + */ +public final class PropertyDefinition { + + public static final class Result { + private static final Result SUCCESS = new Result(null); + + private String errorKey = null; + + private static Result newError(String key) { + return new Result(key); + } + + private Result(String errorKey) { + this.errorKey = errorKey; + } + + public boolean isValid() { + return StringUtils.isBlank(errorKey); + } + + public @Nullable String getErrorKey() { + return errorKey; + } + } + + private String key; + private String defaultValue; + private String name; + private PropertyType type = PropertyType.STRING; + private String[] options; + private String description; + private String category; + private boolean onProject = false; + private boolean onModule = false; + private boolean isGlobal = true; + + private PropertyDefinition(Property annotation) { + this.key = annotation.key(); + this.name = annotation.name(); + this.defaultValue = annotation.defaultValue(); + this.description = annotation.description(); + this.isGlobal = annotation.global(); + this.onProject = annotation.project(); + this.onModule = annotation.module(); + this.category = annotation.category(); + this.type = annotation.type(); + this.options = annotation.options(); + } + + @VisibleForTesting + PropertyDefinition(PropertyType type, String[] options) { + this.type = type; + this.options = options; + } + + public static PropertyDefinition create(Property annotation) { + return new PropertyDefinition(annotation); + } + + public Result validate(@Nullable String value) { + // TODO REFACTORING REQUIRED HERE + Result result = Result.SUCCESS; + if (StringUtils.isNotBlank(value)) { + if (type == PropertyType.BOOLEAN) { + if (!StringUtils.equalsIgnoreCase(value, "true") && !StringUtils.equalsIgnoreCase(value, "false")) { + result = Result.newError("notBoolean"); + } + } else if (type == PropertyType.INTEGER) { + if (!NumberUtils.isDigits(value)) { + result = Result.newError("notInteger"); + } + } else if (type == PropertyType.FLOAT) { + try { + Double.parseDouble(value); + } catch (NumberFormatException e) { + result = Result.newError("notFloat"); + } + } else if (type == PropertyType.SINGLE_SELECT_LIST) { + if (!ArrayUtils.contains(options, value)) { + result = Result.newError("notInOptions"); + } + } + } + return result; + } + + public String getKey() { + return key; + } + + public String getDefaultValue() { + return defaultValue; + } + + public String getName() { + return name; + } + + public PropertyType getType() { + return type; + } + + public String[] getOptions() { + return options; + } + + public String getDescription() { + return description; + } + + public String getCategory() { + return category; + } + + public boolean isOnProject() { + return onProject; + } + + public boolean isOnModule() { + return onModule; + } + + public boolean isGlobal() { + return isGlobal; + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java index a090c1ecb88..13c9b374fa5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyDefinitions.java @@ -38,7 +38,7 @@ import java.util.Map; */ public final class PropertyDefinitions implements BatchComponent, ServerComponent { - private Map properties = Maps.newHashMap(); + private Map definitions = Maps.newHashMap(); private Map categories = Maps.newHashMap(); public PropertyDefinitions(Object... components) { @@ -76,30 +76,31 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen return this; } - PropertyDefinitions addProperty(Property property) { - return addProperty(property, ""); + private PropertyDefinitions addProperty(Property property, String defaultCategory) { + PropertyDefinition definition = PropertyDefinition.create(property); + return add(definition, defaultCategory); } - PropertyDefinitions addProperty(Property property, String defaultCategory) { - if (!properties.containsKey(property.key())) { - properties.put(property.key(), property); - categories.put(property.key(), StringUtils.defaultIfBlank(property.category(), defaultCategory)); + private PropertyDefinitions add(PropertyDefinition definition, String defaultCategory) { + if (!definitions.containsKey(definition.getKey())) { + definitions.put(definition.getKey(), definition); + categories.put(definition.getKey(), StringUtils.defaultIfBlank(definition.getCategory(), defaultCategory)); } return this; } - public Property getProperty(String key) { - return properties.get(key); + public PropertyDefinition get(String key) { + return definitions.get(key); } - public Collection getProperties() { - return properties.values(); + public Collection getAll() { + return definitions.values(); } public String getDefaultValue(String key) { - Property prop = getProperty(key); - if (prop != null) { - return StringUtils.defaultIfEmpty(prop.defaultValue(), null); + PropertyDefinition def = get(key); + if (def != null) { + return StringUtils.defaultIfEmpty(def.getDefaultValue(), null); } return null; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/package-info.java new file mode 100644 index 00000000000..d3ae915cdb2 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/package-info.java @@ -0,0 +1,23 @@ +/* + * 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 + */ +@ParametersAreNonnullByDefault +package org.sonar.api.config; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java index 3e5c1acdd5f..c860399da1f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java @@ -22,15 +22,8 @@ package org.sonar.api.rules; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; -import org.hibernate.annotations.Immutable; import org.sonar.api.database.BaseIdentifiable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Table; - /** * @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007 */ diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java new file mode 100644 index 00000000000..7ccbda1d245 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionTest.java @@ -0,0 +1,153 @@ +/* + * 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.config; + +import org.hamcrest.core.Is; +import org.junit.Test; +import org.sonar.api.Properties; +import org.sonar.api.Property; +import org.sonar.api.PropertyType; +import org.sonar.api.utils.AnnotationUtils; + +import java.util.Arrays; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.junit.matchers.JUnitMatchers.hasItems; + +public class PropertyDefinitionTest { + + @Test + public void createFromAnnotation() { + Properties props = AnnotationUtils.getClassAnnotation(Init.class, Properties.class); + Property prop = props.value()[0]; + + PropertyDefinition def = PropertyDefinition.create(prop); + + assertThat(def.getKey(), Is.is("hello")); + assertThat(def.getName(), Is.is("Hello")); + assertThat(def.getDefaultValue(), Is.is("world")); + assertThat(def.getCategory(), Is.is("categ")); + assertThat(def.getOptions().length, Is.is(2)); + assertThat(Arrays.asList(def.getOptions()), hasItems("de", "en")); + assertThat(def.getDescription(), Is.is("desc")); + assertThat(def.getType(), Is.is(PropertyType.FLOAT)); + assertThat(def.isGlobal(), Is.is(false)); + assertThat(def.isOnProject(), Is.is(true)); + assertThat(def.isOnModule(), Is.is(true)); + } + + @Properties({ + @Property(key = "hello", name = "Hello", defaultValue = "world", description = "desc", + options = {"de", "en"}, category = "categ", type = PropertyType.FLOAT, global = false, project = true, module = true) + }) + static class Init { + } + + @Test + public void createFromAnnotation_default_values() { + Properties props = AnnotationUtils.getClassAnnotation(DefaultValues.class, Properties.class); + Property prop = props.value()[0]; + + PropertyDefinition def = PropertyDefinition.create(prop); + + assertThat(def.getKey(), Is.is("hello")); + assertThat(def.getName(), Is.is("Hello")); + assertThat(def.getDefaultValue(), Is.is("")); + assertThat(def.getCategory(), Is.is("")); + assertThat(def.getOptions().length, Is.is(0)); + assertThat(def.getDescription(), Is.is("")); + assertThat(def.getType(), Is.is(PropertyType.STRING)); + assertThat(def.isGlobal(), Is.is(true)); + assertThat(def.isOnProject(), Is.is(false)); + assertThat(def.isOnModule(), Is.is(false)); + } + + @Properties({ + @Property(key = "hello", name = "Hello") + }) + static class DefaultValues { + } + + @Test + public void validate_string() { + PropertyDefinition def = new PropertyDefinition(PropertyType.STRING, new String[0]); + + assertThat(def.validate(null).isValid(), is(true)); + assertThat(def.validate("").isValid(), is(true)); + assertThat(def.validate(" ").isValid(), is(true)); + assertThat(def.validate("foo").isValid(), is(true)); + } + + @Test + public void validate_boolean() { + PropertyDefinition def = new PropertyDefinition(PropertyType.BOOLEAN, new String[0]); + + assertThat(def.validate(null).isValid(), is(true)); + assertThat(def.validate("").isValid(), is(true)); + assertThat(def.validate(" ").isValid(), is(true)); + assertThat(def.validate("true").isValid(), is(true)); + assertThat(def.validate("false").isValid(), is(true)); + + assertThat(def.validate("foo").isValid(), is(false)); + assertThat(def.validate("foo").getErrorKey(), is("notBoolean")); + } + + @Test + public void validate_integer() { + PropertyDefinition def = new PropertyDefinition(PropertyType.INTEGER, new String[0]); + + assertThat(def.validate(null).isValid(), is(true)); + assertThat(def.validate("").isValid(), is(true)); + assertThat(def.validate(" ").isValid(), is(true)); + assertThat(def.validate("123456").isValid(), is(true)); + + assertThat(def.validate("foo").isValid(), is(false)); + assertThat(def.validate("foo").getErrorKey(), is("notInteger")); + } + + @Test + public void validate_float() { + PropertyDefinition def = new PropertyDefinition(PropertyType.FLOAT, new String[0]); + + assertThat(def.validate(null).isValid(), is(true)); + assertThat(def.validate("").isValid(), is(true)); + assertThat(def.validate(" ").isValid(), is(true)); + assertThat(def.validate("123456").isValid(), is(true)); + assertThat(def.validate("3.14").isValid(), is(true)); + + assertThat(def.validate("foo").isValid(), is(false)); + assertThat(def.validate("foo").getErrorKey(), is("notFloat")); + } + + @Test + public void validate_single_select_list() { + PropertyDefinition def = new PropertyDefinition(PropertyType.SINGLE_SELECT_LIST, new String[]{"de", "en"}); + + assertThat(def.validate(null).isValid(), is(true)); + assertThat(def.validate("").isValid(), is(true)); + assertThat(def.validate(" ").isValid(), is(true)); + assertThat(def.validate("de").isValid(), is(true)); + assertThat(def.validate("en").isValid(), is(true)); + + assertThat(def.validate("fr").isValid(), is(false)); + assertThat(def.validate("fr").getErrorKey(), is("notInOptions")); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java index a5d8a0b2add..1320d9e376d 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/PropertyDefinitionsTest.java @@ -43,16 +43,16 @@ public class PropertyDefinitionsTest { assertProperties(def); } - private void assertProperties(PropertyDefinitions def) { - assertThat(def.getProperty("foo").name(), is("Foo")); - assertThat(def.getProperty("one").name(), is("One")); - assertThat(def.getProperty("two").name(), is("Two")); - assertThat(def.getProperty("unknown"), nullValue()); + private void assertProperties(PropertyDefinitions definitions) { + assertThat(definitions.get("foo").getName(), is("Foo")); + assertThat(definitions.get("one").getName(), is("One")); + assertThat(definitions.get("two").getName(), is("Two")); + assertThat(definitions.get("unknown"), nullValue()); - assertThat(def.getDefaultValue("foo"), nullValue()); - assertThat(def.getDefaultValue("two"), is("2")); + assertThat(definitions.getDefaultValue("foo"), nullValue()); + assertThat(definitions.getDefaultValue("two"), is("2")); - assertThat(def.getProperties().size(), is(3)); + assertThat(definitions.getAll().size(), is(3)); } @Property(key = "foo", name = "Foo") diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java index 4e88f6c939d..72a36ece437 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/SettingsTest.java @@ -23,34 +23,32 @@ import com.google.common.collect.ImmutableMap; import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.Test; +import org.sonar.api.Properties; import org.sonar.api.Property; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class SettingsTest { private PropertyDefinitions definitions; + @Properties({ + @Property(key = "hello", name = "Hello", defaultValue = "world"), + @Property(key = "date", name = "Date", defaultValue = "2010-05-18"), + @Property(key = "boolean", name = "Boolean", defaultValue = "true"), + @Property(key = "falseboolean", name = "False Boolean", defaultValue = "false"), + @Property(key = "integer", name = "Integer", defaultValue = "12345"), + @Property(key = "array", name = "Array", defaultValue = "one,two,three") + }) + static class Init { + } + @Before public void initDefinitions() { definitions = new PropertyDefinitions(); - definitions.addProperty(newProperty("hello", "world")); - definitions.addProperty(newProperty("date", "2010-05-18")); - definitions.addProperty(newProperty("boolean", "true")); - definitions.addProperty(newProperty("falseboolean", "false")); - definitions.addProperty(newProperty("integer", "12345")); - definitions.addProperty(newProperty("array", "one,two,three")); - } - - private Property newProperty(String key, String defaultValue) { - Property prop = mock(Property.class); - when(prop.key()).thenReturn(key); - when(prop.defaultValue()).thenReturn(defaultValue); - return prop; + definitions.addComponent(Init.class); } @Test diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java index bfb16d5f7af..db6f03b1106 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/platform/ComponentContainerTest.java @@ -102,8 +102,8 @@ public class ComponentContainerTest { container.addSingleton(ComponentWithProperty.class); PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class); - assertThat(propertyDefinitions.getProperty("foo"), notNullValue()); - assertThat(propertyDefinitions.getProperty("foo").defaultValue(), is("bar")); + assertThat(propertyDefinitions.get("foo"), notNullValue()); + assertThat(propertyDefinitions.get("foo").getDefaultValue(), is("bar")); } @Test @@ -113,7 +113,7 @@ public class ComponentContainerTest { container.declareExtension(plugin, ComponentWithProperty.class); PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class); - assertThat(propertyDefinitions.getProperty("foo"), notNullValue()); + assertThat(propertyDefinitions.get("foo"), notNullValue()); assertThat(container.getComponentByType(ComponentWithProperty.class), nullValue()); } @@ -124,7 +124,7 @@ public class ComponentContainerTest { container.addExtension(plugin, ComponentWithProperty.class); PropertyDefinitions propertyDefinitions = container.getComponentByType(PropertyDefinitions.class); - assertThat(propertyDefinitions.getProperty("foo"), notNullValue()); + assertThat(propertyDefinitions.get("foo"), notNullValue()); assertThat(container.getComponentByType(ComponentWithProperty.class), notNullValue()); } diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java b/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java index 27d8df7e0a3..191c0e88cbd 100644 --- a/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java +++ b/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java @@ -23,6 +23,7 @@ import org.apache.commons.io.IOUtils; import org.slf4j.LoggerFactory; import org.sonar.api.Properties; import org.sonar.api.Property; +import org.sonar.api.PropertyType; import org.sonar.api.ServerComponent; import org.sonar.api.config.Settings; import org.sonar.api.utils.HttpDownloader; @@ -49,7 +50,7 @@ import java.util.Date; project = false, global = false, // hidden from UI category = "Update Center", - type = Property.Type.BOOLEAN), + type = PropertyType.BOOLEAN), @Property( key = UpdateCenterClient.URL_PROPERTY, defaultValue = "http://update.sonarsource.org/update-center.properties", diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb index c486dfd7d27..88ebe73790d 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb @@ -126,13 +126,13 @@ class ProjectController < ApplicationController end @category=params[:category] ||= 'general' - @properties_per_category={} + @definitions_per_category={} definitions = java_facade.getPropertyDefinitions() - properties = definitions.getProperties().select { |property| (@project.module? && property.module()) || (@project.project? && property.project()) } + properties = definitions.getAll().select { |property| (@project.module? && property.isOnModule()) || (@project.project? && property.isOnProject()) } properties.each do |property| - category = definitions.getCategory(property.key()) - @properties_per_category[category]||=[] - @properties_per_category[category]< :post, :only => ['update'], :redirect_to => {:action => :index} @@ -32,39 +32,39 @@ class SettingsController < ApplicationController end def update - project=nil + @project=nil if params[:resource_id] - project=Project.by_key(params[:resource_id]) - access_denied unless (project && is_admin?(project)) + @project=Project.by_key(params[:resource_id]) + access_denied unless (@project && is_admin?(@project)) else access_denied unless is_admin? end - load_properties(project) + load_properties(@project) + + @persisted_properties_per_key={} + if @category && @definitions_per_category[@category] + @definitions_per_category[@category].each do |property| + value=params[property.getKey()] - if @category && @properties_per_category[@category] - @properties_per_category[@category].each do |property| - value=params[property.key()] - persisted_property = Property.find(:first, :conditions => {:prop_key=> property.key(), :resource_id => (project ? project.id : nil), :user_id => nil}) + persisted_property = Property.find(:first, :conditions => {:prop_key=> property.key(), :resource_id => (@project ? @project.id : nil), :user_id => nil}) if persisted_property if value.empty? - Property.delete_all('prop_key' => property.key(), 'resource_id' => (project ? project.id : nil), 'user_id' => nil) + Property.delete_all('prop_key' => property.key(), 'resource_id' => (@project ? @project.id : nil), 'user_id' => nil) elsif persisted_property.text_value != value.to_s persisted_property.text_value = value.to_s - persisted_property.save! + persisted_property.save + @persisted_properties_per_key[persisted_property.key]=persisted_property end elsif !value.blank? - Property.create(:prop_key => property.key(), :text_value => value.to_s, :resource_id => (project ? project.id : nil)) + persisted_property=Property.create(:prop_key => property.key(), :text_value => value.to_s, :resource_id => (@project ? @project.id : nil)) + @persisted_properties_per_key[persisted_property.key]=persisted_property end end java_facade.reloadConfiguration() - flash[:notice] = 'Parameters updated' - end - if project - redirect_to :controller => 'project', :action => 'settings', :id => project.id, :category => @category - else - redirect_to :controller => 'settings', :action => 'index', :category => @category + params[:layout]='false' + render :partial => 'settings/properties' end end @@ -72,18 +72,18 @@ class SettingsController < ApplicationController def load_properties(project) @category=params[:category] - @properties_per_category={} + @definitions_per_category={} definitions = java_facade.getPropertyDefinitions() - definitions.getProperties().select {|property| - (project.nil? && property.global) || (project && project.module? && property.module()) || (project && project.project? && property.project()) - }.each do |property| - category = definitions.getCategory(property.key()) - @properties_per_category[category]||=[] - @properties_per_category[category]<Settings
- <%= render :partial => 'settings/plugins', :locals => {:project=>@project} %> + <%= render :partial => 'settings/settings', :locals => {:project=>@project} %>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_plugins.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_plugins.html.erb deleted file mode 100644 index 87a5a99b803..00000000000 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_plugins.html.erb +++ /dev/null @@ -1,128 +0,0 @@ - - -
- - - - - - -
- - - - - - - - <% - @properties_per_category.keys.sort_by { |category| message("property.category.#{category}", :default => category).upcase }.each do |category| - if !@properties_per_category[category].empty? || SettingsController::SPECIAL_CATEGORIES.include?(category) - %> - - - - <% end - end - %> - -
- Category -
<%= link_to message("property.category.#{category}", :default => category), :overwrite_params => {:category => category} -%>
-
-
- <% if @category && @properties_per_category[@category] - category_name = message("property.category.#{@category}", :default => @category) - if SettingsController::SPECIAL_CATEGORIES.include?(@category) - %> - <%= render :partial => 'special', :locals => {:url => url_for(:controller => "#{@category}_configuration")} -%> - <% - elsif !@properties_per_category[@category].empty? - %> - <% form_tag :controller => :settings, :action => :update do %> - <%= hidden_field_tag('category', @category) -%> - <% if @project %> - - <% end %> - - - - - - - - <% - if @properties_per_category[@category] - @properties_per_category[@category].each do |property| - value = Property.value(property.key(), (@project ? @project.id : nil), '') - - # for backward-compatibility with properties that do not define the type TEXT - property_type = value.include?("\n") ? 'TEXT' : property.type - %> - - - - <% end - end - %> - -
- <%= h(category_name) -%> -
-

- <%= message("property.#{property.key()}.name", :default => property.name()) -%> -
<%= property.key() -%> -

- <% - desc=message("property.#{property.key()}.description", :default => property.description()) - if desc.present? %> -

<%= desc -%>

- <% end %> -
<%= render :partial => "settings/type_#{property_type}", :locals => {:property => property, :value => value} -%>
-

- <% - default_prop_value = (@project ? Property.value(property.key(), nil, property.defaultValue()) : property.defaultValue()) - unless default_prop_value.blank? %> - Default : <%= h default_prop_value -%> - <% end %> -

-
- <% save_message=message('settings.save_category', :params => [category_name]) %> - <%= submit_tag(save_message, :disable_with => save_message, :id => 'save') -%> - <% end %> - <% end - end - %> -
-
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb new file mode 100644 index 00000000000..fc2fbd2ae09 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_properties.html.erb @@ -0,0 +1,85 @@ +<% if @category && @definitions_per_category[@category] + category_name = message("property.category.#{@category}", :default => @category) + if SettingsController::SPECIAL_CATEGORIES.include?(@category) +%> + <%= render :partial => 'special', :locals => {:url => url_for(:controller => "#{@category}_configuration")} -%> + <% + elsif !@definitions_per_category[@category].empty? + %> + <% form_remote_tag :url => {:controller => 'settings', :action => 'update', :category => @category, :resource_id => @project ? @project.id : nil}, + :method => :post, + :before => "$('submit_settings').hide();$('loading_settings').show()", + :update => 'properties' do -%> + + + + + + + + + <% + if @definitions_per_category[@category] + @definitions_per_category[@category].each do |property| + value = nil + + # set when form has been submitted but some errors have been raised + if @persisted_properties_per_key + p = @persisted_properties_per_key[property.key] + if p + value = p.text_value + end + end + + + # if fresh form or no error, get the current value + value = Property.value(property.getKey(), (@project ? @project.id : nil), '') unless value + + # for backward-compatibility with properties that do not define the type TEXT + property_type = value.include?("\n") ? 'TEXT' : property.getType() + %> + + + + <% end + end + %> + +
+ <%= h(category_name) -%> +
+

+ <%= message("property.#{property.key()}.name", :default => property.name()) -%> +
<%= property.getKey() -%> +

+ <% + desc=message("property.#{property.key()}.description", :default => property.description()) + if desc.present? %> +

<%= desc -%>

+ <% end %> +
<%= render :partial => "settings/type_#{property_type}", :locals => {:property => property, :value => value} -%>
+ <% + if p + p.errors.each_full do |error| + %> +
<%= message("property.error.#{error}") -%>
+ <% + end + end + %> +

+ <% + default_prop_value = (@project ? Property.value(property.key(), nil, property.defaultValue()) : property.defaultValue()) + unless default_prop_value.blank? %> + Default : <%= h default_prop_value -%> + <% end %> +

+
+
+ <%= submit_tag(message('settings.save_category', :params => [category_name]), :id => 'submit_settings') -%> + +
+ <% end %> + <% end + end + %> diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb new file mode 100644 index 00000000000..cbd85572a12 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_settings.html.erb @@ -0,0 +1,69 @@ + + +
+ + + + + + +
+ + + + + + + + <% + @definitions_per_category.keys.sort_by { |category| message("property.category.#{category}", :default => category).upcase }.each do |category| + if !@definitions_per_category[category].empty? || SettingsController::SPECIAL_CATEGORIES.include?(category) + %> + + + + <% end + end + %> + +
+ Category +
<%= link_to message("property.category.#{category}", :default => category), :overwrite_params => {:category => category} -%>
+
+
+
+ <%= render :partial => 'settings/properties' -%> +
+
+
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_sidebar.html.erb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb index a6f905a3038..a317e48ef38 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb index d0fc6625477..81fa7362a09 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/settings/index.html.erb @@ -1 +1 @@ -<%= render :partial => 'plugins', :locals => {:project=>nil} %> \ No newline at end of file +<%= render :partial => 'settings', :locals => {:project=>nil} %> \ No newline at end of file -- cgit v1.2.3