NIFI-12573 Improved support for Enums in PropertyDescriptor.Builder

NIFI-12574 Add clearDefaultValue to PropertyDescriptor.Builder

This closes #8211

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
EndzeitBegins 2024-01-07 16:02:10 +01:00 committed by exceptionfactory
parent 281a28c5d4
commit 4588c6c37e
No known key found for this signature in database
5 changed files with 366 additions and 229 deletions

View File

@ -43,9 +43,8 @@ public class AllowableValue implements DescribedValue {
* Constructs a new AllowableValue with the given value and display name and * Constructs a new AllowableValue with the given value and display name and
* no description * no description
* *
* @param value that is allowed * @param value that is allowed
* @param displayName to display for the value * @param displayName to display for the value
*
* @throws NullPointerException if either argument is null * @throws NullPointerException if either argument is null
*/ */
public AllowableValue(final String value, final String displayName) { public AllowableValue(final String value, final String displayName) {
@ -56,10 +55,9 @@ public class AllowableValue implements DescribedValue {
* Constructs a new AllowableValue with the given value, display name, and * Constructs a new AllowableValue with the given value, display name, and
* description * description
* *
* @param value that is valid * @param value that is valid
* @param displayName to show for the value * @param displayName to show for the value
* @param description of the value * @param description of the value
*
* @throws NullPointerException if identifier or value is null * @throws NullPointerException if identifier or value is null
*/ */
public AllowableValue(final String value, final String displayName, final String description) { public AllowableValue(final String value, final String displayName, final String description) {
@ -68,6 +66,14 @@ public class AllowableValue implements DescribedValue {
this.description = description; this.description = description;
} }
public static AllowableValue fromDescribedValue(final DescribedValue describedValue) {
if (describedValue instanceof AllowableValue allowableValue) {
return allowableValue;
}
return new AllowableValue(describedValue.getValue(), describedValue.getDisplayName(), describedValue.getDescription());
}
/** /**
* @return the value of this AllowableValue * @return the value of this AllowableValue
*/ */
@ -106,8 +112,7 @@ public class AllowableValue implements DescribedValue {
return true; return true;
} }
if (obj instanceof AllowableValue) { if (obj instanceof AllowableValue other) {
final AllowableValue other = (AllowableValue) obj;
return (this.value.equals(other.getValue())); return (this.value.equals(other.getValue()));
} else if (obj instanceof String) { } else if (obj instanceof String) {
return this.value.equals(obj); return this.value.equals(obj);

View File

@ -19,9 +19,9 @@ package org.apache.nifi.components;
import org.apache.nifi.components.resource.ResourceCardinality; import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceDefinition; import org.apache.nifi.components.resource.ResourceDefinition;
import org.apache.nifi.components.resource.ResourceReference; import org.apache.nifi.components.resource.ResourceReference;
import org.apache.nifi.components.resource.StandardResourceReferenceFactory;
import org.apache.nifi.components.resource.ResourceType; import org.apache.nifi.components.resource.ResourceType;
import org.apache.nifi.components.resource.StandardResourceDefinition; import org.apache.nifi.components.resource.StandardResourceDefinition;
import org.apache.nifi.components.resource.StandardResourceReferenceFactory;
import org.apache.nifi.controller.ControllerService; import org.apache.nifi.controller.ControllerService;
import org.apache.nifi.expression.ExpressionLanguageScope; import org.apache.nifi.expression.ExpressionLanguageScope;
@ -38,7 +38,6 @@ import java.util.Set;
/** /**
* An immutable object for holding information about a type of component * An immutable object for holding information about a type of component
* property. * property.
*
*/ */
public final class PropertyDescriptor implements Comparable<PropertyDescriptor> { public final class PropertyDescriptor implements Comparable<PropertyDescriptor> {
@ -123,15 +122,15 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
this.name = builder.name; this.name = builder.name;
this.description = builder.description; this.description = builder.description;
this.defaultValue = builder.defaultValue; this.defaultValue = builder.defaultValue;
this.allowableValues = builder.allowableValues == null ? null : Collections.unmodifiableList(new ArrayList<>(builder.allowableValues)); this.allowableValues = builder.allowableValues == null ? null : List.copyOf(builder.allowableValues);
this.required = builder.required; this.required = builder.required;
this.sensitive = builder.sensitive; this.sensitive = builder.sensitive;
this.dynamic = builder.dynamic; this.dynamic = builder.dynamic;
this.dynamicallyModifiesClasspath = builder.dynamicallyModifiesClasspath; this.dynamicallyModifiesClasspath = builder.dynamicallyModifiesClasspath;
this.expressionLanguageScope = builder.expressionLanguageScope; this.expressionLanguageScope = builder.expressionLanguageScope;
this.controllerServiceDefinition = builder.controllerServiceDefinition; this.controllerServiceDefinition = builder.controllerServiceDefinition;
this.validators = Collections.unmodifiableList(new ArrayList<>(builder.validators)); this.validators = List.copyOf(builder.validators);
this.dependencies = builder.dependencies == null ? Collections.emptySet() : Collections.unmodifiableSet(new HashSet<>(builder.dependencies)); this.dependencies = builder.dependencies == null ? Collections.emptySet() : Set.copyOf(builder.dependencies);
this.resourceDefinition = builder.resourceDefinition; this.resourceDefinition = builder.resourceDefinition;
} }
@ -148,7 +147,7 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* If this descriptor has a set of allowable values then the given value is * If this descriptor has a set of allowable values then the given value is
* only checked against the allowable values. * only checked against the allowable values.
* *
* @param input the value to validate * @param input the value to validate
* @param context the context of validation * @param context the context of validation
* @return the result of validating the input * @return the result of validating the input
*/ */
@ -188,15 +187,15 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
final ControllerService service = context.getControllerServiceLookup().getControllerService(input); final ControllerService service = context.getControllerServiceLookup().getControllerService(input);
if (service == null) { if (service == null) {
return new ValidationResult.Builder() return new ValidationResult.Builder()
.input(input) .input(input)
.subject(getDisplayName()) .subject(getDisplayName())
.valid(false) .valid(false)
.explanation("Property references a Controller Service that does not exist") .explanation("Property references a Controller Service that does not exist")
.build(); .build();
} else { } else {
return new ValidationResult.Builder() return new ValidationResult.Builder()
.valid(true) .valid(true)
.build(); .build();
} }
} }
@ -293,15 +292,13 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
} }
/** /**
* Specifies the initial value and the default value that will be used * Specifies the initial value and the default value that will be used if the user does not specify a value.
* if the user does not specify a value. When {@link #build()} is * <p>
* called, if Allowable Values have been set (see * When {@link #build()} is called, if Allowable Values have been set (see {@link #allowableValues(DescribedValue...)} and overloads)
* {@link #allowableValues(AllowableValue...)}) and this value is not * and this value is not one of those Allowable Values, an Exception will be thrown.
* one of those Allowable Values, an Exception will be thrown. If the * If the Allowable Values have been set, the default value should be set to
* Allowable Values have been set using the * the "Value" of the {@link DescribedValue} object (see {@link DescribedValue#getValue()}).
* {@link #allowableValues(AllowableValue...)} method, the default value * There's an overload available for this (see {@link #defaultValue(DescribedValue)}).
* should be set to the "Value" of the {@link AllowableValue} object
* (see {@link AllowableValue#getValue()}).
* *
* @param value default value * @param value default value
* @return the builder * @return the builder
@ -314,15 +311,12 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
} }
/** /**
* Specifies the initial value and the default value that will be used * Specifies the initial value and the default value that will be used if the user does not specify a value.
* if the user does not specify a value. When {@link #build()} is * <p>
* called, if Allowable Values have been set (see * Sets the default value to the "Value" of the {@link DescribedValue} object.
* {@link #allowableValues(AllowableValue...)}) * When {@link #build()} is called, if Allowable Values have been set (see {@link #allowableValues(DescribedValue...)} and overloads)
* and the "Value" of the {@link DescribedValue} object is not * and this value is not one of those Allowable Values, an Exception will be thrown.
* the "Value" of one of those Allowable Values, an Exception will be thrown. * In case there is not a restricted set of Allowable Values {@link #defaultValue(String)} may be used.
* If the Allowable Values have been set using the
* {@link #allowableValues(AllowableValue...)} method, the default value
* should be set providing the {@link AllowableValue} to this method.
* *
* @param value default value holder * @param value default value holder
* @return the builder * @return the builder
@ -331,6 +325,16 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
return defaultValue(value != null ? value.getValue() : null); return defaultValue(value != null ? value.getValue() : null);
} }
/**
* Clears the initial value and default value from this Property.
*
* @return the builder
*/
public Builder clearDefaultValue() {
this.defaultValue = null;
return this;
}
public Builder dynamic(final boolean dynamic) { public Builder dynamic(final boolean dynamic) {
this.dynamic = dynamic; this.dynamic = dynamic;
return this; return this;
@ -342,17 +346,17 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* libraries for the given component. * libraries for the given component.
* <p/> * <p/>
* NOTE: If a component contains a PropertyDescriptor where dynamicallyModifiesClasspath is set to true, * NOTE: If a component contains a PropertyDescriptor where dynamicallyModifiesClasspath is set to true,
* the component may also be annotated with @RequiresInstanceClassloading, in which case every class will * the component may also be annotated with @RequiresInstanceClassloading, in which case every class will
* be loaded by a separate InstanceClassLoader for each processor instance.<br/> * be loaded by a separate InstanceClassLoader for each processor instance.<br/>
* It also allows to load native libraries from the extra classpath. * It also allows to load native libraries from the extra classpath.
* <p/> * <p/>
* One can chose to omit the annotation. In this case the loading of native libraries from the extra classpath * One can choose to omit the annotation. In this case the loading of native libraries from the extra classpath
* is not supported. * is not supported.
* Also by default, classes will be loaded by a common NarClassLoader, however it's possible to acquire an * Also by default, classes will be loaded by a common NarClassLoader, however it's possible to acquire an
* InstanceClassLoader by calling Thread.currentThread().getContextClassLoader() which can be used manually * InstanceClassLoader by calling Thread.currentThread().getContextClassLoader() which can be used manually
* to load required classes on an instance-by-instance basis * to load required classes on an instance-by-instance basis
* (by calling {@link Class#forName(String, boolean, ClassLoader)} for example). * (by calling {@link Class#forName(String, boolean, ClassLoader)} for example).
* * <p>
* Any property descriptor that dynamically modifies the classpath should also make use of the {@link #identifiesExternalResource(ResourceCardinality, ResourceType, ResourceType...)} method * Any property descriptor that dynamically modifies the classpath should also make use of the {@link #identifiesExternalResource(ResourceCardinality, ResourceType, ResourceType...)} method
* to indicate that the property descriptor references external resources and optionally restrict which types of resources and how many resources the property allows. * to indicate that the property descriptor references external resources and optionally restrict which types of resources and how many resources the property allows.
* *
@ -365,68 +369,99 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
} }
/** /**
* @param values contrained set of values * Sets the Allowable Values for this Property.
*
* @param values constrained set of values
* @return the builder * @return the builder
*/ */
public Builder allowableValues(final Set<String> values) { public Builder allowableValues(final Set<String> values) {
if (null != values) { if (null != values) {
this.allowableValues = new ArrayList<>(); this.allowableValues = values.stream().map(AllowableValue::new).toList();
for (final String value : values) {
this.allowableValues.add(new AllowableValue(value, value));
}
} }
return this; return this;
} }
/**
* Sets the Allowable Values for this Property.
* <p>
* Uses the {@link Enum#name()} of each value as "Value" for the {@link AllowableValue}.
* In case the enum value is a {@link DescribedValue}, uses the information provided instead
* (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()}, {@link DescribedValue#getDescription()}).
*
* @param values constrained set of values
* @return the builder
*/
public <E extends Enum<E>> Builder allowableValues(final E[] values) { public <E extends Enum<E>> Builder allowableValues(final E[] values) {
if (null != values) { if (null != values) {
this.allowableValues = new ArrayList<>(); this.allowableValues = Arrays.stream(values)
for (final E value : values) { .map(enumValue -> enumValue instanceof DescribedValue describedValue
allowableValues.add(new AllowableValue(value.name(), value.name())); ? AllowableValue.fromDescribedValue(describedValue) : new AllowableValue(enumValue.name()))
} .toList();
} }
return this; return this;
} }
/** /**
* Stores allowable values from an enum class. * Sets the Allowable Values for this Property.
* @param enumClass an enum class that implements the DescribedValue interface and contains a set of values * <p>
* @param <E> generic parameter for an enum class that implements the DescribedValue interface * Uses the {@link Enum#name()} of each value from {@link Class#getEnumConstants()} as "Value" for the {@link AllowableValue}.
* In case the enum value is a {@link DescribedValue}, uses the information provided instead
* (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()}, {@link DescribedValue#getDescription()}).
*
* @param enumClass an enum class that contains a set of values and optionally implements the DescribedValue interface
* @param <E> generic parameter for an enum class, that may implement the DescribedValue interface
* @return the builder * @return the builder
*/ */
public <E extends Enum<E> & DescribedValue> Builder allowableValues(final Class<E> enumClass) { public <E extends Enum<E>> Builder allowableValues(final Class<E> enumClass) {
this.allowableValues = new ArrayList<>(); return allowableValues(enumClass.getEnumConstants());
for (E enumValue : enumClass.getEnumConstants()) {
this.allowableValues.add(new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription()));
}
return this;
} }
/** /**
* Stores allowable values from a set of enum values. * Sets the Allowable Values for this Property.
* @param enumValues a set of enum values that implements the DescribedValue interface * <p>
* @param <E> generic parameter for the enum values' class that implements the DescribedValue interface * Uses the {@link Enum#name()} of each value of the {@link EnumSet} as "Value" for the {@link AllowableValue}.
* In case the enum value is a {@link DescribedValue}, uses the information provided instead
* (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()}, {@link DescribedValue#getDescription()}).
*
* @param enumValues an enum set that contains a set of values and optionally implements the DescribedValue interface
* @param <E> generic parameter for an enum class, that may implement the DescribedValue interface
* @return the builder * @return the builder
*/ */
public <E extends Enum<E> & DescribedValue> Builder allowableValues(final EnumSet<E> enumValues) { public <E extends Enum<E>> Builder allowableValues(final EnumSet<E> enumValues) {
this.allowableValues = new ArrayList<>(); if (null != enumValues) {
for (E enumValue : enumValues) { this.allowableValues = enumValues.stream()
this.allowableValues.add(new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription())); .map(enumValue -> enumValue instanceof DescribedValue describedValue
? AllowableValue.fromDescribedValue(describedValue) : new AllowableValue(enumValue.name()))
.toList();
} }
return this; return this;
} }
/** /**
* Sets the Allowable Values for this Property.
*
* @param values constrained set of values * @param values constrained set of values
* @return the builder * @return the builder
*/ */
public Builder allowableValues(final String... values) { public Builder allowableValues(final String... values) {
if (null != values) { if (null != values) {
this.allowableValues = new ArrayList<>(); this.allowableValues = Arrays.stream(values).map(AllowableValue::new).toList();
for (final String value : values) { }
allowableValues.add(new AllowableValue(value, value)); return this;
} }
/**
* Sets the Allowable Values for this Property.
* <p>
* Uses the information provided by each {@link DescribedValue} (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()},
* {@link DescribedValue#getDescription()}) to populate the {@link AllowableValue}s.
*
* @param values constrained set of values
* @return the builder
*/
public Builder allowableValues(final DescribedValue... values) {
if (null != values) {
this.allowableValues = Arrays.stream(values).map(AllowableValue::fromDescribedValue).toList();
} }
return this; return this;
} }
@ -441,19 +476,6 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
return this; return this;
} }
/**
* Sets the Allowable Values for this Property
*
* @param values contrained set of values
* @return the builder
*/
public Builder allowableValues(final AllowableValue... values) {
if (null != values) {
this.allowableValues = Arrays.asList(values);
}
return this;
}
/** /**
* @param required true if yes; false otherwise * @param required true if yes; false otherwise
* @return the builder * @return the builder
@ -498,7 +520,7 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* Service that implements the given interface * Service that implements the given interface
* *
* @param controllerServiceDefinition the interface that is implemented * @param controllerServiceDefinition the interface that is implemented
* by the Controller Service * by the Controller Service
* @return the builder * @return the builder
*/ */
public Builder identifiesControllerService(final Class<? extends ControllerService> controllerServiceDefinition) { public Builder identifiesControllerService(final Class<? extends ControllerService> controllerServiceDefinition) {
@ -533,12 +555,12 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* </li> * </li>
* <li>If the ResourceCardinality is MULTIPLE, the given property value may consist of one or more resources, each separted by a comma and optional white space.</li> * <li>If the ResourceCardinality is MULTIPLE, the given property value may consist of one or more resources, each separted by a comma and optional white space.</li>
* </ul> * </ul>
* * <p>
* Generally, any property descriptor that makes use of the {@link #dynamicallyModifiesClasspath(boolean)} method to dynamically update its classpath should also * Generally, any property descriptor that makes use of the {@link #dynamicallyModifiesClasspath(boolean)} method to dynamically update its classpath should also
* make use of this method, specifying which types of resources are allowed and how many. * make use of this method, specifying which types of resources are allowed and how many.
* *
* @param cardinality specifies how many resources the property should allow * @param cardinality specifies how many resources the property should allow
* @param resourceType the type of resource that is allowed * @param resourceType the type of resource that is allowed
* @param additionalResourceTypes if more than one type of resource is allowed, any resource type in addition to the given resource type may be provided * @param additionalResourceTypes if more than one type of resource is allowed, any resource type in addition to the given resource type may be provided
* @return the builder * @return the builder
*/ */
@ -558,15 +580,15 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a non-null value. * Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a non-null value.
* Furthermore, if one or more explicit Allowable Values are provided, this Property will not be relevant unless the given Property's value is equal to one of the given Allowable Values. * Furthermore, if one or more explicit Allowable Values are provided, this Property will not be relevant unless the given Property's value is equal to one of the given Allowable Values.
* If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied. * If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied.
* * <p>
* In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given * In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given
* Allowable Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for * Allowable Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for
* validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component * validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component
* will still be valid, because the value of this property is irrelevant. * will still be valid, because the value of this property is irrelevant.
* * <p>
* If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid. * If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid.
* *
* @param property the property that must be set in order for this property to become relevant * @param property the property that must be set in order for this property to become relevant
* @param dependentValues the possible values for the given property for which this Property is relevant * @param dependentValues the possible values for the given property for which this Property is relevant
* @return the builder * @return the builder
*/ */
@ -593,16 +615,16 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a value equal to one of the given * Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a value equal to one of the given
* <code>String</code> arguments. * <code>String</code> arguments.
* If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied. * If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied.
* * <p>
* In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given * In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given
* Allowable Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for * Allowable Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for
* validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component * validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component
* will still be valid, because the value of this property is irrelevant. * will still be valid, because the value of this property is irrelevant.
* * <p>
* If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid. * If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid.
* *
* @param property the property that must be set in order for this property to become relevant * @param property the property that must be set in order for this property to become relevant
* @param firstDependentValue the first value for the given property for which this Property is relevant * @param firstDependentValue the first value for the given property for which this Property is relevant
* @param additionalDependentValues any other values for the given property for which this Property is relevant * @param additionalDependentValues any other values for the given property for which this Property is relevant
* @return the builder * @return the builder
*/ */
@ -621,25 +643,25 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a value equal to one of the given * Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a value equal to one of the given
* {@link DescribedValue} arguments. * {@link DescribedValue} arguments.
* If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied. * If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied.
* * <p>
* In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given * In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given
* Described Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for * Described Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for
* validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component * validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component
* will still be valid, because the value of this property is irrelevant. * will still be valid, because the value of this property is irrelevant.
* * <p>
* If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid. * If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid.
* *
* @param property the property that must be set in order for this property to become relevant * @param property the property that must be set in order for this property to become relevant
* @param firstDependentValue the first value for the given property for which this Property is relevant * @param firstDependentValue the first value for the given property for which this Property is relevant
* @param additionalDependentValues any other values for the given property for which this Property is relevant * @param additionalDependentValues any other values for the given property for which this Property is relevant
* @return the builder * @return the builder
*/ */
public Builder dependsOn(final PropertyDescriptor property, final DescribedValue firstDependentValue, final DescribedValue... additionalDependentValues) { public Builder dependsOn(final PropertyDescriptor property, final DescribedValue firstDependentValue, final DescribedValue... additionalDependentValues) {
final AllowableValue[] dependentValues = new AllowableValue[additionalDependentValues.length + 1]; final AllowableValue[] dependentValues = new AllowableValue[additionalDependentValues.length + 1];
dependentValues[0] = toAllowableValue(firstDependentValue); dependentValues[0] = AllowableValue.fromDescribedValue(firstDependentValue);
int i = 1; int i = 1;
for (final DescribedValue additionalDependentValue : additionalDependentValues) { for (final DescribedValue additionalDependentValue : additionalDependentValues) {
dependentValues[i++] = toAllowableValue(additionalDependentValue); dependentValues[i++] = AllowableValue.fromDescribedValue(additionalDependentValue);
} }
return dependsOn(property, dependentValues); return dependsOn(property, dependentValues);
@ -655,16 +677,11 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
return this; return this;
} }
private AllowableValue toAllowableValue(DescribedValue describedValue) {
return new AllowableValue(describedValue.getValue(), describedValue.getDisplayName(), describedValue.getDescription());
}
/** /**
* @return a PropertyDescriptor as configured * @return a PropertyDescriptor as configured
*
* @throws IllegalStateException if allowable values are configured but * @throws IllegalStateException if allowable values are configured but
* no default value is set, or the default value is not contained within * no default value is set, or the default value is not contained within
* the allowable values. * the allowable values.
*/ */
public PropertyDescriptor build() { public PropertyDescriptor build() {
if (name == null) { if (name == null) {
@ -740,18 +757,14 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
@Override @Override
public boolean equals(final Object other) { public boolean equals(final Object other) {
if (other == null) {
return false;
}
if (!(other instanceof PropertyDescriptor)) {
return false;
}
if (this == other) { if (this == other) {
return true; return true;
} }
if (other instanceof PropertyDescriptor otherPropertyDescriptor) {
return this.name.equals(otherPropertyDescriptor.name);
}
final PropertyDescriptor desc = (PropertyDescriptor) other; return false;
return this.name.equals(desc.name);
} }
@Override @Override
@ -768,7 +781,7 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
private static final String POSITIVE_EXPLANATION = "Given value found in allowed set"; private static final String POSITIVE_EXPLANATION = "Given value found in allowed set";
private static final String NEGATIVE_EXPLANATION = "Given value not found in allowed set '%1$s'"; private static final String NEGATIVE_EXPLANATION = "Given value not found in allowed set '%1$s'";
private static final String VALUE_DEMARCATOR = ", "; private static final String VALUE_DELIMITER = ", ";
private final String validStrings; private final String validStrings;
private final Collection<String> validValues; private final Collection<String> validValues;
@ -780,20 +793,8 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
* @throws NullPointerException if the given validValues is null * @throws NullPointerException if the given validValues is null
*/ */
private ConstrainedSetValidator(final Collection<AllowableValue> validValues) { private ConstrainedSetValidator(final Collection<AllowableValue> validValues) {
String validVals = ""; this.validValues = validValues.stream().map(AllowableValue::getValue).toList();
if (!validValues.isEmpty()) { this.validStrings = String.join(VALUE_DELIMITER, this.validValues);
final StringBuilder valuesBuilder = new StringBuilder();
for (final AllowableValue value : validValues) {
valuesBuilder.append(value).append(VALUE_DEMARCATOR);
}
validVals = valuesBuilder.substring(0, valuesBuilder.length() - VALUE_DEMARCATOR.length());
}
validStrings = validVals;
this.validValues = new ArrayList<>(validValues.size());
for (final AllowableValue value : validValues) {
this.validValues.add(value.getValue());
}
} }
@Override @Override
@ -824,13 +825,13 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
@Override @Override
public ValidationResult validate(final String subject, final String configuredInput, final ValidationContext context) { public ValidationResult validate(final String subject, final String configuredInput, final ValidationContext context) {
final ValidationResult.Builder resultBuilder = new ValidationResult.Builder() final ValidationResult.Builder resultBuilder = new ValidationResult.Builder()
.input(configuredInput) .input(configuredInput)
.subject(subject); .subject(subject);
if (configuredInput == null) { if (configuredInput == null) {
return resultBuilder.valid(false) return resultBuilder.valid(false)
.explanation("No value specified") .explanation("No value specified")
.build(); .build();
} }
// If Expression Language is supported and is used in the property value, we cannot perform validation against the configured // If Expression Language is supported and is used in the property value, we cannot perform validation against the configured
@ -843,8 +844,8 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
resultBuilder.input(input); resultBuilder.input(input);
} else { } else {
return resultBuilder.valid(true) return resultBuilder.valid(true)
.explanation("Expression Language is present, so validation of property value cannot be performed") .explanation("Expression Language is present, so validation of property value cannot be performed")
.build(); .build();
} }
} }
@ -854,15 +855,15 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
final boolean allowsText = resourceDefinition.getResourceTypes().contains(ResourceType.TEXT); final boolean allowsText = resourceDefinition.getResourceTypes().contains(ResourceType.TEXT);
if (allowsText) { if (allowsText) {
return resultBuilder.valid(true) return resultBuilder.valid(true)
.explanation("Property allows for Resource Type of Text, so validation of property value cannot be performed") .explanation("Property allows for Resource Type of Text, so validation of property value cannot be performed")
.build(); .build();
} }
final String[] splits = input.split(","); final String[] splits = input.split(",");
if (resourceDefinition.getCardinality() == ResourceCardinality.SINGLE && splits.length > 1) { if (resourceDefinition.getCardinality() == ResourceCardinality.SINGLE && splits.length > 1) {
return resultBuilder.valid(false) return resultBuilder.valid(false)
.explanation("Property only supports a single Resource but " + splits.length + " resources were specified") .explanation("Property only supports a single Resource but " + splits.length + " resources were specified")
.build(); .build();
} }
final Set<ResourceType> resourceTypes = resourceDefinition.getResourceTypes(); final Set<ResourceType> resourceTypes = resourceDefinition.getResourceTypes();
@ -885,25 +886,25 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
if (!resourceTypes.contains(resourceReference.getResourceType())) { if (!resourceTypes.contains(resourceReference.getResourceType())) {
return resultBuilder.valid(false) return resultBuilder.valid(false)
.explanation("Specified Resource is a " + resourceReference.getResourceType().name() + " but this property does not allow this type of resource") .explanation("Specified Resource is a " + resourceReference.getResourceType().name() + " but this property does not allow this type of resource")
.build(); .build();
} }
} }
if (count == 0) { if (count == 0) {
return resultBuilder.valid(false) return resultBuilder.valid(false)
.explanation("No resources were specified") .explanation("No resources were specified")
.build(); .build();
} }
if (!nonExistentResources.isEmpty()) { if (!nonExistentResources.isEmpty()) {
return resultBuilder.valid(false) return resultBuilder.valid(false)
.explanation("The specified resource(s) do not exist or could not be accessed: " + nonExistentResources) .explanation("The specified resource(s) do not exist or could not be accessed: " + nonExistentResources)
.build(); .build();
} }
return resultBuilder.valid(true) return resultBuilder.valid(true)
.build(); .build();
} }
} }
} }

View File

@ -16,7 +16,7 @@
*/ */
package org.apache.nifi.components; package org.apache.nifi.components;
public enum EnumAllowableValue implements DescribedValue { public enum EnumDescribedValue implements DescribedValue {
GREEN { GREEN {
@Override @Override

View File

@ -0,0 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.components;
public enum EnumNotDescribedValue {
GREEN, RED, BLUE;
}

View File

@ -20,17 +20,16 @@ import org.apache.nifi.components.PropertyDescriptor.Builder;
import org.apache.nifi.components.resource.ResourceCardinality; import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceType; import org.apache.nifi.components.resource.ResourceType;
import org.apache.nifi.expression.ExpressionLanguageScope; import org.apache.nifi.expression.ExpressionLanguageScope;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
@ -40,77 +39,180 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
/**
* Regression test for issue NIFI-49, to ensure that if a Processor's Property's
* Default Value is not allowed, the Exception thrown should indicate what the
* default value is
*/
public class TestPropertyDescriptor { public class TestPropertyDescriptor {
private static Builder invalidDescriptorBuilder;
private static Builder validDescriptorBuilder;
private static final String DEFAULT_VALUE = "Default Value"; private static final String DEFAULT_VALUE = "Default Value";
private static final String DEPENDENT_PROPERTY_NAME = "dependentProperty"; private static final String DEPENDENT_PROPERTY_NAME = "dependentProperty";
@BeforeAll @Nested
public static void setUp() { class RegardingDefaultValue {
validDescriptorBuilder = new PropertyDescriptor.Builder().name("").allowableValues("Allowable Value", "Another Allowable Value").defaultValue("Allowable Value"); @Test
invalidDescriptorBuilder = new PropertyDescriptor.Builder().name("").allowableValues("Allowable Value", "Another Allowable Value").defaultValue(DEFAULT_VALUE); void supportsStringValues() {
final PropertyDescriptor descriptor = builder().defaultValue(DEFAULT_VALUE).build();
assertEquals(DEFAULT_VALUE, descriptor.getDefaultValue());
}
@Test
void supportsDescribedValuesValues() {
final PropertyDescriptor descriptor = builder().defaultValue(EnumDescribedValue.GREEN).build();
assertEquals(EnumDescribedValue.GREEN.getValue(), descriptor.getDefaultValue());
}
/**
* Regression test for issue NIFI-49, to ensure that if a Processor's Property's
* Default Value is not allowed, the Exception thrown should indicate what the default value is
*/
@Test
void throwsIllegalStateExceptionWhenDefaultValueNotInAllowableValues() {
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
builder().allowableValues("NOT DEFAULT", "OTHER NOT DEFAULT").defaultValue(DEFAULT_VALUE).build();
});
assertTrue(exception.getMessage().contains("[" + DEFAULT_VALUE + "]"));
}
@Test
void canBeCleared() {
final PropertyDescriptor descriptorWithDefault = builder().defaultValue(DEFAULT_VALUE).build();
final PropertyDescriptor resetDescriptor = builder(descriptorWithDefault).clearDefaultValue().build();
assertNull(resetDescriptor.getDefaultValue());
}
} }
@Test @Nested
void testExceptionThrownByDescriptorWithInvalidDefaultValue() { class RegardingAllowableValues {
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> invalidDescriptorBuilder.build());
assertTrue(exception.getMessage().contains("[" + DEFAULT_VALUE + "]") );
}
@Test private static final Comparator<AllowableValue> allowableValueComparator = Comparator.comparing(AllowableValue::getValue);
void testNoExceptionThrownByPropertyDescriptorWithValidDefaultValue() { private final List<AllowableValue> expectedMinimalAllowableValues =
assertNotNull(validDescriptorBuilder.build()); List.of(new AllowableValue("GREEN"), new AllowableValue("RED"), new AllowableValue("BLUE"));
} private final List<AllowableValue> expectedAllowableValuesWithDescription =
Arrays.stream(EnumDescribedValue.values()).map(AllowableValue::fromDescribedValue).toList();
@Test @Test
void testDefaultValueWithDescribedValue() { void supportsStringVarArgValues() {
final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder() final List<AllowableValue> expected = expectedMinimalAllowableValues;
.name("defaultDescribedValueDescriptor")
.defaultValue(EnumAllowableValue.GREEN)
.build();
assertNotNull(propertyDescriptor); final PropertyDescriptor descriptor = builder().allowableValues("GREEN", "RED", "BLUE").build();
assertEquals(EnumAllowableValue.GREEN.getValue(), propertyDescriptor.getDefaultValue()); final List<AllowableValue> actual = descriptor.getAllowableValues();
}
@Test assertEquals(expected, actual); // equals only compares getValue()
void testAllowableValuesWithEnumClass() { assertEquals(displayNamesOf(expected), displayNamesOf(actual));
final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder() assertEquals(descriptionsOf(expected), descriptionsOf(actual));
.name("enumAllowableValueDescriptor") }
.allowableValues(EnumAllowableValue.class)
.build();
assertNotNull(propertyDescriptor); @Test
void supportsStringSetValues() {
final List<AllowableValue> expected = sort(expectedMinimalAllowableValues);
final List<AllowableValue> expectedAllowableValues = Arrays.stream(EnumAllowableValue.values()) final PropertyDescriptor descriptor = builder().allowableValues(Set.of("GREEN", "RED", "BLUE")).build();
.map(enumValue -> new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription())) // the iteration order of sets is not guaranteed by all implementations, thus we unify the order here
.collect(Collectors.toList()); final List<AllowableValue> actual = sort(descriptor.getAllowableValues());
assertEquals(expectedAllowableValues, propertyDescriptor.getAllowableValues());
}
@Test assertEquals(expected, actual); // equals only compares getValue()
void testAllowableValuesWithEnumSet() { assertEquals(displayNamesOf(expected), displayNamesOf(actual));
final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder() assertEquals(descriptionsOf(expected), descriptionsOf(actual));
.name("enumAllowableValueDescriptor") }
.allowableValues(EnumSet.of(
EnumAllowableValue.GREEN,
EnumAllowableValue.BLUE
))
.build();
assertNotNull(propertyDescriptor); @Test
void supportsEnumArrayValues() {
final List<AllowableValue> expected = expectedMinimalAllowableValues;
final List<AllowableValue> expectedAllowableValues = Stream.of(EnumAllowableValue.GREEN, EnumAllowableValue.BLUE) final PropertyDescriptor descriptor = builder().allowableValues(EnumNotDescribedValue.values()).build();
.map(enumValue -> new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription())) final List<AllowableValue> actual = descriptor.getAllowableValues();
.collect(Collectors.toList());
assertEquals(expectedAllowableValues, propertyDescriptor.getAllowableValues()); assertEquals(expected, actual); // equals only compares getValue()
assertEquals(displayNamesOf(expected), displayNamesOf(actual));
assertEquals(descriptionsOf(expected), descriptionsOf(actual));
}
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
void supportsDescribedValueEnumArrayValues() {
final List<AllowableValue> expected = expectedAllowableValuesWithDescription;
final Enum[] enumArray = EnumDescribedValue.values();
final PropertyDescriptor descriptor = builder().allowableValues(enumArray).build();
final List<AllowableValue> actual = descriptor.getAllowableValues();
assertEquals(expected, actual); // equals only compares getValue()
assertEquals(displayNamesOf(expected), displayNamesOf(actual));
assertEquals(descriptionsOf(expected), descriptionsOf(actual));
}
@Test
void supportsEnumClassValues() {
final List<AllowableValue> expected = expectedMinimalAllowableValues;
final PropertyDescriptor descriptor = builder().allowableValues(EnumNotDescribedValue.class).build();
final List<AllowableValue> actual = descriptor.getAllowableValues();
assertEquals(expected, actual); // equals only compares getValue()
assertEquals(displayNamesOf(expected), displayNamesOf(actual));
assertEquals(descriptionsOf(expected), descriptionsOf(actual));
}
@Test
void supportsDescribedValueEnumClassValues() {
final List<AllowableValue> expected = expectedAllowableValuesWithDescription;
final PropertyDescriptor descriptor = builder().allowableValues(EnumDescribedValue.class).build();
final List<AllowableValue> actual = descriptor.getAllowableValues();
assertEquals(expected, actual); // equals only compares getValue()
assertEquals(displayNamesOf(expected), displayNamesOf(actual));
assertEquals(descriptionsOf(expected), descriptionsOf(actual));
}
@Test
void supportsEnumSetValues() {
final List<AllowableValue> expected = expectedMinimalAllowableValues;
final PropertyDescriptor descriptor = builder().allowableValues(EnumSet.allOf(EnumNotDescribedValue.class)).build();
final List<AllowableValue> actual = descriptor.getAllowableValues();
assertEquals(expected, actual); // equals only compares getValue()
assertEquals(displayNamesOf(expected), displayNamesOf(actual));
assertEquals(descriptionsOf(expected), descriptionsOf(actual));
}
@Test
void supportsDescribedValueEnumSetValues() {
final List<AllowableValue> expected = expectedAllowableValuesWithDescription;
final PropertyDescriptor descriptor = builder().allowableValues(EnumSet.allOf(EnumDescribedValue.class)).build();
final List<AllowableValue> actual = descriptor.getAllowableValues();
assertEquals(expected, actual); // equals only compares getValue()
assertEquals(displayNamesOf(expected), displayNamesOf(actual));
assertEquals(descriptionsOf(expected), descriptionsOf(actual));
}
@Test
void supportsDescribedValueVarArgValues() {
final List<AllowableValue> expected = expectedAllowableValuesWithDescription;
final PropertyDescriptor descriptor = builder()
.allowableValues(EnumDescribedValue.GREEN, EnumDescribedValue.RED, EnumDescribedValue.BLUE).build();
final List<AllowableValue> actual = descriptor.getAllowableValues();
assertEquals(expected, actual); // equals only compares getValue()
assertEquals(displayNamesOf(expected), displayNamesOf(actual));
assertEquals(descriptionsOf(expected), descriptionsOf(actual));
}
private List<AllowableValue> sort(final List<AllowableValue> allowableValues) {
return allowableValues.stream().sorted(allowableValueComparator).toList();
}
private List<String> displayNamesOf(final List<AllowableValue> allowableValues) {
return allowableValues.stream().map(AllowableValue::getDisplayName).toList();
}
private List<String> descriptionsOf(final List<AllowableValue> allowableValues) {
return allowableValues.stream().map(AllowableValue::getDescription).toList();
}
} }
@Test @Test
@ -121,7 +223,7 @@ public class TestPropertyDescriptor {
final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder() final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder()
.name("enumDependsOnDescriptor") .name("enumDependsOnDescriptor")
.dependsOn(dependentPropertyDescriptor, EnumAllowableValue.RED) .dependsOn(dependentPropertyDescriptor, EnumDescribedValue.RED)
.build(); .build();
assertNotNull(propertyDescriptor); assertNotNull(propertyDescriptor);
@ -133,17 +235,17 @@ public class TestPropertyDescriptor {
final Set<String> dependentValues = dependency.getDependentValues(); final Set<String> dependentValues = dependency.getDependentValues();
assertEquals(1, dependentValues.size()); assertEquals(1, dependentValues.size());
final String dependentValue = dependentValues.iterator().next(); final String dependentValue = dependentValues.iterator().next();
assertEquals(EnumAllowableValue.RED.getValue(), dependentValue); assertEquals(EnumDescribedValue.RED.getValue(), dependentValue);
} }
@Test @Test
void testExternalResourceIgnoredIfELWithAttributesPresent() { void testExternalResourceIgnoredIfELWithAttributesPresent() {
final PropertyDescriptor descriptor = new PropertyDescriptor.Builder() final PropertyDescriptor descriptor = new PropertyDescriptor.Builder()
.name("dir") .name("dir")
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE) .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)
.expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES) .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
.required(false) .required(false)
.build(); .build();
final ValidationContext validationContext = Mockito.mock(ValidationContext.class); final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true); Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true);
@ -159,11 +261,11 @@ public class TestPropertyDescriptor {
@Test @Test
void testExternalResourceConsideredIfELVarRegistryPresent() { void testExternalResourceConsideredIfELVarRegistryPresent() {
final PropertyDescriptor descriptor = new PropertyDescriptor.Builder() final PropertyDescriptor descriptor = new PropertyDescriptor.Builder()
.name("dir") .name("dir")
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY) .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY)
.expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
.required(false) .required(false)
.build(); .build();
final AtomicReference<String> variable = new AtomicReference<>("__my_var__"); final AtomicReference<String> variable = new AtomicReference<>("__my_var__");
final ValidationContext validationContext = Mockito.mock(ValidationContext.class); final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
@ -190,9 +292,9 @@ public class TestPropertyDescriptor {
// Consider if Expression Language is not supported. // Consider if Expression Language is not supported.
Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(false); Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(false);
final PropertyDescriptor withElNotAllowed = new PropertyDescriptor.Builder() final PropertyDescriptor withElNotAllowed = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(descriptor) .fromPropertyDescriptor(descriptor)
.expressionLanguageSupported(ExpressionLanguageScope.NONE) .expressionLanguageSupported(ExpressionLanguageScope.NONE)
.build(); .build();
// Expression will not be evaluated, so the directory being looked at will literally be ${TestPropertyDescriptor.Var1} // Expression will not be evaluated, so the directory being looked at will literally be ${TestPropertyDescriptor.Var1}
assertFalse(withElNotAllowed.validate("${TestPropertyDescriptor.Var1}", validationContext).isValid()); assertFalse(withElNotAllowed.validate("${TestPropertyDescriptor.Var1}", validationContext).isValid());
@ -232,4 +334,12 @@ public class TestPropertyDescriptor {
assertTrue(pd2.getDependencies().isEmpty()); assertTrue(pd2.getDependencies().isEmpty());
assertNull(pd2.getAllowableValues()); assertNull(pd2.getAllowableValues());
} }
private Builder builder() {
return new PropertyDescriptor.Builder().name("propertyName");
}
private Builder builder(final PropertyDescriptor propertyDescriptor) {
return new PropertyDescriptor.Builder().fromPropertyDescriptor(propertyDescriptor);
}
} }