Update the defining properties page

This commit is contained in:
Clément Fournier
2019-03-06 04:14:34 +01:00
parent af612751da
commit cee4dab30a
3 changed files with 95 additions and 61 deletions
+2 -2
View File
@@ -28,12 +28,12 @@
{% elsif fun.params_are_same_as %}
(Same as for `{{ fun.params_are_same_as }}`)
{% elsif fun.parameters.size == 1 %}
0: {{ fun.parameters[0].description }}
1: {{ fun.parameters[0].description }}
{% else %}
<ul>
{% for i in 0..fun.parameters.size %}
{{ assign param = fun.parameters[i] }}
<li>{{ i }} : {{ param.description }}</li>
<li>{{ i + 1 }} : {{ param.description }}</li>
{% endfor %}
</ul>
{% endif %}
@@ -9,6 +9,7 @@ author: Hooper Bloob <hooperbloob@users.sourceforge.net>, Romain Pelisse <rpelis
---
{% jdoc_nspace :props core::properties %}
{% jdoc_nspace :PF props::PropertyFactory %}
Rule properties are a way to make your rules configurable directly from the
ruleset XML. Their usage is described on the [Configuring Rules](pmd_userdocs_configuring_rules.html#rule-properties) page.
@@ -28,31 +29,28 @@ The basic thing you need to do as a developer is to define a **property descript
Don't worry, all of these attributes can be specified in a single Java statement (or xml element for XPath rules).
Without further ado, here is the list of available (single-value) properties:
Without further ado, here is the list of available (single-value) property types:
|Class name|Value type|
|----------|----------|
|IntegerProperty | int
|DoubleProperty | double
|FloatProperty | float
|LongProperty | long
|EnumeratedProperty\<*E*\>| *E*
|StringProperty|String
|BooleanProperty|boolean
|CharacterProperty|char
|FileProperty|java.io.File
|MethodProperty|java.lang.reflect.Method
|TypeProperty|java.lang.Class\<?\>
|RegexProperty|java.util.regex.Pattern
|Value type|Factory method|
|----------|--------------|
| int |{% jdoc :PF#intProperty(java.lang.String) %}|
| double |{% jdoc :PF#doubleProperty(java.lang.String) %}|
| long |{% jdoc :PF#longProperty(java.lang.String) %}|
| char |{% jdoc :PF#charProperty(java.lang.String) %}|
| boolean |{% jdoc :PF#booleanProperty(java.lang.String) %}|
| String |{% jdoc :PF#stringProperty(java.lang.String) %}|
| java.util.regex.Pattern |{% jdoc :PF#regexProperty(java.lang.String) %}|
| *anything* |{% jdoc :PF#enumProperty(java.lang.String,java.util.Map) %}|
Each of these is complemented by a multivalued variant, whose name ends with "MultiProperty", and which returns a list of values, e.g.
Each of these is complemented by a multivalued variant, whose value is a list, e.g.
|Class name|Value type|
|----------|----------|
|LongMultiProperty | List\<Long\>
|EnumeratedMultiProperty\<*E*\>| List\<*E*\>
|Value type|Factory method|
|----------|--------------|
| List\<Integer\> |{% jdoc :PF#intListProperty(java.lang.String) %}|
| List\<*E*\> |{% jdoc :PF#enumListProperty(java.lang.String,java.util.Map) %}|
Note that RegexProperty doesn't have a multivalued variant, since the delimiters could be part of a specific value.
Note that no multivalue property is available for regex properties, since the
delimiters could be part of a specific value.
## For Java rules
@@ -64,28 +62,45 @@ You can then retrieve the value of the property at any time using {% jdoc !a!pro
### Creating a descriptor
From version 6.0.0 on, properties can be built using specific **builders**. For example, to build a string property, you'd call
Properties can be built using type-specific **builders**, which can be obtained
from the factory methods of {% jdoc :PF %}. For example, to build a
string property, you'd call
```java
StringProperty.named("myProperty")
.desc("This is my property")
.defaultValue("foo")
.build();
PropertyFactory.stringProperty("myProperty")
.desc("This is my property")
.defaultValue("foo")
.build();
```
This is fairly more readable than a constructor call, but keep in mind the description and the default value are not optional.
{%include note.html content="The constructors may be deprecated in a future release, so please use the builders instead." %}
{%include note.html
content='As of version 6.10.0, all property concrete classes are deprecated for
removal in 7.0.0. See the <a href="pmd_next_major_development.html#properties-framework">detailed list of planned removals</a> for
information about how to migrate.' %}
For **numeric properties**, you'd add a call to `range` to define the range of acceptable values, e.g.
For **numeric properties**, you can add constraints on the range of acceptable values, e.g.
```java
IntegerProperty.named("myIntProperty")
PropertyFactory.intProperty("myIntProperty")
.desc("This is my property")
.defaultValue(3)
.require(positive())
.range(0, 100)
.build();
```
**Enumerated properties** are a bit less straightforward to define, though they are arguably more powerful. These properties don't have a specific value type, instead, you can choose any type of value, provided the values are from a closed set. To make that actionable, you give string labels to each of the acceptable values, and the user will provide one of those labels as a value in the XML. The property will give you back the associated value, not the label. Here's an example:
The {% jdoc props::constraints.NumericConstraints#positive() %} method is part of
the {% jdoc props::constraints.NumericConstraints %} class, which provides some
other constraints. The constraint mechanism will be completely unlocked with 7.0.0,
since we'll be migrating our API to Java 8.
**Enumerated properties** are a bit less straightforward to define, though they are
arguably more powerful. These properties don't have a specific value type, instead,
you can choose any type of value, provided the values are from a closed set. To make
that actionable, you give string labels to each of the acceptable values, and the user
will provide one of those labels as a value in the XML. The property will give you back
the associated value, not the label. Here's an example:
```java
static Map<String, ModeStrategy> map = new HashMap<>();
@@ -94,24 +109,25 @@ static {
map.put("hardMode", new HardStrategy());
}
static EnumeratedProperty<ModeStrategy> modeProperty
= EnumeratedProperty.<ModeStrategy>named("modeProperty")
.desc("This is my property")
.defaultValue(new EasyStrategy())
.mappings(map)
.type(ModeStrategy.class)
.build();
static PropertyDescriptor<ModeStrategy> modeProperty
= PropertyFactory.enumProperty("modeProperty", map)
.desc("This is my property")
.defaultValue(new EasyStrategy())
.build();
```
Note that you're required to fill in the type of the values too, using `type()`.
### Example
You can see an example of properties used in a PMD rule [here](https://github.com/pmd/pmd/blob/ac2ff0f6af8d16f739584ba8d00b7ea1a6311ccc/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/AvoidDeeplyNestedIfStmtsRule.java#L17).
You can see an example of properties used in a PMD rule [here](https://github.com/pmd/pmd/blob/5d86217871f086f8223da327099d1d67a6e45dab/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java#L41-L42).
There are several things to notice here:
* The property descriptor is declared `static final`, which should generally be the case, as descriptors are immutable and can be shared between instances of the same rule;
* The property is declared using `definePropertyDescriptor` *in the constructor*, which ensures the property gets recognised by PMD;
* The value of the property is *not retrieved in the constructor*, but in one of the `visit` methods (typically on the highest node in the tree, since the property doesn't change).
* The property descriptors are declared `static final`, which should generally be
the case, as descriptors are immutable and can be shared between instances of the same rule;
* The property is declared using {% jdoc props::PropertySource#definePropertyDescriptor(props::PropertyDescriptor) %}` *in the constructor*,
which ensures the property gets recognised by PMD at the time the properties
are overridden (which happens before rule execution);
* The value of the property is *not retrieved in the constructor*, but in one of
the `visit` methods (typically on the highest node in the tree, since the property
doesn't change).
@@ -119,21 +135,28 @@ There are several things to notice here:
XPath rules can also define their own properties. To do so, you must add a `property` element in the `properties` element of your rule, which **declares the `type` attribute**. This attribute conditions what type the underlying property has, and can have the following values:
| `type` attribute | Property type|
| `type` attribute | XSD type
|----------|----------|
|Integer|IntegerProperty
|Double | DoubleProperty
|Float|FloatProperty
|Long| LongProperty
|String|StringProperty
|Character|CharacterProperty
|Boolean|BooleanProperty
|Class|TypeProperty
|Regex|RegexProperty
|Integer | xs:integer
|Long | xs:integer
|Double | xs:decimal
|Boolean | xs:boolean
|String | xs:string
|Character| xs:string
|Regex | xs:string
{% include note.html
content="In XPath 1.0 mode, all values are actually represented as
string values, which is mostly fine as there is no type
checking. This is a problem when [migrating from XPath 1.0
to 2.0](pmd_userdocs_extending_writing_xpath_rules.html#migrating-from-10-to-20) though" %}
Note that enumerated properties are not available in XPath rules (yet?).
Properties defined in XPath also *must* declare the `description` attribute. Numeric properties also expect the `min` and `max` attributes. Here are a few examples to sum it up:
Properties defined in XPath also *must* declare the `description` attribute.
Numeric properties also expect the `min` and `max` attributes for now. Here are
a few examples to sum it up:
```xml
<property name="stringProp" type="Boolean" value="true" description="A BooleanProperty."/>
@@ -157,7 +180,12 @@ You can then use the property in XPath with the syntax `$propertyName`, for exam
### Multivalued properties
Multivalued properties are also allowed and their `type` attribute has the form `List[Boolean]` or `List[Character]`, with every above type allowed. These properties **require XPath 2.0** to work properly, and make use of the **sequence datatype** provided by that language. You thus need to set the `version` property to `2.0` to use them. Properties can also declare the `delimiter` attribute.
Multivalued properties are also allowed and their `type` attribute has the form
`List[Boolean]` or `List[Character]`, with every above type allowed. These
properties **require XPath 2.0** to work properly, and make use of the
**sequence datatype** provided by that language. You thus need to set the
`version` property to `2.0` to use them. Properties can also declare the
`delimiter` attribute.
@@ -176,5 +204,9 @@ Multivalued properties are also allowed and their `type` attribute has the form
</rule>
```
Notice that in the example above, `@Image = $reportedIdentifiers` doesn't test `@Image` for equality with the whole sequence `('foo', 'bar')`, it tests whether the sequence *contains* `@Image`. That is, the above rule will report all variables named `foo` or `bar`. All other XPath 2.0 [functions operating on sequences](https://www.w3.org/TR/xpath-functions/#sequence-functions) are supported.
Notice that in the example above, `@Image = $reportedIdentifiers` doesn't test
`@Image` for equality with the whole sequence `('foo', 'bar')`, it tests whether
the sequence *contains* `@Image`. That is, the above rule will report all variables
named `foo` or `bar`. All other XPath 2.0 [functions operating on sequences](https://www.w3.org/TR/xpath-functions/#sequence-functions)
are supported.
@@ -19,6 +19,8 @@ also [the tutorial about how to write an XPath rule](pmd_userdocs_extending_desi
<!-- Later we can document the specific subset of XPath features our wrappers support -->
<!-- TODO describe value representation quirks -->
## XPath version
PMD supports three XPath versions for now: 1.0, 2.0, and 1.0 compatibility mode.
@@ -86,8 +88,8 @@ On XPath 2.0, the namespace of custom PMD function must be explicitly mentioned.
{% include custom/xpath_fun_doc.html %}
{% endrender %}
There is also a `typeOf` function which is deprecated and whose usages
should be replaced with uses of `typeIs` or `typeIsExactly`. That one will
be removed with PMD 7.0.0.
{% include note.html content='There is also a `typeOf` function which is
deprecated and whose usages should be replaced with uses of `typeIs` or
`typeIsExactly`. That one will be removed with PMD 7.0.0.' %}