More mature design w/ small PoC

This commit is contained in:
Clément Fournier
2017-10-15 14:42:44 +02:00
parent aff5f20112
commit 2e272c3f7b
11 changed files with 485 additions and 256 deletions

View File

@@ -4,14 +4,12 @@
package net.sourceforge.pmd.properties;
import static net.sourceforge.pmd.properties.ValueParserConstants.LONG_PARSER;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import net.sourceforge.pmd.properties.builders.AbstractMultiNumericPropertyBuilder;
import static net.sourceforge.pmd.properties.ValueParserConstants.LONG_PARSER;
/**
* Multi-valued long property.
@@ -49,7 +47,6 @@ public final class LongMultiProperty extends AbstractMultiNumericProperty<Long>
* @param max Maximum value of the property
* @param defaultValues Array of defaults
* @param theUIOrder UI order
*
* @throws IllegalArgumentException if min > max or one of the defaults is not between the bounds
*/
public LongMultiProperty(String theName, String theDescription, Long min, Long max,
@@ -74,7 +71,6 @@ public final class LongMultiProperty extends AbstractMultiNumericProperty<Long>
* @param max Maximum value of the property
* @param defaultValues List of defaults
* @param theUIOrder UI order
*
* @throws IllegalArgumentException if min > max or one of the defaults is not between the bounds
*/
public LongMultiProperty(String theName, String theDescription, Long min, Long max,
@@ -95,21 +91,21 @@ public final class LongMultiProperty extends AbstractMultiNumericProperty<Long>
}
public static LongMultiPBuilder builder(String name) {
return new LongMultiPBuilder(name);
static PropertyDescriptor<List<Long>> extract(Map<PropertyDescriptorField, String> fields) {
return new PropertyBuilderConversionWrapper.MultiValue.Numeric<>(fields, ValueParserConstants.LONG_PARSER, new LongMultiPBuilder()).build();
}
private static final class LongMultiPBuilder
extends AbstractMultiNumericPropertyBuilder<Long, LongMultiPBuilder> {
private LongMultiPBuilder(String name) {
super(name);
}
public static LongMultiPBuilder builder(String name) {
return new LongMultiPBuilder().name(name);
}
private static final class LongMultiPBuilder
extends PropertyDescriptorBuilder.MultiValue.Numeric<Long, LongMultiPBuilder> {
@Override
public PropertyDescriptor<List<Long>> build() {
public LongMultiProperty build() {
return new LongMultiProperty(name, description, lowerLimit, upperLimit,
defaultValues, uiOrder, false);
}

View File

@@ -0,0 +1,99 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.properties;
import static net.sourceforge.pmd.properties.ValueParserConstants.CHARACTER_PARSER;
import java.util.List;
import java.util.Map;
/**
* Wraps a property builder and converts its inputs from strings to the target types of the descriptor.
*
* @param <E> Value type of the descriptor
* @param <T> Concrete type of the underlying builder
* @author Clément Fournier
* @since 6.0.0
*/
public abstract class PropertyBuilderConversionWrapper<E, T extends PropertyDescriptorBuilder<E, T>> {
protected final Map<PropertyDescriptorField, String> fields;
protected final T underlying;
protected PropertyBuilderConversionWrapper(Map<PropertyDescriptorField, String> fields, T builder) {
this.fields = fields;
this.underlying = builder;
}
/** Populates the builder with extracted fields. To be overridden. */
protected void populateBuilder() {
underlying.desc(fields.get(PropertyDescriptorField.DESCRIPTION));
underlying.uiOrder(Float.parseFloat(fields.get(PropertyDescriptorField.UI_ORDER)));
}
public PropertyDescriptor<E> build() {
return underlying.build();
}
/**
* For multi-value properties.
*
* @param <V> Element type of the list
* @param <T> Concrete type of the underlying builder
*/
public static class MultiValue<V, T extends PropertyDescriptorBuilder.MultiValue<V, T>>
extends PropertyBuilderConversionWrapper<List<V>, T> {
protected final ValueParser<V> parser;
protected MultiValue(Map<PropertyDescriptorField, String> fields,
ValueParser<V> parser, T builder) {
super(fields, builder);
this.parser = parser;
}
@Override
protected void populateBuilder() {
super.populateBuilder();
char delim = CHARACTER_PARSER.valueOf(fields.get(PropertyDescriptorField.DELIMITER));
underlying.delim(delim);
underlying.deft(ValueParserConstants.multi(parser, delim)
.valueOf(fields.get(PropertyDescriptorField.DEFAULT_VALUE)));
}
/**
* For multi-value numeric properties.
*
* @param <V> Element type of the list
* @param <T> Concrete type of the underlying builder
*/
public static class Numeric<V, T extends PropertyDescriptorBuilder.MultiValue.Numeric<V, T>>
extends MultiValue<V, T> {
protected Numeric(Map<PropertyDescriptorField, String> fields, ValueParser<V> parser, T builder) {
super(fields, parser, builder);
}
@Override
protected void populateBuilder() {
super.populateBuilder();
underlying.min(parser.valueOf(fields.get(PropertyDescriptorField.MIN)));
underlying.max(parser.valueOf(fields.get(PropertyDescriptorField.MAX)));
}
}
}
}

View File

@@ -0,0 +1,88 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.properties;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* @author Clément Fournier
* @since 6.0.0
*/
public class PropertyDescriptorBuildUtil {
private static final Map<String, PropertyDescriptorFactory<?>> DESCRIPTOR_FACTORIES_BY_TYPE;
static {
Map<String, PropertyDescriptorFactory<?>> temp = new HashMap<>(18);
temp.put("Boolean", BooleanProperty.FACTORY);
temp.put("List<Boolean>", BooleanMultiProperty.FACTORY);
temp.put("String", StringProperty.FACTORY);
temp.put("List<String>", StringMultiProperty.FACTORY);
temp.put("Character", CharacterProperty.FACTORY);
temp.put("List<Character>", CharacterMultiProperty.FACTORY);
temp.put("Integer", IntegerProperty.FACTORY);
temp.put("List<Integer>", IntegerMultiProperty.FACTORY);
temp.put("Long", LongProperty.FACTORY);
temp.put("List<Long>", LongMultiProperty.FACTORY);
temp.put("Float", FloatProperty.FACTORY);
temp.put("List<Float>", FloatMultiProperty.FACTORY);
temp.put("Double", DoubleProperty.FACTORY);
temp.put("List<Double>", DoubleMultiProperty.FACTORY);
// temp.put("Enum", EnumeratedProperty.FACTORY); // TODO:cf implement that
// temp.put("List<Enum>", EnumeratedMultiProperty.FACTORY);
temp.put("Class", TypeProperty.FACTORY);
temp.put("List<Class>", TypeMultiProperty.FACTORY);
temp.put("Method", MethodProperty.FACTORY);
temp.put("List<Method>", MethodMultiProperty.FACTORY);
temp.put("File", FileProperty.FACTORY);
DESCRIPTOR_FACTORIES_BY_TYPE = Collections.unmodifiableMap(temp);
}
private PropertyDescriptorBuildUtil() { }
/**
* Gets the factory for the descriptor identified by the string id.
*
* @param typeId The identifier of the type
*
* @return The factory used to build new instances of a descriptor
*/
public static PropertyDescriptorFactory<?> factoryFor(String typeId) {
return DESCRIPTOR_FACTORIES_BY_TYPE.get(typeId);
}
/**
* Gets the string representation of this type, as it should be given when defining a descriptor in the xml.
*
* @param valueType The type to look for
* @param multiValue Whether the descriptor is multivalued or not
*
* @return The type id
*/
public static String typeIdFor(Class<?> valueType, boolean multiValue) {
for (Map.Entry<String, PropertyDescriptorFactory<?>> entry : DESCRIPTOR_FACTORIES_BY_TYPE.entrySet()) {
if (entry.getValue().valueType() == valueType && entry.getValue().isMultiValue() == multiValue) {
return entry.getKey();
}
}
return null;
}
}

View File

@@ -0,0 +1,190 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.properties;
import java.util.List;
/**
* Base class for property builders.
*
* @param <E> Value type of the built descriptor
* @param <T> Concrete type of this builder instance. Removes code duplication at the expense of a few unchecked casts.
* Everything goes well if this parameter's value is correctly set.
* @author Clément Fournier
* @since 6.0.0
*/
public abstract class PropertyDescriptorBuilder<E, T extends PropertyDescriptorBuilder<E, T>> {
protected String name;
protected String description;
protected float uiOrder = 0f;
/**
* Specify the name of the property.
*
* @param name The name
* @return The same builder
*/
@SuppressWarnings("unchecked")
T name(String name) {
this.name = name;
return (T) this;
}
/**
* Specify the description of the property.
*
* @param desc The description
* @return The same builder
*/
@SuppressWarnings("unchecked")
public T desc(String desc) {
this.description = description;
return (T) this;
}
/**
* Specify the UI order of the property.
*
* @param f The UI order
* @return The same builder
*/
@SuppressWarnings("unchecked")
public T uiOrder(float f) {
this.uiOrder = f;
return (T) this;
}
/**
* Builds the descriptor and returns it.
*
* @return The built descriptor
*/
public abstract PropertyDescriptor<E> build();
/**
* For multi-value properties.
*
* @param <V> Element type of the list
* @param <T> Concrete type of the underlying builder
*/
public abstract static class MultiValue<V, T extends MultiValue<V, T>>
extends PropertyDescriptorBuilder<List<V>, T> {
protected List<V> defaultValues;
protected char multiValueDelimiter = MultiValuePropertyDescriptor.DEFAULT_DELIMITER;
/**
* Specify a default value.
*
* @param val List of values
* @return The same builder
*/
@SuppressWarnings("unchecked")
public T deft(List<V> val) {
this.defaultValues = val;
return (T) this;
}
/**
* Specify a delimiter character. By default it's {@link MultiValuePropertyDescriptor#DEFAULT_DELIMITER}, or
* {@link MultiValuePropertyDescriptor#DEFAULT_NUMERIC_DELIMITER} for numeric properties.
*
* @param delim Delimiter
* @return The same builder
*/
@SuppressWarnings("unchecked")
public T delim(char delim) {
this.multiValueDelimiter = delim;
return (T) this;
}
/**
* For multi-value numeric properties.
*
* @param <V> Element type of the list
* @param <T> Concrete type of the underlying builder
*/
public abstract static class Numeric<V, T extends Numeric<V, T>>
extends MultiValue<V, T> {
protected V lowerLimit;
protected V upperLimit;
protected Numeric() {
multiValueDelimiter = MultiValuePropertyDescriptor.DEFAULT_NUMERIC_DELIMITER;
}
/**
* Specify a minimum value.
*
* @param val Value
* @return The same builder
*/
@SuppressWarnings("unchecked")
public T min(V val) {
this.lowerLimit = val;
return (T) this;
}
/**
* Specify a maximum value.
*
* @param val Value
* @return The same builder
*/
@SuppressWarnings("unchecked")
public T max(V val) {
this.upperLimit = val;
return (T) this;
}
}
}
/**
* For single-value property descriptors.
*
* @param <E> Value type of the built descriptor
* @param <T> Concrete type of this builder instance.
*/
public abstract static class SingleValue<E, T extends SingleValue<E, T>>
extends PropertyDescriptorBuilder<E, T> {
protected E defaultValue;
/**
* Specify a default value.
*
* @param val Value
* @return The same builder
*/
@SuppressWarnings("unchecked")
public T deft(E val) {
this.defaultValue = val;
return (T) this;
}
}
}

View File

@@ -23,6 +23,8 @@ public enum PropertyDescriptorField {
NAME("name"),
/** The description of the property. */
DESCRIPTION("description"),
/** The UI order. */
UI_ORDER("uiOrder"),
/** The default value. */
DEFAULT_VALUE("value"),
/** For multi-valued properties, this defines the delimiter of the single values. */

View File

@@ -8,8 +8,6 @@ import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.properties.builders.AbstractSingleValuePropertyBuilder;
/**
* Defines a datatype that supports single String values.
@@ -73,15 +71,11 @@ public final class StringProperty extends AbstractSingleValueProperty<String> {
public static StringPBuilder builder(String name) {
return new StringPBuilder(name);
return new StringPBuilder().name(name);
}
private static final class StringPBuilder extends AbstractSingleValuePropertyBuilder<String, StringPBuilder> {
private StringPBuilder(String name) {
super(name);
}
private static final class StringPBuilder extends PropertyDescriptorBuilder.SingleValue<String, StringPBuilder> {
@Override

View File

@@ -4,10 +4,7 @@
package net.sourceforge.pmd.properties;
import static net.sourceforge.pmd.properties.modules.MethodPropertyModule.ARRAY_FLAG;
import static net.sourceforge.pmd.properties.modules.MethodPropertyModule.CLASS_METHOD_DELIMITER;
import static net.sourceforge.pmd.properties.modules.MethodPropertyModule.METHOD_ARG_DELIMITER;
import static net.sourceforge.pmd.properties.modules.MethodPropertyModule.METHOD_GROUP_DELIMITERS;
import static net.sourceforge.pmd.properties.modules.MethodPropertyModule.*;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
@@ -18,6 +15,7 @@ import org.apache.commons.lang3.StringUtils;
import net.sourceforge.pmd.util.ClassUtil;
/**
* @author Clément Fournier
* @since 6.0.0
@@ -26,94 +24,6 @@ import net.sourceforge.pmd.util.ClassUtil;
public final class ValueParserConstants {
/** Extracts characters. */
static final ValueParser<Character> CHARACTER_PARSER = new ValueParser<Character>() {
@Override
public Character valueOf(String value) {
if (value == null || value.length() != 1) {
throw new IllegalArgumentException("missing/ambiguous character value");
}
return value.charAt(0);
}
};
/** Extracts strings. That's a dummy used to return a list in StringMultiProperty. */
static final ValueParser<String> STRING_PARSER = new ValueParser<String>() {
@Override
public String valueOf(String value) {
return value;
}
};
/** Extracts integers. */
static final ValueParser<Integer> INTEGER_PARSER = new ValueParser<Integer>() {
@Override
public Integer valueOf(String value) {
return Integer.valueOf(value);
}
};
/** Extracts booleans. */
static final ValueParser<Boolean> BOOLEAN_PARSER = new ValueParser<Boolean>() {
@Override
public Boolean valueOf(String value) {
return Boolean.valueOf(value);
}
};
/** Extracts floats. */
static final ValueParser<Float> FLOAT_PARSER = new ValueParser<Float>() {
@Override
public Float valueOf(String value) {
return Float.valueOf(value);
}
};
/** Extracts longs. */
static final ValueParser<Long> LONG_PARSER = new ValueParser<Long>() {
@Override
public Long valueOf(String value) {
return Long.valueOf(value);
}
};
/** Extracts doubles. */
static final ValueParser<Double> DOUBLE_PARSER = new ValueParser<Double>() {
@Override
public Double valueOf(String value) {
return Double.valueOf(value);
}
};
/** Extract classes. */
static final ValueParser<Class> CLASS_PARSER = new ValueParser<Class>() {
@Override
public Class valueOf(String value) throws IllegalArgumentException {
if (StringUtils.isBlank(value)) {
return null;
}
Class<?> cls = ClassUtil.getTypeFor(value);
if (cls != null) {
return cls;
}
try {
return Class.forName(value);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(value);
}
}
};
/** Extracts methods. */
// TODO: make package-private when we move everything to n.s.pmd.properties
public static final ValueParser<Method> METHOD_PARSER = new ValueParser<Method>() {
@@ -223,6 +133,78 @@ public final class ValueParserConstants {
}
};
/** Extracts characters. */
static final ValueParser<Character> CHARACTER_PARSER = new ValueParser<Character>() {
@Override
public Character valueOf(String value) {
if (value == null || value.length() != 1) {
throw new IllegalArgumentException("missing/ambiguous character value");
}
return value.charAt(0);
}
};
/** Extracts strings. That's a dummy used to return a list in StringMultiProperty. */
static final ValueParser<String> STRING_PARSER = new ValueParser<String>() {
@Override
public String valueOf(String value) {
return value;
}
};
/** Extracts integers. */
static final ValueParser<Integer> INTEGER_PARSER = new ValueParser<Integer>() {
@Override
public Integer valueOf(String value) {
return Integer.valueOf(value);
}
};
/** Extracts booleans. */
static final ValueParser<Boolean> BOOLEAN_PARSER = new ValueParser<Boolean>() {
@Override
public Boolean valueOf(String value) {
return Boolean.valueOf(value);
}
};
/** Extracts floats. */
static final ValueParser<Float> FLOAT_PARSER = new ValueParser<Float>() {
@Override
public Float valueOf(String value) {
return Float.valueOf(value);
}
};
/** Extracts longs. */
static final ValueParser<Long> LONG_PARSER = new ValueParser<Long>() {
@Override
public Long valueOf(String value) {
return Long.valueOf(value);
}
};
/** Extracts doubles. */
static final ValueParser<Double> DOUBLE_PARSER = new ValueParser<Double>() {
@Override
public Double valueOf(String value) {
return Double.valueOf(value);
}
};
/** Extract classes. */
static final ValueParser<Class> CLASS_PARSER = new ValueParser<Class>() {
@Override
public Class valueOf(String value) throws IllegalArgumentException {
if (StringUtils.isBlank(value)) {
return null;
}
Class<?> cls = ClassUtil.getTypeFor(value);
if (cls != null) {
return cls;
}
try {
return Class.forName(value);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(value);
}
}
};
private ValueParserConstants() {
@@ -230,6 +212,25 @@ public final class ValueParserConstants {
}
/**
* Returns a value parser parsing lists of values of type U.
*
* @param parser Parser used to parse a single value
* @param delimiter Char delimiting values
* @param <U> Element type of the target list
*
* @return A list of values
*/
public static <U> ValueParser<List<U>> multi(final ValueParser<U> parser, final char delimiter) {
return new ValueParser<List<U>>() {
@Override
public List<U> valueOf(String value) throws IllegalArgumentException {
return parsePrimitives(value, delimiter, parser);
}
};
}
/**
* Parses a string into a list of values of type {@literal <U>}.
*

View File

@@ -1,35 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.properties.builders;
/**
* @author Clément Fournier
* @since 6.0.0
*/
public abstract class AbstractMultiNumericPropertyBuilder<V, T extends AbstractMultiNumericPropertyBuilder<V, T>>
extends AbstractMultiValuePropertyBuilder<V, T> {
protected V lowerLimit;
protected V upperLimit;
protected AbstractMultiNumericPropertyBuilder(String name) {
super(name);
}
public T min(V val) {
this.lowerLimit = val;
return (T) this;
}
public T max(V val) {
this.upperLimit = val;
return (T) this;
}
}

View File

@@ -1,39 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.properties.builders;
import java.util.List;
import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor;
/**
* @author Clément Fournier
* @since 6.0.0
*/
public abstract class AbstractMultiValuePropertyBuilder<V, T extends AbstractMultiValuePropertyBuilder<V, T>>
extends AbstractPropertyBuilder<List<V>, T> {
protected List<V> defaultValues;
protected char multiValueDelimiter = MultiValuePropertyDescriptor.DEFAULT_DELIMITER;
protected AbstractMultiValuePropertyBuilder(String name) {
super(name);
}
public T deft(List<V> val) {
this.defaultValues = val;
return (T) this;
}
public T delim(char delim) {
this.multiValueDelimiter = delim;
return (T) this;
}
}

View File

@@ -1,41 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.properties.builders;
import net.sourceforge.pmd.properties.PropertyDescriptor;
/**
* @author Clément Fournier
* @since 6.0.0
*/
public abstract class AbstractPropertyBuilder<E, T extends AbstractPropertyBuilder<E, T>> {
protected String name;
protected String description;
protected float uiOrder = 0f;
protected AbstractPropertyBuilder(String name) {
this.name = name;
}
public T description(String desc) {
this.description = description;
return (T) this;
}
public T uiOrder(float f) {
this.uiOrder = f;
return (T) this;
}
public abstract PropertyDescriptor<E> build();
}

View File

@@ -1,26 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.properties.builders;
/**
* @author Clément Fournier
* @since 6.0.0
*/
public abstract class AbstractSingleValuePropertyBuilder<E, T extends AbstractSingleValuePropertyBuilder<E, T>>
extends AbstractPropertyBuilder<E, T> {
protected E defaultValue;
protected AbstractSingleValuePropertyBuilder(String name) {
super(name);
}
public T deft(E val) {
this.defaultValue = val;
return (T) this;
}
}