diff options
author | Julien Lancelot <julien.lancelot@gmail.com> | 2013-03-26 18:12:39 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@gmail.com> | 2013-03-26 18:13:15 +0100 |
commit | 4689f87a0ad833bb9522fbde1896b3ab5fcb52c8 (patch) | |
tree | fc8be79d3fa5d36b2c1ee0d2fed688bcaddc0607 /sonar-plugin-api/src/main/java/org/sonar/api/config | |
parent | 996c779bd2b0336bbb3d207d0fb3c5bcaab18fca (diff) | |
download | sonarqube-4689f87a0ad833bb9522fbde1896b3ab5fcb52c8.tar.gz sonarqube-4689f87a0ad833bb9522fbde1896b3ab5fcb52c8.zip |
SONAR-3891 Update PropertyDefinition in order to be used directly to define properties (with builder pattern)
Diffstat (limited to 'sonar-plugin-api/src/main/java/org/sonar/api/config')
4 files changed, 402 insertions, 148 deletions
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 index 083b598d4da..be4ee0527e2 100644 --- 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 @@ -19,121 +19,95 @@ */ package org.sonar.api.config; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; -import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; +import org.sonar.api.BatchComponent; import org.sonar.api.Property; import org.sonar.api.PropertyType; +import org.sonar.api.ServerComponent; +import org.sonar.api.resources.Qualifiers; import javax.annotation.Nullable; import java.util.List; +import static com.google.common.collect.Lists.newArrayList; + /** + * Property value can be set in different ways : + * <ul> + * <li>System property</li> + * <li>Maven command-line (-Dfoo=bar)</li> + * <li>Maven pom.xml (element <properties>)</li> + * <li>Maven settings.xml</li> + * <li>Sonar web interface</li> + * </ul> + * <p/> + * Value is accessible in batch extensions via the Configuration object of class <code>org.sonar.api.resources.Project</code> + * (see method <code>getConfiguration()</code>). + * <p/> + * <p><strong>Must be used in <code>org.sonar.api.Plugin</code> classes only.</strong></p> + * * @since 3.0 */ -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); - } - - @Nullable - private Result(@Nullable String errorKey) { - this.errorKey = errorKey; - } - - public boolean isValid() { - return StringUtils.isBlank(errorKey); - } - - @Nullable - public String getErrorKey() { - return errorKey; - } - } - - private final String key; - private final String defaultValue; - private final String name; - private final PropertyType type; - private final String[] options; - private final String description; - private final String category; - private final boolean onProject; - private final boolean onModule; - private final boolean isGlobal; - private final boolean multiValues; - private final String propertySetKey; - private final String deprecatedKey; - private final List<PropertyFieldDefinition> fields; - - 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 = fixType(annotation.key(), annotation.type()); - this.options = annotation.options(); - this.multiValues = annotation.multiValues(); - this.propertySetKey = annotation.propertySetKey(); - this.fields = ImmutableList.copyOf(PropertyFieldDefinition.create(annotation.fields())); - this.deprecatedKey = annotation.deprecatedKey(); - } - - private PropertyDefinition(String key, PropertyType type, String[] options) { - this.key = key; - this.name = null; - this.defaultValue = null; - this.description = null; - this.isGlobal = true; - this.onProject = false; - this.onModule = false; - this.category = null; - this.type = type; - this.options = options; - this.multiValues = false; - this.propertySetKey = null; - this.fields = null; - this.deprecatedKey = null; - } - - private static PropertyType fixType(String key, PropertyType type) { - // Auto-detect passwords and licenses for old versions of plugins that - // do not declare property types - if (type == PropertyType.STRING) { - if (StringUtils.endsWith(key, ".password.secured")) { - return PropertyType.PASSWORD; - } else if (StringUtils.endsWith(key, ".license.secured")) { - return PropertyType.LICENSE; - } - } - return type; +public final class PropertyDefinition implements BatchComponent, ServerComponent { + + private String key; + private String defaultValue; + private String name; + private PropertyType type; + private List<String> options; + private String description; + private String category; + private List<String> qualifiers; + private boolean global; + private boolean multiValues; + private String propertySetKey; + private String deprecatedKey; + private List<PropertyFieldDefinition> fields; + + private PropertyDefinition(Builder builder) { + this.key = builder.key; + this.name = builder.name; + this.description = builder.description; + this.defaultValue = builder.defaultValue; + this.category = builder.category; + this.global = builder.global; + this.type = builder.type; + this.options = builder.options; + this.multiValues = builder.multiValues; + this.propertySetKey = builder.propertySetKey; + this.fields = builder.fields; + this.deprecatedKey = builder.deprecatedKey; + this.qualifiers = builder.qualifiers; } - public static PropertyDefinition create(Property annotation) { - return new PropertyDefinition(annotation); + public static Builder build(String key) { + return new Builder(key); } - public static PropertyDefinition create(String key, PropertyType type, String[] options) { - return new PropertyDefinition(key, type, options); + static PropertyDefinition create(Property annotation) { + return PropertyDefinition.build(annotation.key()) + .name(annotation.name()) + .defaultValue(annotation.defaultValue()) + .description(annotation.description()) + .global(annotation.global()) + .project(annotation.project()) + .module(annotation.module()) + .category(annotation.category()) + .type(annotation.type()) + .options(annotation.options()) + .multiValues(annotation.multiValues()) + .propertySetKey(annotation.propertySetKey()) + .fields(PropertyFieldDefinition.create(annotation.fields())) + .deprecatedKey(annotation.deprecatedKey()) + .build(); } - public Result validate(@Nullable String value) { - return validate(type, value, options); - } - - static Result validate(PropertyType type, @Nullable String value, String[] options) { + public static Result validate(PropertyType type, @Nullable String value, List<String> options) { if (StringUtils.isNotBlank(value)) { if (type == PropertyType.BOOLEAN) { if (!StringUtils.equalsIgnoreCase(value, "true") && !StringUtils.equalsIgnoreCase(value, "false")) { @@ -150,7 +124,7 @@ public final class PropertyDefinition { return Result.newError("notFloat"); } } else if (type == PropertyType.SINGLE_SELECT_LIST) { - if (!ArrayUtils.contains(options, value)) { + if (!options.contains(value)) { return Result.newError("notInOptions"); } } @@ -158,71 +132,273 @@ public final class PropertyDefinition { return Result.SUCCESS; } - public String getKey() { + public Result validate(@Nullable String value) { + return validate(type, value, options); + } + + /** + * 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'. + */ + public String key() { return key; } - public String getDefaultValue() { + public String defaultValue() { return defaultValue; } - public String getName() { + public String name() { return name; } - public PropertyType getType() { + public PropertyType type() { return type; } - public String[] getOptions() { - return options.clone(); + /** + * Options for *_LIST types + * <p/> + * Options for property of type PropertyType.SINGLE_SELECT_LIST</code> + * For example {"property_1", "property_3", "property_3"}). + * <p/> + * Options for property of type PropertyType.METRIC</code>. + * If no option is specified, any metric will match. + * If options are specified, all must match for the metric to be displayed. + * Three types of filter are supported <code>key:REGEXP</code>, <code>domain:REGEXP</code> and <code>type:comma_separated__list_of_types</code>. + * For example <code>key:new_.*</code> will match any metric which key starts by <code>new_</code>. + * For example <code>type:INT,FLOAT</code> will match any metric of type <code>INT</code> or <code>FLOAT</code>. + * For example <code>type:NUMERIC</code> will match any metric of numerictype. + */ + public List<String> options() { + return options; } - public String getDescription() { + public String description() { return description; } - public String getCategory() { + public String category() { return category; } - public boolean isOnProject() { - return onProject; + /** + * Is the property displayed in project settings page ? + */ + public boolean project() { + return qualifiers.contains(Qualifiers.PROJECT); + } + + /** + * Is the property displayed in module settings page ? A module is a maven sub-project. + */ + public boolean module() { + return qualifiers.contains(Qualifiers.MODULE); } - public boolean isOnModule() { - return onModule; + /** + * Qualifiers that can display this property + * + * @since 3.6 + */ + public List<String> qualifiers() { + return qualifiers; } - public boolean isGlobal() { - return isGlobal; + /** + * Is the property displayed in global settings page ? + */ + public boolean global() { + return global; } /** * @since 3.3 */ - public boolean isMultiValues() { + public boolean multiValues() { return multiValues; } /** * @since 3.3 */ - public String getPropertySetKey() { + public String propertySetKey() { return propertySetKey; } /** * @since 3.3 */ - public List<PropertyFieldDefinition> getFields() { + public List<PropertyFieldDefinition> fields() { return fields; } /** * @since 3.4 */ - public String getDeprecatedKey() { + public String deprecatedKey() { return deprecatedKey; } + + public static final class Result { + private static final Result SUCCESS = new Result(null); + private String errorKey = null; + + @Nullable + private Result(@Nullable String errorKey) { + this.errorKey = errorKey; + } + + private static Result newError(String key) { + return new Result(key); + } + + public boolean isValid() { + return StringUtils.isBlank(errorKey); + } + + @Nullable + public String getErrorKey() { + return errorKey; + } + } + + public static class Builder { + private String key; + private String name; + private String description; + private String defaultValue; + private String category; + private List<String> qualifiers; + private boolean global; + private PropertyType type; + private List<String> options; + private boolean multiValues; + private String propertySetKey; + private List<PropertyFieldDefinition> fields; + private String deprecatedKey; + + private Builder(String key) { + this.key = key; + this.name = ""; + this.description = ""; + this.defaultValue = ""; + this.category = ""; + this.propertySetKey = ""; + this.deprecatedKey = ""; + this.global = true; + this.type = PropertyType.STRING; + this.qualifiers = newArrayList(); + this.options = newArrayList(); + this.fields = newArrayList(); + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder defaultValue(String defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public Builder category(String category) { + this.category = category; + return this; + } + + public Builder qualifiers(String... qualifiers) { + this.qualifiers.addAll(newArrayList(qualifiers)); + return this; + } + + public Builder project(boolean displayOnProject) { + if (displayOnProject) { + this.qualifiers.add(Qualifiers.PROJECT); + } else { + this.qualifiers.remove(Qualifiers.PROJECT); + } + return this; + } + + public Builder module(boolean displayOnModule) { + if (displayOnModule) { + this.qualifiers.add(Qualifiers.MODULE); + } else { + this.qualifiers.remove(Qualifiers.MODULE); + } + return this; + } + + public Builder global(boolean global) { + this.global = global; + return this; + } + + public Builder type(PropertyType type) { + this.type = type; + return this; + } + + public Builder options(String... options) { + this.options.addAll(ImmutableList.copyOf(options)); + return this; + } + + public Builder options(List<String> options) { + this.options.addAll(ImmutableList.copyOf(options)); + return this; + } + + public Builder multiValues(boolean multiValues) { + this.multiValues = multiValues; + return this; + } + + public Builder propertySetKey(String propertySetKey) { + this.propertySetKey = propertySetKey; + return this; + } + + public Builder fields(PropertyFieldDefinition... fields) { + this.fields.addAll(ImmutableList.copyOf(fields)); + return this; + } + + public Builder fields(List<PropertyFieldDefinition> fields) { + this.fields.addAll(ImmutableList.copyOf(fields)); + return this; + } + + public Builder deprecatedKey(String deprecatedKey) { + this.deprecatedKey = deprecatedKey; + return this; + } + + public PropertyDefinition build() { + Preconditions.checkArgument(!Strings.isNullOrEmpty(key), "Key must be set"); + Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Name must be set"); + fixType(key, type); + return new PropertyDefinition(this); + } + + private void fixType(String key, PropertyType type) { + // Auto-detect passwords and licenses for old versions of plugins that + // do not declare property types + if (type == PropertyType.STRING) { + if (StringUtils.endsWith(key, ".password.secured")) { + this.type = PropertyType.PASSWORD; + } else if (StringUtils.endsWith(key, ".license.secured")) { + this.type = PropertyType.LICENSE; + } + } + } + } + } 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 58820d07115..e7e2fb96c64 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 @@ -69,6 +69,15 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen } public PropertyDefinitions addComponent(Object component, String defaultCategory) { + addComponentFromAnnotationPropety(component, defaultCategory); + if (component instanceof PropertyDefinition) { + PropertyDefinition propertyDefinition = (PropertyDefinition) component; + add(propertyDefinition, defaultCategory); + } + return this; + } + + private PropertyDefinitions addComponentFromAnnotationPropety(Object component, String defaultCategory){ Properties annotations = AnnotationUtils.getAnnotation(component, Properties.class); if (annotations != null) { for (Property property : annotations.value()) { @@ -88,11 +97,11 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen } 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)); - if (!Strings.isNullOrEmpty(definition.getDeprecatedKey()) && !definition.getDeprecatedKey().equals(definition.getKey())) { - deprecatedKeys.put(definition.getDeprecatedKey(), definition.getKey()); + if (!definitions.containsKey(definition.key())) { + definitions.put(definition.key(), definition); + categories.put(definition.key(), StringUtils.defaultIfBlank(definition.category(), defaultCategory)); + if (!Strings.isNullOrEmpty(definition.deprecatedKey()) && !definition.deprecatedKey().equals(definition.key())) { + deprecatedKeys.put(definition.deprecatedKey(), definition.key()); } } return this; @@ -114,19 +123,19 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen GLOBAL { @Override boolean accept(PropertyDefinition propertyDefinition) { - return propertyDefinition.isGlobal(); + return propertyDefinition.global(); } }, PROJECT { @Override boolean accept(PropertyDefinition propertyDefinition) { - return propertyDefinition.isOnProject(); + return propertyDefinition.project(); } }, MODULE { @Override boolean accept(PropertyDefinition propertyDefinition) { - return propertyDefinition.isOnModule(); + return propertyDefinition.module(); } }; @@ -138,7 +147,7 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen for (PropertyDefinition definition : getAll()) { if (filter.accept(definition)) { - byCategory.put(getCategory(definition.getKey()), definition); + byCategory.put(getCategory(definition.key()), definition); } } @@ -171,7 +180,7 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen if (def == null) { return null; } - return StringUtils.defaultIfEmpty(def.getDefaultValue(), null); + return StringUtils.defaultIfEmpty(def.defaultValue(), null); } public String getCategory(String key) { @@ -191,6 +200,6 @@ public final class PropertyDefinitions implements BatchComponent, ServerComponen if (def == null) { return null; } - return StringUtils.defaultIfEmpty(def.getDeprecatedKey(), null); + return StringUtils.defaultIfEmpty(def.deprecatedKey(), null); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyFieldDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyFieldDefinition.java index 05fbea9cf7e..b71ff860ec9 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyFieldDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/PropertyFieldDefinition.java @@ -19,7 +19,9 @@ */ package org.sonar.api.config; -import com.google.common.collect.Lists; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import org.sonar.api.PropertyField; import org.sonar.api.PropertyType; @@ -27,6 +29,8 @@ import javax.annotation.Nullable; import java.util.List; +import static com.google.common.collect.Lists.newArrayList; + /** * @since 3.3 */ @@ -36,50 +40,115 @@ public final class PropertyFieldDefinition { private final String description; private final int indicativeSize; private final PropertyType type; - private final String[] options; - - private PropertyFieldDefinition(PropertyField annotation) { - this.key = annotation.key(); - this.name = annotation.name(); - this.description = annotation.description(); - this.indicativeSize = annotation.indicativeSize(); - this.type = annotation.type(); - this.options = annotation.options(); + private final List<String> options; + + private PropertyFieldDefinition(Builder builder) { + this.key = builder.key; + this.name = builder.name; + this.description = builder.description; + this.indicativeSize = builder.indicativeSize; + this.type = builder.type; + this.options = builder.options; } - public static List<PropertyFieldDefinition> create(PropertyField[] fields) { - List<PropertyFieldDefinition> definitions = Lists.newArrayList(); + static List<PropertyFieldDefinition> create(PropertyField[] fields) { + List<PropertyFieldDefinition> definitions = newArrayList(); for (PropertyField field : fields) { - definitions.add(new PropertyFieldDefinition(field)); + definitions.add(PropertyFieldDefinition.build(field.key()) + .name(field.name()) + .description(field.description()) + .indicativeSize(field.indicativeSize()) + .type(field.type()) + .options(field.options()) + .build() + ); } return definitions; } - public String getKey() { + public static Builder build(String key) { + return new Builder(key); + } + + public String key() { return key; } - public String getName() { + public String name() { return name; } - public String getDescription() { + public String description() { return description; } - public int getIndicativeSize() { + public int indicativeSize() { return indicativeSize; } - public PropertyType getType() { + public PropertyType type() { return type; } - public String[] getOptions() { - return options.clone(); + public List<String> options() { + return options; } public PropertyDefinition.Result validate(@Nullable String value) { return PropertyDefinition.validate(type, value, options); } + + public static class Builder { + private String key; + private String name; + private String description; + private int indicativeSize; + private PropertyType type; + private List<String> options; + + private Builder(String key) { + this.key = key; + this.name = ""; + this.description = ""; + this.indicativeSize = 20; + this.type = PropertyType.STRING; + this.options = newArrayList(); + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder indicativeSize(int indicativeSize) { + this.indicativeSize = indicativeSize; + return this; + } + + public Builder type(PropertyType type) { + this.type = type; + return this; + } + + public Builder options(String... options) { + this.options.addAll(ImmutableList.copyOf(options)); + return this; + } + + public Builder options(List<String> options) { + this.options.addAll(ImmutableList.copyOf(options)); + return this; + } + + public PropertyFieldDefinition build() { + Preconditions.checkArgument(!Strings.isNullOrEmpty(key), "Key must be set"); + Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Name must be set"); + return new PropertyFieldDefinition(this); + } + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java index 1a9c2ffc975..d51c5efcfac 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java @@ -191,7 +191,7 @@ public class Settings implements BatchComponent, ServerComponent { */ public String[] getStringArray(String key) { PropertyDefinition property = getDefinitions().get(key); - if ((null != property) && (property.isMultiValues())) { + if ((null != property) && (property.multiValues())) { String value = getString(key); if (value == null) { return ArrayUtils.EMPTY_STRING_ARRAY; @@ -259,7 +259,7 @@ public class Settings implements BatchComponent, ServerComponent { public Settings setProperty(String key, @Nullable String[] values) { PropertyDefinition property = getDefinitions().get(key); - if ((null == property) || (!property.isMultiValues())) { + if ((null == property) || (!property.multiValues())) { throw new IllegalStateException("Fail to set multiple values on a single value property " + key); } |