From cb072600b6ec0194ba7fcb96e3a3feab41192ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 9 Apr 2020 18:56:52 +0200 Subject: [PATCH 001/347] Update some more usages --- .../pmd/lang/ecmascript/EcmascriptParserOptionsTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java index b343d5bbdc..1deff072d8 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java @@ -70,7 +70,6 @@ public class EcmascriptParserOptionsTest { @Test public void testEqualsHashcode() throws Exception { - @SuppressWarnings("unchecked") List> properties = listOf(EcmascriptParserOptions.RECORDING_COMMENTS_DESCRIPTOR, EcmascriptParserOptions.RECORDING_LOCAL_JSDOC_COMMENTS_DESCRIPTOR); From 714563cffdc9ac356564b60ac7605de5209ed896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 18:55:27 +0200 Subject: [PATCH 002/347] Remove transitional classes of property framework --- .../constraints/PropertyConstraint.java | 54 ++++++++----------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java index 31e89195a8..808582cd67 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.properties.constraints; -import java.util.ArrayList; -import java.util.List; import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; @@ -30,11 +28,6 @@ import net.sourceforge.pmd.annotation.Experimental; @Experimental public interface PropertyConstraint { - default boolean test(T t) { - return validate(t) == null; - } - - /** * Returns a diagnostic message if the value * has a problem. Otherwise returns an empty @@ -62,34 +55,12 @@ public interface PropertyConstraint { /** * Returns a constraint that validates a collection of Ts - * by checking each component conforms to this constraint. + * by checking each component conforms to this conforms. + * + * @return A collection validator */ @Experimental - default PropertyConstraint> toCollectionConstraint() { - return new PropertyConstraint>() { - private final PropertyConstraint itemConstraint = PropertyConstraint.this; - - @Override - public @Nullable String validate(Iterable value) { - List errors = new ArrayList<>(); - int i = 0; - for (T u : value) { - String err = itemConstraint.validate(u); - if (err != null) { - errors.add("Item " + i + " " + StringUtils.uncapitalize(err)); - } - i++; - } - - return errors.isEmpty() ? null : String.join("; ", errors); - } - - @Override - public String getConstraintDescription() { - return "Components " + StringUtils.uncapitalize(itemConstraint.getConstraintDescription()); - } - }; - } + PropertyConstraint> toCollectionConstraint(); /** @@ -120,6 +91,23 @@ public interface PropertyConstraint { public String getConstraintDescription() { return StringUtils.capitalize(constraintDescription); } + + + @Override + public PropertyConstraint> toCollectionConstraint() { + final PropertyConstraint thisValidator = this; + return fromPredicate( + us -> { + for (U u : us) { + if (!pred.test(u)) { + return false; + } + } + return true; + }, + "Components " + StringUtils.uncapitalize(thisValidator.getConstraintDescription()) + ); + } }; } From b3b5ecc63bcb53238a68cf0748651657b8fad8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 19:08:11 +0200 Subject: [PATCH 003/347] Cleanup ValueParser --- .../GenericMultiValuePropertyDescriptor.java | 1 + .../properties/GenericPropertyDescriptor.java | 1 + .../pmd/properties/PropertyBuilder.java | 1 + .../pmd/properties/PropertyTypeId.java | 1 + .../pmd/properties/ValueParserConstants.java | 97 ++++--------------- ...rtyDescriptorBuilderConversionWrapper.java | 2 +- .../{ => internal}/ValueParser.java | 20 ++-- 7 files changed, 38 insertions(+), 85 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{ => internal}/ValueParser.java (58%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java index 80f3ac0ef8..df74366109 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Set; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.internal.ValueParser; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 4cd39da265..c344648f04 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.properties; import java.util.Set; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.internal.ValueParser; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 222cc9f143..661e914c63 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.internal.ValueParser; // @formatter:off /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 7122fbb28a..c2098b2828 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -10,6 +10,7 @@ import java.util.Map; import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; +import net.sourceforge.pmd.properties.internal.ValueParser; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java index 8884cb156a..d510047155 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java @@ -9,11 +9,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.properties.internal.ValueParser; /** @@ -22,80 +24,33 @@ import net.sourceforge.pmd.annotation.InternalApi; * * @author Clรฉment Fournier * @since 6.0.0 - * @deprecated Was internal API */ -@Deprecated -@InternalApi public final class ValueParserConstants { /** Extracts characters. */ - static final ValueParser CHARACTER_PARSER = new ValueParser() { - @Override - public Character valueOf(String value) { - if (value == null || value.length() != 1) { - throw new IllegalArgumentException("missing/ambiguous character value"); - } - return value.charAt(0); + static final ValueParser CHARACTER_PARSER = value -> { + if (value == null || value.length() != 1) { + throw new IllegalArgumentException("missing/ambiguous character value for string \"" + value + "\""); } + return value.charAt(0); }; /** Extracts strings. That's a dummy used to return a list in StringMultiProperty. */ - static final ValueParser STRING_PARSER = new ValueParser() { - @Override - public String valueOf(String value) { - return value; - } - }; + static final ValueParser STRING_PARSER = value -> value; /** Extracts integers. */ - static final ValueParser INTEGER_PARSER = new ValueParser() { - @Override - public Integer valueOf(String value) { - return Integer.valueOf(value); - } - }; + static final ValueParser INTEGER_PARSER = Integer::valueOf; /** Extracts booleans. */ - static final ValueParser BOOLEAN_PARSER = new ValueParser() { - @Override - public Boolean valueOf(String value) { - return Boolean.valueOf(value); - } - }; + static final ValueParser BOOLEAN_PARSER = Boolean::valueOf; /** Extracts floats. */ - static final ValueParser FLOAT_PARSER = new ValueParser() { - @Override - public Float valueOf(String value) { - return Float.valueOf(value); - } - }; + static final ValueParser FLOAT_PARSER = Float::valueOf; /** Extracts longs. */ - static final ValueParser LONG_PARSER = new ValueParser() { - @Override - public Long valueOf(String value) { - return Long.valueOf(value); - } - }; + static final ValueParser LONG_PARSER = Long::valueOf; /** Extracts doubles. */ - static final ValueParser DOUBLE_PARSER = new ValueParser() { - @Override - public Double valueOf(String value) { - return Double.valueOf(value); - } - }; + static final ValueParser DOUBLE_PARSER = Double::valueOf; /** Extracts files */ - static final ValueParser FILE_PARSER = new ValueParser() { - @Override - public File valueOf(String value) throws IllegalArgumentException { - return new File(value); - } - }; - + static final ValueParser FILE_PARSER = File::new; /** Compiles a regex. */ - static final ValueParser REGEX_PARSER = new ValueParser() { - @Override - public Pattern valueOf(String value) throws IllegalArgumentException { - return Pattern.compile(value); - } - }; + static final ValueParser REGEX_PARSER = Pattern::compile; private ValueParserConstants() { @@ -109,14 +64,11 @@ public final class ValueParserConstants { throw new IllegalArgumentException("Map may not contain entries with null values"); } - return new ValueParser() { - @Override - public T valueOf(String value) throws IllegalArgumentException { - if (!mappings.containsKey(value)) { - throw new IllegalArgumentException("Value was not in the set " + mappings.keySet()); - } - return mappings.get(value); + return value -> { + if (!mappings.containsKey(value)) { + throw new IllegalArgumentException("Value was not in the set " + mappings.keySet()); } + return mappings.get(value); }; } @@ -131,12 +83,7 @@ public final class ValueParserConstants { * @return A list of values */ public static ValueParser> multi(final ValueParser parser, final char delimiter) { - return new ValueParser>() { - @Override - public List valueOf(String value) throws IllegalArgumentException { - return parsePrimitives(value, delimiter, parser); - } - }; + return value -> parsePrimitives(value, delimiter, parser); } @@ -150,13 +97,11 @@ public final class ValueParserConstants { * * @return A list of values */ - // FUTURE 1.8 : use java.util.function.Function in place of ValueParser, - // replace ValueParser constants with static functions - static List parsePrimitives(String toParse, char delimiter, ValueParser extractor) { + private static List parsePrimitives(String toParse, char delimiter, Function extractor) { String[] values = StringUtils.split(toParse, delimiter); List result = new ArrayList<>(); for (String s : values) { - result.add(extractor.valueOf(s)); + result.add(extractor.apply(s)); } return result; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java index d7e0ca7ef0..62b61f80a8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java @@ -16,7 +16,7 @@ import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; -import net.sourceforge.pmd.properties.ValueParser; +import net.sourceforge.pmd.properties.internal.ValueParser; import net.sourceforge.pmd.properties.ValueParserConstants; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueParser.java similarity index 58% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParser.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueParser.java index ba4dd796c3..c20c434b31 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueParser.java @@ -1,21 +1,25 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties; - -import net.sourceforge.pmd.annotation.InternalApi; +package net.sourceforge.pmd.properties.internal; +import java.util.function.Function; /** * Parses a value from a string. * * @param The type of the value to parse */ -// FUTURE @FunctionalInterface -@Deprecated -@InternalApi -public interface ValueParser { +@FunctionalInterface +public interface ValueParser extends Function { + + /** An alias for {@link #valueOf(String)}. */ + @Override + default U apply(String s) throws IllegalArgumentException { + return valueOf(s); + } + /** * Extracts a primitive from a string. From a788e7e40c508db1c8bbf5a1e7b21b2d9d58b7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 19:11:53 +0200 Subject: [PATCH 004/347] Remove some property types --- .../pmd/properties/AbstractProperty.java | 11 -- .../pmd/properties/FloatMultiProperty.java | 117 ----------------- .../pmd/properties/FloatProperty.java | 118 ------------------ .../GenericMultiValuePropertyDescriptor.java | 6 +- .../properties/GenericPropertyDescriptor.java | 6 +- .../pmd/properties/PropertyBuilder.java | 14 +-- .../pmd/properties/PropertyDescriptor.java | 21 +--- .../pmd/properties/PropertyTypeId.java | 32 ++--- .../pmd/properties/ValueParserConstants.java | 25 ++-- ...rtyDescriptorBuilderConversionWrapper.java | 18 +-- .../{ValueParser.java => StringParser.java} | 2 +- .../pmd/properties/FloatPropertyTest.java | 94 -------------- 12 files changed, 43 insertions(+), 421 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatMultiProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatProperty.java rename pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/{ValueParser.java => StringParser.java} (92%) delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/FloatPropertyTest.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java index f6946437ea..67bbd23ad8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java @@ -65,17 +65,6 @@ import org.apache.commons.lang3.StringUtils; } - @Override - public final int compareTo(PropertyDescriptor otherProperty) { - float otherOrder = otherProperty.uiOrder(); - return (int) (otherOrder - uiOrder); - } - - - @Override - public int preferredRowCount() { - return 1; - } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatMultiProperty.java deleted file mode 100644 index a35e56a9b3..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatMultiProperty.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Arrays; -import java.util.List; - -import net.sourceforge.pmd.properties.builders.MultiNumericPropertyBuilder; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; - - -/** - * Multi-valued float property. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor>} instead. A builder is available from {@link PropertyFactory#doubleListProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class FloatMultiProperty extends AbstractMultiNumericProperty { - - - /** - * Constructor using an array of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues Array of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated use {@link PropertyFactory#doubleListProperty(String)} - */ - @Deprecated - public FloatMultiProperty(String theName, String theDescription, Float min, Float max, - Float[] defaultValues, float theUIOrder) { - this(theName, theDescription, min, max, Arrays.asList(defaultValues), theUIOrder, false); - } - - - /** Master constructor. */ - private FloatMultiProperty(String theName, String theDescription, Float min, Float max, - List defaultValues, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, min, max, defaultValues, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues List of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated use {@link PropertyFactory#doubleListProperty(String)} - */ - @Deprecated - public FloatMultiProperty(String theName, String theDescription, Float min, Float max, - List defaultValues, float theUIOrder) { - this(theName, theDescription, min, max, defaultValues, theUIOrder, false); - } - - - @Override - public Class type() { - return Float.class; - } - - - @Override - protected Float createFrom(String value) { - return Float.valueOf(value); - } - - - static PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric(Float.class, ValueParserConstants.FLOAT_PARSER) { - @Override - protected FloatMultiPBuilder newBuilder(String name) { - return new FloatMultiPBuilder(name); - } - }; - } - - - /** @deprecated use {@link PropertyFactory#doubleListProperty(String)} */ - @Deprecated - public static FloatMultiPBuilder named(String name) { - return new FloatMultiPBuilder(name); - } - - - /** @deprecated use {@link PropertyFactory#doubleListProperty(String)} */ - @Deprecated - public static final class FloatMultiPBuilder extends MultiNumericPropertyBuilder { - private FloatMultiPBuilder(String name) { - super(name); - } - - - @Override - public FloatMultiProperty build() { - return new FloatMultiProperty(name, description, lowerLimit, upperLimit, defaultValues, uiOrder, isDefinedInXML); - } - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatProperty.java deleted file mode 100644 index c8a649b4ab..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FloatProperty.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * 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.FLOAT_PARSER; - -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; -import net.sourceforge.pmd.properties.builders.SingleNumericPropertyBuilder; - - -/** - * Defines a property type that supports single float property values within an upper and lower boundary. - * - * - * @deprecated Use {@link PropertyFactory#doubleProperty(String)} instead. This class will be removed with 7.0.0. - * @author Brian Remedios - */ -@Deprecated -public final class FloatProperty extends AbstractNumericProperty { - - - /** - * Constructor for FloatProperty that limits itself to a single value within the specified limits. Converts string - * arguments into the Float values. - * - * @param theName Name - * @param theDescription Description - * @param minStr Minimum value of the property - * @param maxStr Maximum value of the property - * @param defaultStr Default value - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#doubleProperty(String)} instead. - */ - @Deprecated - public FloatProperty(String theName, String theDescription, String minStr, String maxStr, String defaultStr, - float theUIOrder) { - this(theName, theDescription, FLOAT_PARSER.valueOf(minStr), - FLOAT_PARSER.valueOf(maxStr), FLOAT_PARSER.valueOf(defaultStr), theUIOrder, false); - } - - - /** Master constructor. */ - private FloatProperty(String theName, String theDescription, Float min, Float max, Float theDefault, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, min, max, theDefault, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor that limits itself to a single value within the specified limits. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param theDefault Default value - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#doubleProperty(String)} instead. - */ - @Deprecated - public FloatProperty(String theName, String theDescription, Float min, Float max, Float theDefault, - float theUIOrder) { - this(theName, theDescription, min, max, theDefault, theUIOrder, false); - } - - - @Override - public Class type() { - return Float.class; - } - - - @Override - protected Float createFrom(String value) { - return FLOAT_PARSER.valueOf(value); - } - - - static PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric(Float.class, ValueParserConstants.FLOAT_PARSER) { - @Override - protected FloatPBuilder newBuilder(String name) { - return new FloatPBuilder(name); - } - }; - } - - /** @deprecated Use {@link PropertyFactory#doubleProperty(String)} instead. */ - @Deprecated - public static FloatPBuilder named(String name) { - return new FloatPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#doubleProperty(String)} instead. - */ - @Deprecated - public static final class FloatPBuilder extends SingleNumericPropertyBuilder { - private FloatPBuilder(String name) { - super(name); - } - - - @Override - public FloatProperty build() { - return new FloatProperty(name, description, lowerLimit, upperLimit, defaultValue, uiOrder, isDefinedInXML); - } - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java index df74366109..90bdc663cf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Set; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.ValueParser; +import net.sourceforge.pmd.properties.internal.StringParser; /** @@ -25,14 +25,14 @@ final class GenericMultiValuePropertyDescriptor> exte private final Set> listValidators; - private final ValueParser parser; + private final StringParser parser; private final Class type; GenericMultiValuePropertyDescriptor(String name, String description, float uiOrder, Collection defaultValue, Set> listValidators, - ValueParser parser, + StringParser parser, char delim, Class type) { // this cast is safe until 7.0.0 diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index c344648f04..8f0889ab9d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.properties; import java.util.Set; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.ValueParser; +import net.sourceforge.pmd.properties.internal.StringParser; /** @@ -19,7 +19,7 @@ import net.sourceforge.pmd.properties.internal.ValueParser; final class GenericPropertyDescriptor extends AbstractSingleValueProperty { - private final ValueParser parser; + private final StringParser parser; private final Class type; private final Set> constraints; @@ -29,7 +29,7 @@ final class GenericPropertyDescriptor extends AbstractSingleValueProperty float uiOrder, T defaultValue, Set> constraints, - ValueParser parser, + StringParser parser, boolean isDefinedExternally, Class type) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 661e914c63..1fd59337e6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -18,7 +18,7 @@ import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.ValueParser; +import net.sourceforge.pmd.properties.internal.StringParser; // @formatter:off /** @@ -202,19 +202,19 @@ public abstract class PropertyBuilder, T> { // then the syntax should be the only one available. // This would allow specifying eg lists of numbers as 1,2,3, for which the syntax would look clumsy abstract static class BaseSinglePropertyBuilder, T> extends PropertyBuilder { - private final ValueParser parser; + private final StringParser parser; private final Class type; // Class is not final but a package-private constructor restricts inheritance - BaseSinglePropertyBuilder(String name, ValueParser parser, Class type) { + BaseSinglePropertyBuilder(String name, StringParser parser, Class type) { super(name); this.parser = parser; this.type = type; } - protected ValueParser getParser() { + protected StringParser getParser() { return parser; } @@ -293,7 +293,7 @@ public abstract class PropertyBuilder, T> { // Note: This type is used to fix the first type parameter for classes that don't need more API. public static final class GenericPropertyBuilder extends BaseSinglePropertyBuilder, T> { - GenericPropertyBuilder(String name, ValueParser parser, Class type) { + GenericPropertyBuilder(String name, StringParser parser, Class type) { super(name, parser, type); } } @@ -365,7 +365,7 @@ public abstract class PropertyBuilder, T> { * @since 6.10.0 */ public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { - private final ValueParser parser; + private final StringParser parser; private final Supplier emptyCollSupplier; private final Class type; private char multiValueDelimiter = MultiValuePropertyDescriptor.DEFAULT_DELIMITER; @@ -375,7 +375,7 @@ public abstract class PropertyBuilder, T> { * Builds a new builder for a collection type. Package-private. */ GenericCollectionPropertyBuilder(String name, - ValueParser parser, + StringParser parser, Supplier emptyCollSupplier, Class type) { super(name); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 9cc323678c..3af24bea91 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -25,7 +25,7 @@ import net.sourceforge.pmd.annotation.InternalApi; * @author Clรฉment Fournier * @version Refactored June 2017 (6.0.0) */ -public interface PropertyDescriptor extends Comparable> { +public interface PropertyDescriptor { /** * The name of the property without spaces as it serves as the key into the property map. @@ -108,14 +108,6 @@ public interface PropertyDescriptor extends Comparable> float uiOrder(); - /** - * @deprecated Comparing property descriptors is not useful within PMD - */ - @Deprecated - @Override - int compareTo(PropertyDescriptor o); - - /** * Returns the value represented by this string. * @@ -160,17 +152,6 @@ public interface PropertyDescriptor extends Comparable> String propertyErrorFor(Rule rule); - /** - * If the datatype is a String then return the preferred number of rows to allocate in the text widget, returns a - * value of one for all other types. Useful for multi-line XPATH editors. - * - * @deprecated Was never implemented, and is none of the descriptor's concern. Will be removed with 7.0.0 - * @return int - */ - @Deprecated - int preferredRowCount(); - - /** * Returns a map representing all the property attributes of the receiver in string form. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index c2098b2828..8d980a9ce9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -8,9 +8,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; -import net.sourceforge.pmd.properties.internal.ValueParser; +import net.sourceforge.pmd.properties.internal.StringParser; /** @@ -50,10 +49,6 @@ public enum PropertyTypeId { INTEGER_LIST("List[Integer]", IntegerMultiProperty.extractor(), ValueParserConstants.INTEGER_PARSER), LONG("Long", LongProperty.extractor(), ValueParserConstants.LONG_PARSER), LONG_LIST("List[Long]", LongMultiProperty.extractor(), ValueParserConstants.LONG_PARSER), - @Deprecated - FLOAT("Float", FloatProperty.extractor(), ValueParserConstants.FLOAT_PARSER), - @Deprecated - FLOAT_LIST("List[Float]", FloatMultiProperty.extractor(), ValueParserConstants.FLOAT_PARSER), DOUBLE("Double", DoubleProperty.extractor(), ValueParserConstants.DOUBLE_PARSER), DOUBLE_LIST("List[Double]", DoubleMultiProperty.extractor(), ValueParserConstants.DOUBLE_PARSER); @@ -61,7 +56,7 @@ public enum PropertyTypeId { private static final Map CONSTANTS_BY_MNEMONIC; private final String stringId; private final PropertyDescriptorExternalBuilder factory; - private final ValueParser valueParser; + private final StringParser stringParser; static { Map temp = new HashMap<>(); @@ -72,10 +67,10 @@ public enum PropertyTypeId { } - PropertyTypeId(String id, PropertyDescriptorExternalBuilder factory, ValueParser valueParser) { + PropertyTypeId(String id, PropertyDescriptorExternalBuilder factory, StringParser stringParser) { this.stringId = id; this.factory = factory; - this.valueParser = valueParser; + this.stringParser = stringParser; } @@ -101,19 +96,6 @@ public enum PropertyTypeId { } - /** - * Returns true if the property corresponding to this factory is numeric, - * which means it can be safely cast to a {@link NumericPropertyDescriptor}. - * - * @return whether the property is numeric - */ - @Deprecated - public boolean isPropertyNumeric() { - return factory instanceof PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric - || factory instanceof PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric; - } - - /** * Returns true if the property corresponding to this factory takes * lists of values as its value. @@ -145,15 +127,15 @@ public enum PropertyTypeId { /** * Gets the object used to parse the values of this property from a string. * If the property is multivalued, the parser only parses individual components - * of the list. A list parser can be obtained with {@link ValueParserConstants#multi(ValueParser, char)}. + * of the list. A list parser can be obtained with {@link ValueParserConstants#multi(StringParser, char)}. * * @return The value parser * * @deprecated see {@link PropertyDescriptor#valueFrom(String)} */ @Deprecated - public ValueParser getValueParser() { - return valueParser; + public StringParser getStringParser() { + return stringParser; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java index d510047155..e4010a6bfe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java @@ -14,8 +14,7 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.properties.internal.ValueParser; +import net.sourceforge.pmd.properties.internal.StringParser; /** @@ -29,28 +28,28 @@ public final class ValueParserConstants { /** Extracts characters. */ - static final ValueParser CHARACTER_PARSER = value -> { + static final StringParser CHARACTER_PARSER = value -> { if (value == null || value.length() != 1) { throw new IllegalArgumentException("missing/ambiguous character value for string \"" + value + "\""); } return value.charAt(0); }; /** Extracts strings. That's a dummy used to return a list in StringMultiProperty. */ - static final ValueParser STRING_PARSER = value -> value; + static final StringParser STRING_PARSER = value -> value; /** Extracts integers. */ - static final ValueParser INTEGER_PARSER = Integer::valueOf; + static final StringParser INTEGER_PARSER = Integer::valueOf; /** Extracts booleans. */ - static final ValueParser BOOLEAN_PARSER = Boolean::valueOf; + static final StringParser BOOLEAN_PARSER = Boolean::valueOf; /** Extracts floats. */ - static final ValueParser FLOAT_PARSER = Float::valueOf; + static final StringParser FLOAT_PARSER = Float::valueOf; /** Extracts longs. */ - static final ValueParser LONG_PARSER = Long::valueOf; + static final StringParser LONG_PARSER = Long::valueOf; /** Extracts doubles. */ - static final ValueParser DOUBLE_PARSER = Double::valueOf; + static final StringParser DOUBLE_PARSER = Double::valueOf; /** Extracts files */ - static final ValueParser FILE_PARSER = File::new; + static final StringParser FILE_PARSER = File::new; /** Compiles a regex. */ - static final ValueParser REGEX_PARSER = Pattern::compile; + static final StringParser REGEX_PARSER = Pattern::compile; private ValueParserConstants() { @@ -58,7 +57,7 @@ public final class ValueParserConstants { } - static ValueParser enumerationParser(final Map mappings) { + static StringParser enumerationParser(final Map mappings) { if (mappings.containsValue(null)) { throw new IllegalArgumentException("Map may not contain entries with null values"); @@ -82,7 +81,7 @@ public final class ValueParserConstants { * * @return A list of values */ - public static ValueParser> multi(final ValueParser parser, final char delimiter) { + public static StringParser> multi(final StringParser parser, final char delimiter) { return value -> parsePrimitives(value, delimiter, parser); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java index 62b61f80a8..e2114cf5f3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java @@ -16,7 +16,7 @@ import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; -import net.sourceforge.pmd.properties.internal.ValueParser; +import net.sourceforge.pmd.properties.internal.StringParser; import net.sourceforge.pmd.properties.ValueParserConstants; @@ -105,10 +105,10 @@ public abstract class PropertyDescriptorBuilderConversionWrapper> extends PropertyDescriptorBuilderConversionWrapper, T> { - protected final ValueParser parser; + protected final StringParser parser; - protected MultiValue(Class valueType, ValueParser parser) { + protected MultiValue(Class valueType, StringParser parser) { super(valueType); this.parser = parser; } @@ -138,7 +138,7 @@ public abstract class PropertyDescriptorBuilderConversionWrapper> extends MultiValue { - protected Numeric(Class valueType, ValueParser parser) { + protected Numeric(Class valueType, StringParser parser) { super(valueType, parser); } @@ -162,7 +162,7 @@ public abstract class PropertyDescriptorBuilderConversionWrapper> extends MultiValue { - protected Packaged(Class valueType, ValueParser parser) { + protected Packaged(Class valueType, StringParser parser) { super(valueType, parser); } @@ -187,10 +187,10 @@ public abstract class PropertyDescriptorBuilderConversionWrapper> extends PropertyDescriptorBuilderConversionWrapper { - protected final ValueParser parser; + protected final StringParser parser; - protected SingleValue(Class valueType, ValueParser parser) { + protected SingleValue(Class valueType, StringParser parser) { super(valueType); this.parser = parser; } @@ -218,7 +218,7 @@ public abstract class PropertyDescriptorBuilderConversionWrapper> extends SingleValue { - protected Numeric(Class valueType, ValueParser parser) { + protected Numeric(Class valueType, StringParser parser) { super(valueType, parser); } @@ -242,7 +242,7 @@ public abstract class PropertyDescriptorBuilderConversionWrapper> extends SingleValue { - protected Packaged(Class valueType, ValueParser parser) { + protected Packaged(Class valueType, StringParser parser) { super(valueType, parser); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/StringParser.java similarity index 92% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueParser.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/StringParser.java index c20c434b31..a2280eb02b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/StringParser.java @@ -12,7 +12,7 @@ import java.util.function.Function; * @param The type of the value to parse */ @FunctionalInterface -public interface ValueParser extends Function { +public interface StringParser extends Function { /** An alias for {@link #valueOf(String)}. */ @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/FloatPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/FloatPropertyTest.java deleted file mode 100644 index 4ebe325a7d..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/FloatPropertyTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - - -/** - * Evaluates the functionality of the FloatProperty descriptor by testing its - * ability to catch creation errors (illegal args), flag out-of-range test - * values, and serialize/deserialize groups of float values onto/from a string - * buffer. - * - * @author Brian Remedios - */ -public class FloatPropertyTest extends AbstractNumericPropertyDescriptorTester { - - private static final float MIN = 1.0f; - private static final float MAX = 11.0f; - private static final float SHIFT = 3.0f; - - - public FloatPropertyTest() { - super("Float"); - } - - - @Override - protected Float createValue() { - return randomFloat(MIN, MAX); - } - - - @Override - protected Float createBadValue() { - return randomBool() ? randomFloat(MIN - SHIFT, MIN) : randomFloat(MAX + 1, MAX + SHIFT); - } - - - @Override - protected FloatProperty.FloatPBuilder singleBuilder() { - return FloatProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValue(createValue()).uiOrder(1.0f); - } - - - @Override - protected FloatMultiProperty.FloatMultiPBuilder multiBuilder() { - return FloatMultiProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValues(createValue(), createValue()).uiOrder(1.0f); - } - - - @Override - protected PropertyDescriptor createProperty() { - return new FloatProperty("testFloat", "Test float property", MIN, MAX, 9.0f, 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new FloatMultiProperty("testFloat", "Test float property", MIN, MAX, - new Float[]{6f, 9f, 1f, 2f}, 1.0f); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new FloatProperty("testFloat", "Test float property", 5f, 4f, 9.0f, 1.0f); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new FloatMultiProperty("testFloat", "Test float property", 0f, 5f, - new Float[]{-1f, 0f, 1f, 2f}, 1.0f); - } - - - @Override - protected Float min() { - return MIN; - } - - - @Override - protected Float max() { - return MAX; - } - - -} From f01eb4cec72c8f5f21a67b14f4d3d8d11f646cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 19:28:16 +0200 Subject: [PATCH 005/347] Add value syntax --- .../properties/AbstractPropertySource.java | 2 - .../pmd/properties/PropertyBuilder.java | 19 +------ .../internal/parsers/SeqSyntax.java | 52 ++++++++++++++++++ .../internal/parsers/ValueSyntax.java | 53 +++++++++++++++++++ .../internal/parsers/XmlErrorReporter.java | 22 ++++++++ .../internal/parsers/XmlSyntax.java | 36 +++++++++++++ .../properties/internal/parsers/XmlUtils.java | 45 ++++++++++++++++ 7 files changed, 210 insertions(+), 19 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/SeqSyntax.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/ValueSyntax.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlErrorReporter.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlSyntax.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlUtils.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java index 3d7d7a3c95..6af714011b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java @@ -87,8 +87,6 @@ public abstract class AbstractPropertySource implements PropertySource { } propertyDescriptors.add(propertyDescriptor); - // Sort in UI order - Collections.sort(propertyDescriptors); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 1fd59337e6..bef345f3a6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -236,27 +236,12 @@ public abstract class PropertyBuilder, T> { */ /* package private */ GenericCollectionPropertyBuilder> toList() { - Supplier> listSupplier = new Supplier>() { - @Override - public List get() { - return new ArrayList<>(); - } - }; - - return toCollection(listSupplier); - } - - - // TODO 7.0.0 this can be inlined - private > GenericCollectionPropertyBuilder toCollection(Supplier emptyCollSupplier) { if (isDefaultValueSet()) { throw new IllegalStateException("The default value is already set!"); } - GenericCollectionPropertyBuilder result = new GenericCollectionPropertyBuilder<>(getName(), - getParser(), - emptyCollSupplier, - getType()); + GenericCollectionPropertyBuilder> result = + new GenericCollectionPropertyBuilder<>(getName(), getParser(), ArrayList::new, getType()); for (PropertyConstraint validator : getConstraints()) { result.require(validator.toCollectionConstraint()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/SeqSyntax.java new file mode 100644 index 0000000000..8180f85492 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/SeqSyntax.java @@ -0,0 +1,52 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.internal.parsers; + +import java.util.Collection; +import java.util.function.Supplier; + +import org.w3c.dom.Element; + +/** + * Serialize to and from a simple string. Examples: + * + *
{@code
+ *  1
+ * }
+ */ +public final class SeqSyntax> extends XmlSyntax { + + private final XmlSyntax itemSyntax; + private final Supplier emptyCollSupplier; + + SeqSyntax(XmlSyntax itemSyntax, Supplier emptyCollSupplier, String name) { + super(name); + this.itemSyntax = itemSyntax; + this.emptyCollSupplier = emptyCollSupplier; + } + + + @Override + public void toXml(Element container, C value) { + for (T v : value) { + Element item = container.getOwnerDocument().createElement(itemSyntax.getElementName()); + itemSyntax.toXml(item, v); + container.appendChild(item); + } + } + + @Override + public C fromXml(Element element, XmlErrorReporter err) { + C result = emptyCollSupplier.get(); + + XmlUtils.getElementChildren(element).forEach(child -> { + T item = XmlUtils.expectElement(err, child, itemSyntax); + if (item != null) { + result.add(item); + } + }); + return result; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/ValueSyntax.java new file mode 100644 index 0000000000..f105adc6e2 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/ValueSyntax.java @@ -0,0 +1,53 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.internal.parsers; + +import java.util.function.Function; + +import org.w3c.dom.Element; + +/** + * Serialize to and from a simple string. Examples: + * + *
{@code
+ *  1
+ *  someString
+ *  1,2,3
+ * }
+ */ +public final class ValueSyntax extends XmlSyntax { + + private final Function toString; + private final Function fromString; + + public ValueSyntax(Function toString, + Function fromString) { + super("value"); + this.toString = toString; + this.fromString = fromString; + } + + public final String toString(T t) { + return toString.apply(t); + } + + public final T fromString(String string) { + return fromString.apply(string); + } + + @Override + public void toXml(Element container, T value) { + container.setTextContent(toString.apply(value)); + } + + @Override + public T fromXml(Element element, XmlErrorReporter err) { + try { + return fromString.apply(element.getTextContent()); + } catch (IllegalArgumentException e) { + throw err.error(element, e); + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlErrorReporter.java new file mode 100644 index 0000000000..3a7f85b743 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlErrorReporter.java @@ -0,0 +1,22 @@ +package net.sourceforge.pmd.properties.internal.parsers; + +import org.w3c.dom.Node; + +/** + * Reports errors in an XML document. Implementations have a way to + * associate nodes with their location in the document. + * + * TODO this is an interface I ripped off another project. It's a placeholder for now + */ +public interface XmlErrorReporter { + + void warn(Node node, String message, Object... args); + + + RuntimeException error(Node node, String message, Object... args); + + + RuntimeException error(Node node, Throwable ex); + + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlSyntax.java new file mode 100644 index 0000000000..8e029a9fa1 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlSyntax.java @@ -0,0 +1,36 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.internal.parsers; + +import org.w3c.dom.Element; + + +/** + * Strategy to serialize a property to and from XML. + * + * @author Clรฉment Fournier + */ +public abstract class XmlSyntax { + + private final String eltName; + + /* package */ XmlSyntax(String eltName) { + this.eltName = eltName; + } + + /** Extract the value from an XML element. */ + public abstract T fromXml(Element element, XmlErrorReporter err); + + + /** Write the value into the given XML element. */ + public abstract void toXml(Element container, T value); + + + public final String getElementName() { + return eltName; + } + + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlUtils.java new file mode 100644 index 0000000000..10f1e6942a --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlUtils.java @@ -0,0 +1,45 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.internal.parsers; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public final class XmlUtils { + + private XmlUtils() { + + } + + public static List toList(NodeList lst) { + ArrayList nodes = new ArrayList<>(); + for (int i = 0; i < lst.getLength(); i++) { + nodes.add(lst.item(i)); + } + return nodes; + } + + static Stream getElementChildren(Element parent) { + return toList(parent.getChildNodes()).stream() + .filter(it -> it.getNodeType() == Node.ELEMENT_NODE) + .map(Element.class::cast); + } + + public static T expectElement(XmlErrorReporter err, Element elt, XmlSyntax syntax) { + + if (!elt.getTagName().equals(syntax.getElementName())) { + err.warn(elt, "Expecting an element with name '" + syntax.getElementName() + "'"); + } else { + return syntax.fromXml(elt, err); + } + + return null; + } +} From ceb856d9303c9af76e5028ae6d94974ebb8512ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 20:03:54 +0200 Subject: [PATCH 006/347] Remove package properties --- .../pmd/properties/FileProperty.java | 4 +- .../MultiPackagedPropertyBuilder.java | 36 ----------- ...rtyDescriptorBuilderConversionWrapper.java | 45 -------------- .../SinglePackagedPropertyBuilder.java | 59 ------------------- 4 files changed, 2 insertions(+), 142 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiPackagedPropertyBuilder.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SinglePackagedPropertyBuilder.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java index 7fc043e613..ec833682d2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java @@ -9,7 +9,7 @@ import java.io.File; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; -import net.sourceforge.pmd.properties.builders.SinglePackagedPropertyBuilder; +import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; /** @@ -69,7 +69,7 @@ public final class FileProperty extends AbstractSingleValueProperty { } - public static final class FilePBuilder extends SinglePackagedPropertyBuilder { + public static final class FilePBuilder extends SingleValuePropertyBuilder { private FilePBuilder(String name) { super(name); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiPackagedPropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiPackagedPropertyBuilder.java deleted file mode 100644 index e6d1c298ee..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiPackagedPropertyBuilder.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.builders; - -import java.util.Arrays; - - -/** - * @author Clรฉment Fournier - * @since 6.0.0 - * - * @deprecated see {@link net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder} - */ -@Deprecated -public abstract class MultiPackagedPropertyBuilder> - extends MultiValuePropertyBuilder { - - protected String[] legalPackageNames; - - - protected MultiPackagedPropertyBuilder(String name) { - super(name); - } - - - @SuppressWarnings("unchecked") - public T legalPackages(String[] packs) { - if (packs != null) { - this.legalPackageNames = Arrays.copyOf(packs, packs.length); - } - return (T) this; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java index e2114cf5f3..5f6c13ae35 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java @@ -153,28 +153,6 @@ public abstract class PropertyDescriptorBuilderConversionWrapper Element type of the list - * @param Concrete type of the underlying builder - */ - public abstract static class Packaged> - extends MultiValue { - - protected Packaged(Class valueType, StringParser parser) { - super(valueType, parser); - } - - - @Override - protected void populate(T builder, Map fields) { - super.populate(builder, fields); - builder.legalPackages(legalPackageNamesIn(fields, PropertyDescriptorBuilderConversionWrapper.delimiterIn(fields, - MultiValuePropertyDescriptor.DEFAULT_DELIMITER))); - } - } - } @@ -233,29 +211,6 @@ public abstract class PropertyDescriptorBuilderConversionWrapper Element type of the list - * @param Concrete type of the underlying builder - */ - public abstract static class Packaged> - extends SingleValue { - - protected Packaged(Class valueType, StringParser parser) { - super(valueType, parser); - } - - - @Override - protected void populate(T builder, Map fields) { - super.populate(builder, fields); - builder.legalPackageNames(legalPackageNamesIn(fields, PropertyDescriptorBuilderConversionWrapper.delimiterIn(fields, - MultiValuePropertyDescriptor.DEFAULT_DELIMITER))); - } - } - - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SinglePackagedPropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SinglePackagedPropertyBuilder.java deleted file mode 100644 index 5f253e9cc3..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SinglePackagedPropertyBuilder.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.builders; - -import java.util.Arrays; -import java.util.Collection; - - -/** - * @author Clรฉment Fournier - * @since 6.0.0 - * @deprecated see {@link net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder} - */ -@Deprecated -public abstract class SinglePackagedPropertyBuilder> - extends SingleValuePropertyBuilder { - - protected String[] legalPackageNames; - - - public SinglePackagedPropertyBuilder(String name) { - super(name); - } - - - /** - * Specify the allowed package prefixes. - * - * @param packs The package prefixes - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T legalPackageNames(String... packs) { - if (packs != null) { - this.legalPackageNames = Arrays.copyOf(packs, packs.length); - } - return (T) this; - } - - - /** - * Specify the allowed package prefixes. - * - * @param packs The package prefixes - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T legalPackageNames(Collection packs) { - if (packs != null) { - this.legalPackageNames = packs.toArray(new String[0]); - } - return (T) this; - } - -} From f5080d3d0f02325d7c7857c9b785c7cd4ebe1bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 20:23:23 +0200 Subject: [PATCH 007/347] Port ecmascript parser options --- .../properties/EnumeratedMultiProperty.java | 209 ------------------ .../pmd/properties/PropertyFactory.java | 44 +++- .../SimpleEnumeratedPropertyTest.java | 159 ------------- 3 files changed, 37 insertions(+), 375 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedMultiProperty.java delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/SimpleEnumeratedPropertyTest.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedMultiProperty.java deleted file mode 100644 index bf27ee573c..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedMultiProperty.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import net.sourceforge.pmd.properties.builders.MultiValuePropertyBuilder; -import net.sourceforge.pmd.properties.modules.EnumeratedPropertyModule; -import net.sourceforge.pmd.util.CollectionUtil; - - -/** - * Multi-valued property which can take only a fixed set of values of any type, then selected via String labels. The - * mappings method returns the set of mappings between the labels and their values. - * - * @param The type of the values - * - * @author Brian Remedios - * @author Clรฉment Fournier - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor>} instead. A builder is available from {@link PropertyFactory#enumListProperty(String, Map)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class EnumeratedMultiProperty extends AbstractMultiValueProperty - implements EnumeratedPropertyDescriptor> { - - - private final EnumeratedPropertyModule module; - - - /** - * Constructor using arrays to define the label-value mappings. The correct construction of the property depends on - * the correct ordering of the arrays. - * - * @param theName Name - * @param theDescription Description - * @param theLabels Labels of the choices - * @param theChoices Values that can be chosen - * @param choiceIndices Indices of the default values - * @param valueType Type of the values - * @param theUIOrder UI order - * - * @deprecated Use {@link PropertyFactory#enumListProperty(String, Map)} - */ - @Deprecated - public EnumeratedMultiProperty(String theName, String theDescription, String[] theLabels, E[] theChoices, - int[] choiceIndices, Class valueType, float theUIOrder) { - this(theName, theDescription, CollectionUtil.mapFrom(theLabels, theChoices), - selection(choiceIndices, theChoices), valueType, theUIOrder, false); - } - - - /** - * Constructor using arrays to define the label-value mappings. The correct construction of the property depends on - * the correct ordering of the arrays. - * - * @param theName Name - * @param theDescription Description - * @param theLabels Labels of the choices - * @param theChoices Values that can be chosen - * @param choiceIndices Indices of the default values - * @param theUIOrder UI order - * - * @deprecated Use {@link PropertyFactory#enumListProperty(String, Map)} - */ - @Deprecated - public EnumeratedMultiProperty(String theName, String theDescription, String[] theLabels, E[] theChoices, - int[] choiceIndices, float theUIOrder) { - this(theName, theDescription, CollectionUtil.mapFrom(theLabels, theChoices), - selection(choiceIndices, theChoices), null, theUIOrder, false); - } - - - /** - * Constructor using a map to define the label-value mappings. The default values are specified with a list. - * - * @param theName Name - * @param theDescription Description - * @param choices Map of labels to values - * @param defaultValues List of default values - * @param valueType Type of the values - * @param theUIOrder UI order - * @deprecated Use {@link PropertyFactory#enumListProperty(String, Map)} - */ - @Deprecated - public EnumeratedMultiProperty(String theName, String theDescription, Map choices, - List defaultValues, Class valueType, float theUIOrder) { - this(theName, theDescription, choices, defaultValues, valueType, theUIOrder, false); - } - - - private EnumeratedMultiProperty(String theName, String theDescription, Map choices, - List defaultValues, Class valueType, float theUIOrder, - boolean isDefinedExternally) { - super(theName, theDescription, defaultValues, theUIOrder, isDefinedExternally); - - module = new EnumeratedPropertyModule<>(choices, valueType); - checkDefaults(defaultValues); - } - - - @Override - public Map mappings() { - return module.getChoicesByLabel(); // unmodifiable - } - - - @Override - public Class type() { - return module.getValueType(); - } - - - @Override - public String errorFor(List values) { - for (E value : values) { - String error = module.errorFor(value); - if (error != null) { - return error; - } - } - return null; - } - - - @Override - protected E createFrom(String toParse) { - return module.choiceFrom(toParse); - } - - - @Override - public String asString(E item) { - return module.getLabelsByChoice().get(item); - } - - - private void checkDefaults(List defaults) { - for (E elt : defaults) { - module.checkValue(elt); - } - } - - - private static List selection(int[] choiceIndices, E[] theChoices) { - List selected = new ArrayList<>(); - for (int i : choiceIndices) { - if (i < 0 || i > theChoices.length) { - throw new IllegalArgumentException("Default value index is out of bounds: " + i); - } - selected.add(theChoices[i]); - } - return selected; - } - - - /** - * @deprecated Use {@link PropertyFactory#enumListProperty(String, Map)} - */ - @Deprecated - public static EnumMultiPBuilder named(String name) { - return new EnumMultiPBuilder<>(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#enumListProperty(String, Map)} - */ - @Deprecated - public static final class EnumMultiPBuilder extends MultiValuePropertyBuilder> { - - private Class valueType; - private Map mappings; - - - private EnumMultiPBuilder(String name) { - super(name); - } - - public EnumMultiPBuilder type(Class type) { - this.valueType = type; - return this; - } - - /** - * Sets the key-value mappings. - * - * @param map A map of label to value - * - * @return The same builder - */ - public EnumMultiPBuilder mappings(Map map) { - this.mappings = map; - return this; - } - - - @Override - public EnumeratedMultiProperty build() { - return new EnumeratedMultiProperty<>(this.name, this.description, mappings, this.defaultValues, valueType, this.uiOrder, isDefinedInXML); - } - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 94654fef2b..0979406b3e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -4,8 +4,20 @@ package net.sourceforge.pmd.properties; +import static net.sourceforge.pmd.properties.ValueParserConstants.BOOLEAN_PARSER; +import static net.sourceforge.pmd.properties.ValueParserConstants.CHARACTER_PARSER; +import static net.sourceforge.pmd.properties.ValueParserConstants.DOUBLE_PARSER; +import static net.sourceforge.pmd.properties.ValueParserConstants.INTEGER_PARSER; +import static net.sourceforge.pmd.properties.ValueParserConstants.LONG_PARSER; +import static net.sourceforge.pmd.properties.ValueParserConstants.STRING_PARSER; +import static net.sourceforge.pmd.properties.ValueParserConstants.enumerationParser; + +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; + +import org.apache.commons.lang3.EnumUtils; import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; @@ -97,7 +109,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder intProperty(String name) { - return new GenericPropertyBuilder<>(name, ValueParserConstants.INTEGER_PARSER, Integer.class); + return new GenericPropertyBuilder<>(name, INTEGER_PARSER, Integer.class); } @@ -134,7 +146,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder longIntProperty(String name) { - return new GenericPropertyBuilder<>(name, ValueParserConstants.LONG_PARSER, Long.class); + return new GenericPropertyBuilder<>(name, LONG_PARSER, Long.class); } @@ -166,7 +178,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder doubleProperty(String name) { - return new GenericPropertyBuilder<>(name, ValueParserConstants.DOUBLE_PARSER, Double.class); + return new GenericPropertyBuilder<>(name, DOUBLE_PARSER, Double.class); } @@ -214,7 +226,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder stringProperty(String name) { - return new GenericPropertyBuilder<>(name, ValueParserConstants.STRING_PARSER, String.class); + return new GenericPropertyBuilder<>(name, STRING_PARSER, String.class); } @@ -245,7 +257,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder charProperty(String name) { - return new GenericPropertyBuilder<>(name, ValueParserConstants.CHARACTER_PARSER, Character.class); + return new GenericPropertyBuilder<>(name, CHARACTER_PARSER, Character.class); } @@ -272,7 +284,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder booleanProperty(String name) { - return new GenericPropertyBuilder<>(name, ValueParserConstants.BOOLEAN_PARSER, Boolean.class); + return new GenericPropertyBuilder<>(name, BOOLEAN_PARSER, Boolean.class); } // We can add more useful factories with Java 8. @@ -301,7 +313,25 @@ public final class PropertyFactory { // TODO find solution to document the set of possible values // At best, map that requirement to a constraint (eg make parser return null if not found, and // add a non-null constraint with the right description.) - return new GenericPropertyBuilder<>(name, ValueParserConstants.enumerationParser(nameToValue), (Class) Object.class); + return new GenericPropertyBuilder<>(name, enumerationParser(nameToValue), (Class) Object.class); + } + + public static > GenericPropertyBuilder enumProperty(String name, Class enumClass) { + return new GenericPropertyBuilder<>( + name, + enumerationParser(EnumUtils.getEnumMap(enumClass)), + enumClass + ); + } + + public static > GenericPropertyBuilder enumProperty(String name, Class enumClass, Function labelMaker) { + Map labels = new HashMap<>(); + for (T constant : enumClass.getEnumConstants()) { + labels.put(labelMaker.apply(constant), constant); + } + + + return new GenericPropertyBuilder<>(name, enumerationParser(labels), enumClass); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/SimpleEnumeratedPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/SimpleEnumeratedPropertyTest.java deleted file mode 100644 index b890cc89ea..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/SimpleEnumeratedPropertyTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Assume; -import org.junit.Test; - -import net.sourceforge.pmd.properties.SimpleEnumeratedPropertyTest.Foo; - -/** - * Evaluates the functionality of the EnumeratedProperty descriptor by testing - * its ability to catch creation errors (illegal args), flag invalid selections, - * and serialize/deserialize selection options. - * - * @author Brian Remedios - */ -@Deprecated -public class SimpleEnumeratedPropertyTest extends AbstractPropertyDescriptorTester { - - private static final String[] KEYS = {"bar", "na", "bee", "coo"}; - private static final Foo[] VALUES = {Foo.BAR, Foo.NA, Foo.BEE, Foo.COO}; - private static final Map MAPPINGS; - - - static { - Map map = new HashMap<>(); - map.put("bar", Foo.BAR); - map.put("na", Foo.NA); - map.put("bee", Foo.BEE); - map.put("coo", Foo.COO); - MAPPINGS = Collections.unmodifiableMap(map); - } - - - public SimpleEnumeratedPropertyTest() { - super("Enum"); - } - - - @Test - public void testMappings() { - EnumeratedPropertyDescriptor prop - = (EnumeratedPropertyDescriptor) createProperty(); - EnumeratedPropertyDescriptor> multi - = (EnumeratedPropertyDescriptor>) createMultiProperty(); - - assertEquals(MAPPINGS, prop.mappings()); - assertEquals(MAPPINGS, multi.mappings()); - } - - - @Override - protected PropertyDescriptor createProperty() { - return new EnumeratedProperty<>("testEnumerations", - "Test enumerations with complex types", - KEYS, - VALUES, 0, Foo.class, - 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new EnumeratedMultiProperty<>("testEnumerations", - "Test enumerations with complex types", - KEYS, - VALUES, - new int[] {0, 1}, Foo.class, 1.0f); - } - - - @Test(expected = IllegalArgumentException.class) - public void testDefaultIndexOutOfBounds() { - new EnumeratedMultiProperty<>("testEnumerations", "Test enumerations with simple type", - KEYS, VALUES, new int[] {99}, Foo.class, 1.0f); - } - - - @Test(expected = IllegalArgumentException.class) - public void testNoMappingForDefault() { - new EnumeratedMultiProperty<>("testEnumerations", "Test enumerations with simple type", - MAPPINGS, Collections.singletonList(Foo.IGNORED), Foo.class, 1.0f); - - } - - - @Test - public void creationTest() { - PropertyDescriptor prop = createProperty(); - PropertyDescriptor> multi = createMultiProperty(); - - for (Map.Entry e : MAPPINGS.entrySet()) { - assertEquals(e.getValue(), prop.valueFrom(e.getKey())); - assertTrue(multi.valueFrom(e.getKey()).contains(e.getValue())); - } - } - - - @Override - protected Foo createValue() { - return randomChoice(VALUES); - } - - - @Override - protected Foo createBadValue() { - return Foo.IGNORED; // not in the set of values - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new EnumeratedProperty<>("testEnumerations", "Test enumerations with simple type", - new String[0], VALUES, -1, Foo.class, 1.0f); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new EnumeratedMultiProperty<>("testEnumerations", "Test enumerations with simple type", - KEYS, VALUES, new int[] {99}, Foo.class, 1.0f); - } - - - @Override - @Test - public void testFactorySingleValue() { - Assume.assumeTrue("The EnumeratedProperty factory is not implemented yet", false); - } - - - @Override - @Test - public void testFactoryMultiValueCustomDelimiter() { - Assume.assumeTrue("The EnumeratedProperty factory is not implemented yet", false); - } - - - @Override - @Test - public void testFactoryMultiValueDefaultDelimiter() { - Assume.assumeTrue("The EnumeratedProperty factory is not implemented yet", false); - } - - - enum Foo { - BAR, NA, BEE, COO, IGNORED - } -} From bb4c7b7a45cbfa9cbaef98a29a0dad18680c75f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 20:24:09 +0200 Subject: [PATCH 008/347] Remove enum properties --- .../pmd/properties/EnumeratedProperty.java | 175 ------------------ .../EnumeratedPropertyDescriptor.java | 31 ---- .../pmd/properties/PropertyTypeId.java | 3 +- ...rtyDescriptorBuilderConversionWrapper.java | 6 - .../modules/EnumeratedPropertyModule.java | 73 -------- 5 files changed, 2 insertions(+), 286 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedPropertyDescriptor.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/EnumeratedPropertyModule.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedProperty.java deleted file mode 100644 index 9d709a287b..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedProperty.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Map; - -import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; -import net.sourceforge.pmd.properties.modules.EnumeratedPropertyModule; -import net.sourceforge.pmd.util.CollectionUtil; - - -/** - * Property which can take only a fixed set of values of any type, then selected via String labels. The mappings method - * returns the set of mappings between the labels and their values. - * - *

This property currently doesn't support serialization and cannot be defined in a ruleset file.z

- * - * @param Type of the values - * - * @author Brian Remedios - * @author Clรฉment Fournier - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor} instead. A builder is available from {@link PropertyFactory#enumProperty(String, Map)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class EnumeratedProperty extends AbstractSingleValueProperty - implements EnumeratedPropertyDescriptor { - - private final EnumeratedPropertyModule module; - - - /** - * Constructor using arrays to define the label-value mappings. The correct construction of the property depends on - * the correct ordering of the arrays. - * - * @param theName Name - * @param theDescription Description - * @param theLabels Labels of the choices - * @param theChoices Values that can be chosen - * @param defaultIndex The index of the default value - * @param valueType Type of the values - * @param theUIOrder UI order - * - * @deprecated Use {@link PropertyFactory#enumProperty(String, Map)} - */ - @Deprecated - public EnumeratedProperty(String theName, String theDescription, String[] theLabels, E[] theChoices, - int defaultIndex, Class valueType, float theUIOrder) { - this(theName, theDescription, CollectionUtil.mapFrom(theLabels, theChoices), - theChoices[defaultIndex], valueType, theUIOrder, false); - } - - - /** - * Constructor using arrays to define the label-value mappings. The correct construction of the property depends on - * the correct ordering of the arrays. - * - * @param theName Name - * @param theDescription Description - * @param theLabels Labels of the choices - * @param theChoices Values that can be chosen - * @param defaultIndex Index of the default value - * @param theUIOrder UI order - * - * @deprecated Use {@link PropertyFactory#enumProperty(String, Map)} - */ - @Deprecated - public EnumeratedProperty(String theName, String theDescription, String[] theLabels, E[] theChoices, - int defaultIndex, float theUIOrder) { - this(theName, theDescription, CollectionUtil.mapFrom(theLabels, theChoices), - theChoices[defaultIndex], null, theUIOrder, false); - } - - - /** - * Constructor using a map to define the label-value mappings. - * - * @param theName Name - * @param theDescription Description - * @param labelsToChoices Map of labels to values - * @param defaultValue Default value - * @param valueType Type of the values - * @param theUIOrder UI order - * @deprecated Use {@link PropertyFactory#enumProperty(String, Map)} - */ - @Deprecated - public EnumeratedProperty(String theName, String theDescription, Map labelsToChoices, - E defaultValue, Class valueType, float theUIOrder) { - this(theName, theDescription, labelsToChoices, defaultValue, valueType, theUIOrder, false); - } - - - /** Master constructor. */ - private EnumeratedProperty(String theName, String theDescription, Map labelsToChoices, - E defaultValue, Class valueType, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, defaultValue, theUIOrder, isDefinedExternally); - - module = new EnumeratedPropertyModule<>(labelsToChoices, valueType); - module.checkValue(defaultValue); - } - - - @Override - public Class type() { - return module.getValueType(); - } - - - @Override - public String errorFor(E value) { - return module.errorFor(value); - } - - - @Override - public E createFrom(String value) throws IllegalArgumentException { - return module.choiceFrom(value); - } - - - @Override - public String asString(E value) { - return module.getLabelsByChoice().get(value); - } - - - @Override - public Map mappings() { - return module.getChoicesByLabel(); // unmodifiable - } - - - /** - * @deprecated Use {@link PropertyFactory#enumProperty(String, Map)} - */ - @Deprecated - public static EnumPBuilder named(String name) { - return new EnumPBuilder<>(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#enumProperty(String, Map)} - */ - @Deprecated - public static final class EnumPBuilder extends SingleValuePropertyBuilder> { - - private Class valueType; - private Map mappings; - - - private EnumPBuilder(String name) { - super(name); - } - - public EnumPBuilder type(Class type) { - this.valueType = type; - return this; - } - - public EnumPBuilder mappings(Map map) { - this.mappings = map; - return this; - } - - - @Override - public EnumeratedProperty build() { - return new EnumeratedProperty<>(this.name, this.description, mappings, this.defaultValue, valueType, this.uiOrder, isDefinedInXML); - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedPropertyDescriptor.java deleted file mode 100644 index b8d17f99bd..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/EnumeratedPropertyDescriptor.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Map; - - -/** - * Interface defining an enumerated property descriptor. - * - * @param The type of the values - * @param The type of default values the descriptor can take (can be a List) - * - * @author Clรฉment Fournier - * @since 6.0.0 - * @deprecated Will be removed with 7.0.0. In the future this interface won't exist, - * but enumerated properties will still be around - */ -@Deprecated -public interface EnumeratedPropertyDescriptor extends PropertyDescriptor { - - /** - * Returns an immutable map of the label - value mappings defined by this descriptor. - * - * @return an immutable map of the label - value mappings defined by this descriptor. - */ - Map mappings(); - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 8d980a9ce9..ae72cab0b4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -50,7 +50,8 @@ public enum PropertyTypeId { LONG("Long", LongProperty.extractor(), ValueParserConstants.LONG_PARSER), LONG_LIST("List[Long]", LongMultiProperty.extractor(), ValueParserConstants.LONG_PARSER), DOUBLE("Double", DoubleProperty.extractor(), ValueParserConstants.DOUBLE_PARSER), - DOUBLE_LIST("List[Double]", DoubleMultiProperty.extractor(), ValueParserConstants.DOUBLE_PARSER); + DOUBLE_LIST("List[Double]", DoubleMultiProperty.extractor(), ValueParserConstants.DOUBLE_PARSER), + ; private static final Map CONSTANTS_BY_MNEMONIC; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java index 5f6c13ae35..5df12be389 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java @@ -73,12 +73,6 @@ public abstract class PropertyDescriptorBuilderConversionWrapper valuesById, char delimiter) { - String names = valuesById.get(LEGAL_PACKAGES); - return StringUtils.isBlank(names) ? null : StringUtils.split(names, delimiter); - } - - private static char delimiterIn(Map valuesById, char defalt) { String characterStr = ""; if (valuesById.containsKey(DELIMITER)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/EnumeratedPropertyModule.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/EnumeratedPropertyModule.java deleted file mode 100644 index 9836ceecb6..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/EnumeratedPropertyModule.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.modules; - -import java.util.Collections; -import java.util.Map; - -import net.sourceforge.pmd.util.CollectionUtil; - - -/** - * Factorises common functionality for enumerated properties. - * - * @author Clรฉment Fournier - */ -@Deprecated -public class EnumeratedPropertyModule { - - private final Map choicesByLabel; - private final Map labelsByChoice; - private final Class valueType; - - - public EnumeratedPropertyModule(Map choicesByLabel, Class valueType) { - this.valueType = valueType; - this.choicesByLabel = Collections.unmodifiableMap(choicesByLabel); - this.labelsByChoice = Collections.unmodifiableMap(CollectionUtil.invertedMapFrom(choicesByLabel)); - } - - - public Class getValueType() { - return valueType; - } - - - public Map getLabelsByChoice() { - return labelsByChoice; - } - - - public Map getChoicesByLabel() { - return choicesByLabel; - } - - - private String nonLegalValueMsgFor(E value) { - return value + " is not a legal value"; - } - - - public String errorFor(E value) { - return labelsByChoice.containsKey(value) ? null : nonLegalValueMsgFor(value); - } - - - public E choiceFrom(String label) { - E result = choicesByLabel.get(label); - if (result != null) { - return result; - } - throw new IllegalArgumentException(label); - } - - - public void checkValue(E value) { - if (!choicesByLabel.containsValue(value)) { - throw new IllegalArgumentException("Invalid default value: no mapping to this value"); - } - } - -} From 4f8b79d91954b46932afb2d985704ef90e63d11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 20:48:47 +0200 Subject: [PATCH 009/347] Add syntax set --- .../internal/{parsers => }/SeqSyntax.java | 2 +- .../pmd/properties/internal/SyntaxSet.java | 76 +++++++++++++++++++ .../internal/{parsers => }/ValueSyntax.java | 2 +- .../{parsers => }/XmlErrorReporter.java | 6 +- .../internal/{parsers => }/XmlSyntax.java | 5 +- .../internal/{parsers => }/XmlUtils.java | 2 +- 6 files changed, 86 insertions(+), 7 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/{parsers => }/SeqSyntax.java (95%) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java rename pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/{parsers => }/ValueSyntax.java (95%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/{parsers => }/XmlErrorReporter.java (77%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/{parsers => }/XmlSyntax.java (86%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/{parsers => }/XmlUtils.java (95%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java similarity index 95% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/SeqSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java index 8180f85492..64506eabab 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal.parsers; +package net.sourceforge.pmd.properties.internal; import java.util.Collection; import java.util.function.Supplier; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java new file mode 100644 index 0000000000..f1466d28d2 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -0,0 +1,76 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.internal; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.w3c.dom.Element; + +/** + * A set of syntaxes for read and write. One special syntax is designated + * as the one used to write elements, the others are used to read. + */ +public final class SyntaxSet extends XmlSyntax { + + private final XmlSyntax forWrite; + private final Map> readIndex; + + public SyntaxSet(XmlSyntax forWrite, Collection> forRead) { + super(forWrite.getElementName()); + this.forWrite = forWrite; + + if (forRead.isEmpty()) { + throw new IllegalArgumentException("Empty set of reads strategies!"); + } + + + this.readIndex = forRead.stream().collect(Collectors.toMap( + XmlSyntax::getElementName, + it -> it, + (a, b) -> { + // merge function + throw new IllegalArgumentException( + "Duplicate name '" + a.getElementName() + "', for syntaxes " + a + " and " + b + ); + } + )); + } + + @Override + public T fromXml(Element element, XmlErrorReporter err) { + XmlSyntax syntax = readIndex.get(element.getTagName()); + if (syntax == null) { + throw err.error( + element, + "Unexpected element name " + enquote(element.getTagName()) + ", expecting " + formatPossibilities() + ); + } else { + return syntax.fromXml(element, err); + } + } + + @Override + public void toXml(Element container, T value) { + forWrite.toXml(container, value); + } + + // nullable + private String formatPossibilities() { + Set strings = readIndex.keySet(); + if (strings.isEmpty()) { + return null; + } else if (strings.size() == 1) { + return enquote(strings.iterator().next()); + } else { + return "one of " + strings.stream().map(SyntaxSet::enquote).collect(Collectors.joining(", ")); + } + } + + private static String enquote(String it) {return "'" + it + "'";} + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java similarity index 95% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/ValueSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index f105adc6e2..f76b65d373 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal.parsers; +package net.sourceforge.pmd.properties.internal; import java.util.function.Function; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java similarity index 77% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlErrorReporter.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java index 3a7f85b743..e28a4fd022 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java @@ -1,4 +1,8 @@ -package net.sourceforge.pmd.properties.internal.parsers; +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.internal; import org.w3c.dom.Node; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java similarity index 86% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java index 8e029a9fa1..7ab1f007a5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal.parsers; +package net.sourceforge.pmd.properties.internal; import org.w3c.dom.Element; @@ -27,10 +27,9 @@ public abstract class XmlSyntax { /** Write the value into the given XML element. */ public abstract void toXml(Element container, T value); - + /** Get the preferred name used to write elements. */ public final String getElementName() { return eltName; } - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java similarity index 95% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlUtils.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java index 10f1e6942a..888ad193f3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/parsers/XmlUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal.parsers; +package net.sourceforge.pmd.properties.internal; import java.util.ArrayList; import java.util.List; From c169e501219b6c8c18cea73c532bc3b015b1dbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 20:56:33 +0200 Subject: [PATCH 010/347] Add examples --- .../pmd/properties/internal/SeqSyntax.java | 8 ++++++++ .../pmd/properties/internal/SyntaxSet.java | 11 ++++++++++- .../pmd/properties/internal/ValueSyntax.java | 5 +++++ .../pmd/properties/internal/XmlSyntax.java | 6 ++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java index 64506eabab..7c340fac96 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java @@ -49,4 +49,12 @@ public final class SeqSyntax> extends XmlSyntax { }); return result; } + + @Override + public String example() { + return "<" + getElementName() + ">\n" + + " " + itemSyntax.toString() + "\n" + + " ..." + + ""; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index f1466d28d2..03ea1dc3cc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.properties.internal; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -37,7 +38,8 @@ public final class SyntaxSet extends XmlSyntax { throw new IllegalArgumentException( "Duplicate name '" + a.getElementName() + "', for syntaxes " + a + " and " + b ); - } + }, + LinkedHashMap::new )); } @@ -73,4 +75,11 @@ public final class SyntaxSet extends XmlSyntax { private static String enquote(String it) {return "'" + it + "'";} + @Override + public String example() { + if (readIndex.size() == 1) { + return readIndex.values().iterator().next().example(); + } + return "One of:\n" + readIndex.values().stream().map(XmlSyntax::example).collect(Collectors.joining("\nor\n")); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index f76b65d373..c26c4fdfe2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -50,4 +50,9 @@ public final class ValueSyntax extends XmlSyntax { throw err.error(element, e); } } + + @Override + public String example() { + return "<" + getElementName() + ">data"; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java index 7ab1f007a5..c8ceaafb2c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java @@ -32,4 +32,10 @@ public abstract class XmlSyntax { return eltName; } + public abstract String example(); + + @Override + public String toString() { + return example(); + } } From 1de32ef047002c39c5a3e40380a799ec4f714235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 21:18:04 +0200 Subject: [PATCH 011/347] Better doc support --- .../pmd/properties/internal/SeqSyntax.java | 6 +-- .../pmd/properties/internal/SyntaxSet.java | 53 +++++++++++++++++-- .../pmd/properties/internal/ValueSyntax.java | 51 +++++++++++++----- .../pmd/properties/internal/XmlSyntax.java | 29 +++++++++- .../pmd/properties/internal/XmlUtils.java | 4 +- 5 files changed, 120 insertions(+), 23 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java index 7c340fac96..aade41c415 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java @@ -31,7 +31,7 @@ public final class SeqSyntax> extends XmlSyntax { @Override public void toXml(Element container, C value) { for (T v : value) { - Element item = container.getOwnerDocument().createElement(itemSyntax.getElementName()); + Element item = container.getOwnerDocument().createElement(itemSyntax.getWriteElementName()); itemSyntax.toXml(item, v); container.appendChild(item); } @@ -52,9 +52,9 @@ public final class SeqSyntax> extends XmlSyntax { @Override public String example() { - return "<" + getElementName() + ">\n" + return "<" + getWriteElementName() + ">\n" + " " + itemSyntax.toString() + "\n" + " ..." - + ""; + + ""; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index 03ea1dc3cc..8800f47e5c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -6,12 +6,16 @@ package net.sourceforge.pmd.properties.internal; import java.util.Collection; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; +import net.sourceforge.pmd.util.CollectionUtil; + /** * A set of syntaxes for read and write. One special syntax is designated * as the one used to write elements, the others are used to read. @@ -21,8 +25,21 @@ public final class SyntaxSet extends XmlSyntax { private final XmlSyntax forWrite; private final Map> readIndex; - public SyntaxSet(XmlSyntax forWrite, Collection> forRead) { - super(forWrite.getElementName()); + /** + * @param newSyntax Newer syntax (eg seq) + * @param compat Value syntax (eg delimited string for sequence) + */ + public SyntaxSet(XmlSyntax newSyntax, ValueSyntax compat, boolean preferNew) { + // the set here prunes duplicates + this(preferNew ? newSyntax : compat, CollectionUtil.setOf(newSyntax, compat)); + } + + /** + * @param forWrite Designated strategy for writing + * @param forRead Set of supported syntaxes, must have pairwise different read names + */ + private SyntaxSet(XmlSyntax forWrite, Collection> forRead) { + super(forWrite.getWriteElementName(), readNames(forRead)); this.forWrite = forWrite; if (forRead.isEmpty()) { @@ -31,18 +48,40 @@ public final class SyntaxSet extends XmlSyntax { this.readIndex = forRead.stream().collect(Collectors.toMap( - XmlSyntax::getElementName, + XmlSyntax::getWriteElementName, it -> it, (a, b) -> { // merge function throw new IllegalArgumentException( - "Duplicate name '" + a.getElementName() + "', for syntaxes " + a + " and " + b + "Duplicate name '" + a.getWriteElementName() + "', for syntaxes " + a + " and " + b ); }, LinkedHashMap::new )); } + @Override + public @Nullable T fromString(Element owner, String attributeData, XmlErrorReporter err) { + + for (XmlSyntax syntax : supportedReadStrategies()) { + if (syntax.supportsFromString()) { + // do not catch any exception, it will already have been reported on the error reporter. + return syntax.fromString(owner, attributeData, err); + } + } + + throw new UnsupportedOperationException(); + } + + public Set> supportedReadStrategies() { + return new LinkedHashSet<>(readIndex.values()); + } + + @Override + public boolean supportsFromString() { + return supportedReadStrategies().stream().anyMatch(XmlSyntax::supportsFromString); + } + @Override public T fromXml(Element element, XmlErrorReporter err) { XmlSyntax syntax = readIndex.get(element.getTagName()); @@ -73,6 +112,12 @@ public final class SyntaxSet extends XmlSyntax { } } + private static Set readNames(Collection> syntaxes) { + return syntaxes.stream() + .flatMap(it -> it.getSupportedReadElementNames().stream()) + .collect(Collectors.toSet()); + } + private static String enquote(String it) {return "'" + it + "'";} @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index c26c4fdfe2..a1865051c3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -4,8 +4,13 @@ package net.sourceforge.pmd.properties.internal; +import java.util.Collection; import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; /** @@ -16,25 +21,30 @@ import org.w3c.dom.Element; * someString * 1,2,3 * } + * + *
This class is special because it enables compatibility with the
+ * pre 7.0.0 XML syntax.
  */
 public final class ValueSyntax extends XmlSyntax {
 
+    private static final String VALUE_NAME = "value";
     private final Function toString;
     private final Function fromString;
 
     public ValueSyntax(Function toString,
                        Function fromString) {
-        super("value");
+        super(VALUE_NAME);
         this.toString = toString;
         this.fromString = fromString;
     }
 
-    public final String toString(T t) {
-        return toString.apply(t);
-    }
-
-    public final T fromString(String string) {
-        return fromString.apply(string);
+    @Override
+    public @Nullable T fromString(Element owner, String attributeData, XmlErrorReporter err) {
+        try {
+            return fromString.apply(attributeData);
+        } catch (IllegalArgumentException e) {
+            throw err.error(owner, e);
+        }
     }
 
     @Override
@@ -44,15 +54,30 @@ public final class ValueSyntax extends XmlSyntax {
 
     @Override
     public T fromXml(Element element, XmlErrorReporter err) {
-        try {
-            return fromString.apply(element.getTextContent());
-        } catch (IllegalArgumentException e) {
-            throw err.error(element, e);
-        }
+        return fromString(element, element.getTextContent(), err);
     }
 
     @Override
     public String example() {
-        return "<" + getElementName() + ">data";
+        return "<" + getWriteElementName() + ">data";
+    }
+
+    public static > ValueSyntax delimitedString(
+        Function toString,
+        Function fromString,
+        String delimiter,
+        Supplier emptyCollSupplier
+    ) {
+
+        return new ValueSyntax<>(
+            coll -> coll.stream().map(toString).collect(Collectors.joining(delimiter)),
+            string -> {
+                C coll = emptyCollSupplier.get();
+                for (String item : string.split(Pattern.quote(delimiter))) {
+                    coll.add(fromString.apply(item));
+                }
+                return coll;
+            }
+        );
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
index c8ceaafb2c..c0c0570e68 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
@@ -4,6 +4,9 @@
 
 package net.sourceforge.pmd.properties.internal;
 
+import java.util.Collections;
+import java.util.Set;
+
 import org.w3c.dom.Element;
 
 
@@ -15,9 +18,15 @@ import org.w3c.dom.Element;
 public abstract class XmlSyntax {
 
     private final String eltName;
+    private final Set readNames;
 
     /* package */ XmlSyntax(String eltName) {
+        this(eltName, Collections.singleton(eltName));
+    }
+
+    /* package */ XmlSyntax(String eltName, Set readNames) {
         this.eltName = eltName;
+        this.readNames = readNames;
     }
 
     /** Extract the value from an XML element. */
@@ -27,11 +36,29 @@ public abstract class XmlSyntax {
     /** Write the value into the given XML element. */
     public abstract void toXml(Element container, T value);
 
+    public boolean supportsFromString() {
+        return false;
+    }
+
+    /**
+     * Read the value from a string, returns null if unsupported.
+     * @throws UnsupportedOperationException if unsupported
+     * @throws IllegalArgumentException if something goes wrong (but should report on the error reporter)
+     */
+    public T fromString(Element owner, String attributeData, XmlErrorReporter err) {
+        throw new UnsupportedOperationException();
+    }
+
+
     /** Get the preferred name used to write elements. */
-    public final String getElementName() {
+    public final String getWriteElementName() {
         return eltName;
     }
 
+    public final Set getSupportedReadElementNames() {
+        return readNames;
+    }
+
     public abstract String example();
 
     @Override
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java
index 888ad193f3..d644867c5c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java
@@ -34,8 +34,8 @@ public final class XmlUtils {
 
     public static  T expectElement(XmlErrorReporter err, Element elt, XmlSyntax syntax) {
 
-        if (!elt.getTagName().equals(syntax.getElementName())) {
-            err.warn(elt, "Expecting an element with name '" + syntax.getElementName() + "'");
+        if (!elt.getTagName().equals(syntax.getWriteElementName())) {
+            err.warn(elt, "Expecting an element with name '" + syntax.getWriteElementName() + "'");
         } else {
             return syntax.fromXml(elt, err);
         }

From 6d41176e412644ffbbcca54bc8ddbcc4e2e37f0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Thu, 12 Sep 2019 22:54:16 +0200
Subject: [PATCH 012/347] Use XML syntax to parse

---
 .../pmd/properties/PropertyDescriptor.java    | 11 +++
 .../properties/PropertyDescriptorField.java   | 19 ++++
 .../pmd/properties/PropertyTypeId.java        | 56 +++++------
 .../pmd/properties/internal/SeqSyntax.java    |  4 +-
 .../pmd/properties/internal/ValueSyntax.java  | 48 ++++------
 .../properties/internal/XmlErrorReporter.java | 12 ++-
 .../pmd/properties/internal/XmlSyntax.java    |  8 +-
 .../properties/internal/XmlSyntaxUtils.java   | 95 +++++++++++++++++++
 .../sourceforge/pmd/rules/RuleFactory.java    | 59 ++++++++----
 9 files changed, 227 insertions(+), 85 deletions(-)
 create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
index 3af24bea91..c7245af529 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
@@ -9,6 +9,8 @@ import java.util.Map;
 import net.sourceforge.pmd.Rule;
 import net.sourceforge.pmd.RuleSetWriter;
 import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.properties.internal.ValueSyntax;
+import net.sourceforge.pmd.properties.internal.XmlSyntax;
 
 
 /**
@@ -51,6 +53,15 @@ public interface PropertyDescriptor {
     T defaultValue();
 
 
+    /**
+     * Returns the strategy used to read and write this property to XML.
+     * May support strings too.
+     */
+    default XmlSyntax xmlStrategy() {
+        return new ValueSyntax<>(this::asDelimitedString, this::valueFrom);
+    }
+
+
     /**
      * Validation function that returns a diagnostic error message for a sample property value. Returns null if the
      * value is acceptable.
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java
index 8a01f07572..7d76f24a5a 100755
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java
@@ -6,7 +6,12 @@ package net.sourceforge.pmd.properties;
 
 import java.util.Objects;
 
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.w3c.dom.Element;
+
 import net.sourceforge.pmd.RuleSetFactory;
+import net.sourceforge.pmd.properties.internal.XmlErrorReporter;
 
 
 /**
@@ -53,6 +58,20 @@ public enum PropertyDescriptorField {
         this.attributeName = attributeName;
     }
 
+    @NonNull
+    public String getOrThrow(Element element, XmlErrorReporter err) {
+        String attribute = element.getAttribute(attributeName);
+        if (attribute == null) {
+            throw err.error(element, "Missing attribute '" + attributeName + "'");
+        }
+
+        return attribute;
+    }
+
+    @Nullable
+    public String getOptional(Element element) {
+        return element.getAttribute(attributeName);
+    }
 
     /**
      * Returns the String name of this attribute.
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java
index ae72cab0b4..2e789b4e2e 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java
@@ -7,9 +7,12 @@ package net.sourceforge.pmd.properties;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Function;
 
 import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder;
 import net.sourceforge.pmd.properties.internal.StringParser;
+import net.sourceforge.pmd.properties.internal.XmlSyntax;
+import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils;
 
 
 /**
@@ -33,31 +36,28 @@ public enum PropertyTypeId {
     // property types around XML Schema Datatypes (XSD) 1.0 or 1.1 instead of Java datatypes (save for
     // e.g. the Class type), including the mnemonics (eg. xs:integer instead of Integer)
 
-    BOOLEAN("Boolean", BooleanProperty.extractor(), ValueParserConstants.BOOLEAN_PARSER),
-    /** @deprecated see {@link BooleanMultiProperty} */
-    @Deprecated
-    BOOLEAN_LIST("List[Boolean]", BooleanMultiProperty.extractor(), ValueParserConstants.BOOLEAN_PARSER),
+    BOOLEAN("Boolean", XmlSyntaxUtils.BOOLEAN, PropertyFactory::booleanProperty),
+    STRING("String", XmlSyntaxUtils.STRING, PropertyFactory::stringProperty),
+    STRING_LIST("List[String]", XmlSyntaxUtils.STRING_LIST, PropertyFactory::stringListProperty),
+    CHARACTER("Character", XmlSyntaxUtils.CHARACTER, PropertyFactory::charProperty),
+    CHARACTER_LIST("List[Character]", XmlSyntaxUtils.CHAR_LIST, PropertyFactory::charListProperty),
 
-    STRING("String", StringProperty.extractor(), ValueParserConstants.STRING_PARSER),
-    STRING_LIST("List[String]", StringMultiProperty.extractor(), ValueParserConstants.STRING_PARSER),
-    CHARACTER("Character", CharacterProperty.extractor(), ValueParserConstants.CHARACTER_PARSER),
-    CHARACTER_LIST("List[Character]", CharacterMultiProperty.extractor(), ValueParserConstants.CHARACTER_PARSER),
+    REGEX("Regex", XmlSyntaxUtils.REGEX, PropertyFactory::regexProperty),
 
-    REGEX("Regex", RegexProperty.extractor(), ValueParserConstants.REGEX_PARSER),
-
-    INTEGER("Integer", IntegerProperty.extractor(), ValueParserConstants.INTEGER_PARSER),
-    INTEGER_LIST("List[Integer]", IntegerMultiProperty.extractor(), ValueParserConstants.INTEGER_PARSER),
-    LONG("Long", LongProperty.extractor(), ValueParserConstants.LONG_PARSER),
-    LONG_LIST("List[Long]", LongMultiProperty.extractor(), ValueParserConstants.LONG_PARSER),
-    DOUBLE("Double", DoubleProperty.extractor(), ValueParserConstants.DOUBLE_PARSER),
-    DOUBLE_LIST("List[Double]", DoubleMultiProperty.extractor(), ValueParserConstants.DOUBLE_PARSER),
+    INTEGER("Integer", XmlSyntaxUtils.INTEGER, PropertyFactory::intProperty),
+    INTEGER_LIST("List[Integer]", XmlSyntaxUtils.INTEGER_LIST, PropertyFactory::intListProperty),
+    LONG("Long", XmlSyntaxUtils.LONG, PropertyFactory::longIntProperty),
+    LONG_LIST("List[Long]", XmlSyntaxUtils.LONG_LIST, PropertyFactory::longIntListProperty),
+    DOUBLE("Double", XmlSyntaxUtils.DOUBLE, PropertyFactory::doubleProperty),
+    DOUBLE_LIST("List[Double]", XmlSyntaxUtils.DOUBLE_LIST, PropertyFactory::doubleListProperty),
     ;
 
 
     private static final Map CONSTANTS_BY_MNEMONIC;
     private final String stringId;
-    private final PropertyDescriptorExternalBuilder factory;
-    private final StringParser stringParser;
+    private final XmlSyntax xmlSyntax;
+    private final Function> factory;
+
 
     static {
         Map temp = new HashMap<>();
@@ -68,10 +68,14 @@ public enum PropertyTypeId {
     }
 
 
-    PropertyTypeId(String id, PropertyDescriptorExternalBuilder factory, StringParser stringParser) {
+     PropertyTypeId(String id, XmlSyntax syntax, Function> factory) {
         this.stringId = id;
+        this.xmlSyntax = syntax;
         this.factory = factory;
-        this.stringParser = stringParser;
+    }
+
+    public XmlSyntax getXmlSyntax() {
+        return xmlSyntax;
     }
 
 
@@ -85,18 +89,10 @@ public enum PropertyTypeId {
     }
 
 
-    /**
-     * Gets the factory associated to the type id, that can build the
-     * property from strings extracted from the XML.
-     *
-     * @return The factory
-     */
-    @Deprecated
-    public PropertyDescriptorExternalBuilder getFactory() {
-        return factory;
+    public PropertyBuilder newBuilder(String name) {
+        return factory.apply(name);
     }
 
-
     /**
      * Returns true if the property corresponding to this factory takes
      * lists of values as its value.
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java
index aade41c415..83e20d9d3f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java
@@ -21,8 +21,8 @@ public final class SeqSyntax> extends XmlSyntax {
     private final XmlSyntax itemSyntax;
     private final Supplier emptyCollSupplier;
 
-    SeqSyntax(XmlSyntax itemSyntax, Supplier emptyCollSupplier, String name) {
-        super(name);
+    SeqSyntax(XmlSyntax itemSyntax, Supplier emptyCollSupplier) {
+        super("seq");
         this.itemSyntax = itemSyntax;
         this.emptyCollSupplier = emptyCollSupplier;
     }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java
index a1865051c3..b7f2da816f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java
@@ -4,11 +4,9 @@
 
 package net.sourceforge.pmd.properties.internal;
 
-import java.util.Collection;
+import java.util.Objects;
+import java.util.function.BiFunction;
 import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 import org.w3c.dom.Element;
@@ -38,13 +36,19 @@ public final class ValueSyntax extends XmlSyntax {
         this.fromString = fromString;
     }
 
+    public ValueSyntax(Function fromString) {
+        super(VALUE_NAME);
+        this.toString = Objects::toString;
+        this.fromString = fromString;
+    }
+
     @Override
-    public @Nullable T fromString(Element owner, String attributeData, XmlErrorReporter err) {
-        try {
-            return fromString.apply(attributeData);
-        } catch (IllegalArgumentException e) {
-            throw err.error(owner, e);
-        }
+    public T fromString(String attributeData) {
+        return fromString.apply(attributeData);
+    }
+
+    public String toString(T data) {
+        return toString.apply(data);
     }
 
     @Override
@@ -54,7 +58,11 @@ public final class ValueSyntax extends XmlSyntax {
 
     @Override
     public T fromXml(Element element, XmlErrorReporter err) {
-        return fromString(element, element.getTextContent(), err);
+        try {
+            return fromString.apply(element.getTextContent());
+        } catch (IllegalArgumentException e) {
+            throw err.error(element, e);
+        }
     }
 
     @Override
@@ -62,22 +70,4 @@ public final class ValueSyntax extends XmlSyntax {
         return "<" + getWriteElementName() + ">data";
     }
 
-    public static > ValueSyntax delimitedString(
-        Function toString,
-        Function fromString,
-        String delimiter,
-        Supplier emptyCollSupplier
-    ) {
-
-        return new ValueSyntax<>(
-            coll -> coll.stream().map(toString).collect(Collectors.joining(delimiter)),
-            string -> {
-                C coll = emptyCollSupplier.get();
-                for (String item : string.split(Pattern.quote(delimiter))) {
-                    coll.add(fromString.apply(item));
-                }
-                return coll;
-            }
-        );
-    }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java
index e28a4fd022..8d1f30dca2 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java
@@ -14,13 +14,19 @@ import org.w3c.dom.Node;
  */
 public interface XmlErrorReporter {
 
-    void warn(Node node, String message, Object... args);
+    default void warn(Node node, String message, Object... args) {
+        throw new UnsupportedOperationException("TODO");
+    }
 
 
-    RuntimeException error(Node node, String message, Object... args);
+    default RuntimeException error(Node node, String message, Object... args) {
+        return new IllegalArgumentException(String.format(message, args));
+    }
 
 
-    RuntimeException error(Node node, Throwable ex);
+    default RuntimeException error(Node node, Throwable ex) {
+        return new IllegalArgumentException(ex);
+    }
 
 
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
index c0c0570e68..5c90fd33dc 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
@@ -41,11 +41,11 @@ public abstract class XmlSyntax {
     }
 
     /**
-     * Read the value from a string, returns null if unsupported.
-     * @throws UnsupportedOperationException if unsupported
-     * @throws IllegalArgumentException if something goes wrong (but should report on the error reporter)
+     * Read the value from a string.
+     * @throws UnsupportedOperationException if unsupported, see {@link #supportsFromString()}
+     * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter)
      */
-    public T fromString(Element owner, String attributeData, XmlErrorReporter err) {
+    public T fromString(String attributeData) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
new file mode 100644
index 0000000000..8cb66bac12
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
@@ -0,0 +1,95 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.properties.internal;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public final class XmlSyntaxUtils {
+
+    public static final ValueSyntax STRING = new ValueSyntax<>(Function.identity());
+    public static final ValueSyntax CHARACTER = new ValueSyntax<>(value -> {
+        if (value == null || value.length() != 1) {
+            throw new IllegalArgumentException("missing/ambiguous character value for string \"" + value + "\"");
+        }
+        return value.charAt(0);
+    });
+
+    public static final ValueSyntax REGEX = new ValueSyntax<>(Pattern::compile);
+    public static final ValueSyntax INTEGER = new ValueSyntax<>(Integer::valueOf);
+    public static final ValueSyntax LONG = new ValueSyntax<>(Long::valueOf);
+    public static final ValueSyntax BOOLEAN = new ValueSyntax<>(Boolean::valueOf);
+    public static final ValueSyntax DOUBLE = new ValueSyntax<>(Double::valueOf);
+
+
+    public static final XmlSyntax> INTEGER_LIST = numberList(INTEGER);
+    public static final XmlSyntax> DOUBLE_LIST = numberList(DOUBLE);
+    public static final XmlSyntax> LONG_LIST = numberList(LONG);
+
+    public static final XmlSyntax> CHAR_LIST = otherList(CHARACTER);
+    public static final XmlSyntax> STRING_LIST = otherList(STRING);
+    public static final XmlSyntax> BOOLEAN_LIST = otherList(BOOLEAN);
+
+    public static final XmlSyntax> REGEX_LIST = new SeqSyntax<>(REGEX, ArrayList::new);
+
+
+    private XmlSyntaxUtils() {
+
+    }
+
+
+    private static  XmlSyntax> numberList(ValueSyntax valueSyntax) {
+        return withSeq(valueSyntax,
+                       ArrayList::new,
+                       true,
+                       ","
+        );
+    }
+
+    private static  XmlSyntax> otherList(ValueSyntax valueSyntax) {
+        return withSeq(valueSyntax,
+                       ArrayList::new,
+                       true,
+                       "|"
+        );
+    }
+
+    static > XmlSyntax withSeq(ValueSyntax itemSyntax,
+                                                             Supplier emptyCollSupplier,
+                                                             boolean preferOldSyntax,
+                                                             String delimiter) {
+        return new SyntaxSet<>(
+            new SeqSyntax<>(itemSyntax, emptyCollSupplier),
+            delimitedString(itemSyntax::toString, itemSyntax::fromString, delimiter, emptyCollSupplier),
+            preferOldSyntax
+        );
+    }
+
+
+    public static > ValueSyntax delimitedString(
+        Function toString,
+        Function fromString,
+        String delimiter,
+        Supplier emptyCollSupplier
+    ) {
+
+        return new ValueSyntax<>(
+            coll -> coll.stream().map(toString).collect(Collectors.joining(delimiter)),
+            string -> {
+                C coll = emptyCollSupplier.get();
+                for (String item : string.split(Pattern.quote(delimiter))) {
+                    coll.add(fromString.apply(item));
+                }
+                return coll;
+            }
+        );
+    }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index 16e8f93540..3114a7ed2e 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -17,9 +17,7 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import org.apache.commons.lang3.StringUtils;
-import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
@@ -28,10 +26,12 @@ import net.sourceforge.pmd.RulePriority;
 import net.sourceforge.pmd.RuleSetReference;
 import net.sourceforge.pmd.annotation.InternalApi;
 import net.sourceforge.pmd.lang.rule.RuleReference;
+import net.sourceforge.pmd.properties.PropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.PropertyDescriptorField;
 import net.sourceforge.pmd.properties.PropertyTypeId;
-import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder;
+import net.sourceforge.pmd.properties.internal.XmlErrorReporter;
+import net.sourceforge.pmd.properties.internal.XmlSyntax;
 import net.sourceforge.pmd.util.ResourceLoader;
 
 
@@ -321,35 +321,60 @@ public class RuleFactory {
      * @return The property descriptor
      */
     private static PropertyDescriptor parsePropertyDefinition(Element propertyElement) {
-        String typeId = propertyElement.getAttribute(PropertyDescriptorField.TYPE.attributeName());
+        XmlErrorReporter err = new XmlErrorReporter() {};
 
-        PropertyDescriptorExternalBuilder pdFactory = PropertyTypeId.factoryFor(typeId);
-        if (pdFactory == null) {
+        String typeId = PropertyDescriptorField.TYPE.getOrThrow(propertyElement, err);
+
+        PropertyTypeId factory = PropertyTypeId.lookupMnemonic(typeId);
+        if (factory == null) {
             throw new IllegalArgumentException("No property descriptor factory for type: " + typeId);
         }
 
-        Map values = new HashMap<>();
-        NamedNodeMap atts = propertyElement.getAttributes();
+        PropertyBuilder builder =
+            factory.newBuilder(PropertyDescriptorField.NAME.getOrThrow(propertyElement, err));
 
-        /// populate a map of values for an individual descriptor
-        for (int i = 0; i < atts.getLength(); i++) {
-            Attr a = (Attr) atts.item(i);
-            values.put(PropertyDescriptorField.getConstant(a.getName()), a.getValue());
-        }
+        builder.desc(PropertyDescriptorField.DESCRIPTION.getOrThrow(propertyElement, err));
 
-        if (StringUtils.isBlank(values.get(DEFAULT_VALUE))) {
+        propertyValueCapture(propertyElement, typeId, factory.getXmlSyntax(), builder, err);
+
+        // TODO support constraints like numeric range
+
+        return builder.build();
+    }
+
+
+    private static  void propertyValueCapture(Element propertyElement,
+                                                 String typeId,
+                                                 XmlSyntax baseSyntax,
+                                                 PropertyBuilder builder,
+                                                 XmlErrorReporter err) {
+        @SuppressWarnings("unchecked")
+        XmlSyntax syntax = (XmlSyntax) baseSyntax;
+        T defaultValue;
+
+
+        String defaultAttr = DEFAULT_VALUE.getOptional(propertyElement);
+        if (!StringUtils.isBlank(defaultAttr)) {
+            if (syntax.supportsFromString()) {
+                defaultValue = syntax.fromString(defaultAttr);
+            } else {
+                throw err.error(propertyElement.getAttributeNode(DEFAULT_VALUE.attributeName()),
+                                "Type " + typeId + " cannot be parsed from a string, use a nested element, e.g. "
+                                    + syntax.example());
+            }
+        } else {
             NodeList children = propertyElement.getElementsByTagName(DEFAULT_VALUE.attributeName());
             if (children.getLength() == 1) {
-                values.put(DEFAULT_VALUE, children.item(0).getTextContent());
+                defaultValue = syntax.fromXml((Element) children.item(0), err);
             } else {
                 throw new IllegalArgumentException("No value defined!");
             }
         }
 
-        // casting is not pretty but prevents the interface from having this method
-        return pdFactory.build(values);
+        builder.defaultValue(defaultValue);
     }
 
+
     /** Gets the string value from a property node. */
     private static String valueFrom(Element propertyNode) {
         String strValue = propertyNode.getAttribute(DEFAULT_VALUE.attributeName());

From 89837b0fc5e3c3ea94602fa8730baa6aa260b283 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Thu, 12 Sep 2019 23:29:59 +0200
Subject: [PATCH 013/347] Use type id instead of isdefinedExternally

---
 .../net/sourceforge/pmd/RuleSetWriter.java    |  58 +--
 .../pmd/properties/AbstractProperty.java      |   6 -
 .../GenericMultiValuePropertyDescriptor.java  |   2 +
 .../properties/GenericPropertyDescriptor.java |   8 +-
 .../pmd/properties/PropertyBuilder.java       |  29 +-
 .../pmd/properties/PropertyDescriptor.java    |  22 +-
 .../properties/PropertyDescriptorField.java   |   6 +-
 .../pmd/properties/PropertyTypeId.java        |  81 ----
 ...rtyDescriptorBuilderConversionWrapper.java |   3 -
 .../pmd/properties/internal/ValueSyntax.java  |   3 +-
 ...stractNumericPropertyDescriptorTester.java |  83 ----
 ...tractPackagedPropertyDescriptorTester.java |  36 --
 .../AbstractPropertyDescriptorTester.java     | 365 ------------------
 .../pmd/properties/BooleanPropertyTest.java   |  71 ----
 .../pmd/properties/CharacterPropertyTest.java |  78 ----
 .../pmd/properties/DoublePropertyTest.java    |  91 -----
 .../pmd/properties/IntegerPropertyTest.java   | 102 -----
 .../pmd/properties/LongPropertyTest.java      |  88 -----
 .../pmd/properties/RegexPropertyTest.java     | 102 -----
 .../pmd/properties/StringPropertyTest.java    |  94 -----
 20 files changed, 72 insertions(+), 1256 deletions(-)
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractNumericPropertyDescriptorTester.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPackagedPropertyDescriptorTester.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/BooleanPropertyTest.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/CharacterPropertyTest.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/DoublePropertyTest.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/IntegerPropertyTest.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/LongPropertyTest.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/RegexPropertyTest.java
 delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/StringPropertyTest.java

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
index a736777dc8..dcbeb3d427 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
@@ -8,6 +8,7 @@ import java.io.OutputStream;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -23,6 +24,7 @@ import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
 
 import org.apache.commons.io.IOUtils;
+import org.checkerframework.checker.nullness.qual.NonNull;
 import org.w3c.dom.CDATASection;
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
@@ -33,10 +35,10 @@ import net.sourceforge.pmd.lang.Language;
 import net.sourceforge.pmd.lang.LanguageVersion;
 import net.sourceforge.pmd.lang.rule.ImmutableLanguage;
 import net.sourceforge.pmd.lang.rule.RuleReference;
-import net.sourceforge.pmd.lang.rule.XPathRule;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.PropertyDescriptorField;
 import net.sourceforge.pmd.properties.PropertyTypeId;
+import net.sourceforge.pmd.properties.internal.XmlSyntax;
 
 /**
  * This class represents a way to serialize a RuleSet to an XML configuration
@@ -127,6 +129,14 @@ public class RuleSetWriter {
         return createTextElement("description", description);
     }
 
+    private Element createPropertyValueElement(String name) {
+        return document.createElementNS(RULESET_2_0_0_NS_URI, name);
+    }
+
+    private Element createPropertyDefaultElement() {
+        return document.createElementNS(RULESET_2_0_0_NS_URI, "default");
+    }
+
     private Element createExcludePatternElement(String excludePattern) {
         return createTextElement("exclude-pattern", excludePattern);
     }
@@ -271,20 +281,22 @@ public class RuleSetWriter {
             for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                 // For each provided PropertyDescriptor
 
-                if (propertyDescriptor.isDefinedExternally()) {
+                PropertyTypeId typeId = propertyDescriptor.getTypeId();
+
+                if (typeId != null) {
                     // Any externally defined property needs to go out as a definition.
                     if (propertiesElement == null) {
                         propertiesElement = createPropertiesElement();
                     }
 
-                    Element propertyElement = createPropertyDefinitionElementBR(propertyDescriptor);
+                    Element propertyElement = createPropertyDefinitionElementBR(propertyDescriptor, typeId);
                     propertiesElement.appendChild(propertyElement);
                 } else {
                     if (propertiesByPropertyDescriptor != null) {
                         // Otherwise, any property which has a value different than the default needs to go out as a value.
                         Object defaultValue = propertyDescriptor.defaultValue();
                         Object value = propertiesByPropertyDescriptor.get(propertyDescriptor);
-                        if (value != defaultValue && (value == null || !value.equals(defaultValue))) {
+                        if (!Objects.equals(value, defaultValue)) {
                             if (propertiesElement == null) {
                                 propertiesElement = createPropertiesElement();
                             }
@@ -308,7 +320,7 @@ public class RuleSetWriter {
                     // default needs to go out as a value.
                     Object defaultValue = propertyDescriptor.defaultValue();
                     Object value = entry.getValue();
-                    if (value != defaultValue && (value == null || !value.equals(defaultValue))) {
+                    if (!Objects.equals(value, defaultValue)) {
                         if (propertiesElement == null) {
                             propertiesElement = createPropertiesElement();
                         }
@@ -321,35 +333,33 @@ public class RuleSetWriter {
         return propertiesElement;
     }
 
-    private Element createPropertyValueElement(PropertyDescriptor propertyDescriptor, Object value) {
-        Element propertyElement = document.createElementNS(RULESET_2_0_0_NS_URI, "property");
-        propertyElement.setAttribute("name", propertyDescriptor.name());
-        String valueString = propertyDescriptor.asDelimitedString(value);
-        if (XPathRule.XPATH_DESCRIPTOR.equals(propertyDescriptor)) {
-            Element valueElement = createCDATASectionElement("value", valueString);
-            propertyElement.appendChild(valueElement);
-        } else {
-            propertyElement.setAttribute("value", valueString);
-        }
+    private  Element createPropertyValueElement(PropertyDescriptor propertyDescriptor, T value) {
+        Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property");
+        PropertyDescriptorField.NAME.setOn(element, propertyDescriptor.name());
 
-        return propertyElement;
+        XmlSyntax xmlStrategy = propertyDescriptor.xmlStrategy();
+
+        Element valueElt = createPropertyValueElement(xmlStrategy.getWriteElementName());
+        xmlStrategy.toXml(valueElt, value);
+        element.appendChild(valueElt);
+
+        return element;
     }
 
+    private  Element createPropertyDefinitionElementBR(PropertyDescriptor propertyDescriptor, @NonNull PropertyTypeId typeId) {
 
-    private Element createPropertyDefinitionElementBR(PropertyDescriptor propertyDescriptor) {
+        final Element element = createPropertyValueElement(propertyDescriptor, propertyDescriptor.defaultValue());
 
-        final Element propertyElement = createPropertyValueElement(propertyDescriptor,
-                propertyDescriptor.defaultValue());
-        propertyElement.setAttribute(PropertyDescriptorField.TYPE.attributeName(),
-                                     PropertyTypeId.typeIdFor(propertyDescriptor.type(),
-                                                                      propertyDescriptor.isMultiValue()));
+        PropertyDescriptorField.NAME.setOn(element, propertyDescriptor.name());
+        PropertyDescriptorField.TYPE.setOn(element, typeId.getStringId());
+        PropertyDescriptorField.DESCRIPTION.setOn(element, propertyDescriptor.description());
 
         Map propertyValuesById = propertyDescriptor.attributeValuesById();
         for (Map.Entry entry : propertyValuesById.entrySet()) {
-            propertyElement.setAttribute(entry.getKey().attributeName(), entry.getValue());
+            element.setAttribute(entry.getKey().attributeName(), entry.getValue());
         }
 
-        return propertyElement;
+        return element;
     }
 
     private Element createTextElement(String name, String value) {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java
index 67bbd23ad8..1ceddd36d3 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java
@@ -131,12 +131,6 @@ import org.apache.commons.lang3.StringUtils;
     protected abstract String defaultAsString();
 
 
-    @Override
-    public boolean isDefinedExternally() {
-        return isDefinedExternally;
-    }
-
-
     private static String checkNotEmpty(String arg, PropertyDescriptorField argId) throws IllegalArgumentException {
         if (StringUtils.isBlank(arg)) {
             throw new IllegalArgumentException("Property attribute '" + argId + "' cannot be null or blank");
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java
index 90bdc663cf..74c433fa90 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java
@@ -9,6 +9,8 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
 import net.sourceforge.pmd.properties.constraints.PropertyConstraint;
 import net.sourceforge.pmd.properties.internal.StringParser;
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java
index 8f0889ab9d..0eff12903b 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java
@@ -6,6 +6,8 @@ package net.sourceforge.pmd.properties;
 
 import java.util.Set;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
 import net.sourceforge.pmd.properties.constraints.PropertyConstraint;
 import net.sourceforge.pmd.properties.internal.StringParser;
 
@@ -20,6 +22,7 @@ final class GenericPropertyDescriptor extends AbstractSingleValueProperty
 
 
     private final StringParser parser;
+    private final PropertyTypeId typeId;
     private final Class type;
     private final Set> constraints;
 
@@ -30,12 +33,13 @@ final class GenericPropertyDescriptor extends AbstractSingleValueProperty
                               T defaultValue,
                               Set> constraints,
                               StringParser parser,
-                              boolean isDefinedExternally,
+                              @Nullable PropertyTypeId typeId,
                               Class type) {
 
-        super(name, description, defaultValue, uiOrder, isDefinedExternally);
+        super(name, description, defaultValue, uiOrder, typeId != null);
         this.constraints = constraints;
         this.parser = parser;
+        this.typeId = typeId;
         this.type = type;
 
         String dftValueError = errorFor(defaultValue);
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index bef345f3a6..bf70262923 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -55,10 +55,10 @@ public abstract class PropertyBuilder, T> {
 
     private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z][\\w-]*");
     private final Set> validators = new LinkedHashSet<>();
-    protected boolean isDefinedExternally;
     private String name;
     private String description;
     private T defaultValue;
+    protected PropertyTypeId typeId;
 
 
     PropertyBuilder(String name) {
@@ -72,11 +72,6 @@ public abstract class PropertyBuilder, T> {
     }
 
 
-    void setDefinedExternally(boolean bool) {
-        this.isDefinedExternally = bool;
-    }
-
-
     Set> getConstraints() {
         return validators;
     }
@@ -126,6 +121,12 @@ public abstract class PropertyBuilder, T> {
         return (B) this;
     }
 
+    @SuppressWarnings("unchecked")
+    B typeId(PropertyTypeId typeId) {
+        this.typeId = typeId;
+        return (B) this;
+    }
+
     // TODO 7.0.0 document the following:
     //
     //     * 

Constraints should be independent from each other, and should @@ -255,14 +256,14 @@ public abstract class PropertyBuilder, T> { @Override public PropertyDescriptor build() { return new GenericPropertyDescriptor<>( - getName(), - getDescription(), - 0f, - getDefaultValue(), - getConstraints(), - parser, - isDefinedExternally, - type + getName(), + getDescription(), + 0f, + getDefaultValue(), + getConstraints(), + parser, + typeId, + type ); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index c7245af529..d4d2f9ffb6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.properties; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSetWriter; import net.sourceforge.pmd.annotation.InternalApi; @@ -89,6 +91,14 @@ public interface PropertyDescriptor { Class type(); + /** + * Returns the type ID which was used to define this property. Returns + * null if this property was defined in Java code and not in XML. + */ + default @Nullable PropertyTypeId getTypeId() { + return null; + } + /** * Returns whether the property is multi-valued, i.e. an array of strings, * @@ -173,16 +183,4 @@ public interface PropertyDescriptor { Map attributeValuesById(); - /** - * True if this descriptor was defined in the ruleset xml. This precision is necessary for the {@link RuleSetWriter} - * to write out the property correctly: if it was defined externally, then its definition must be written out, - * otherwise only its value. - * - * @deprecated May be removed with 7.0.0 - * @return True if the descriptor was defined in xml - */ - @Deprecated - @InternalApi - boolean isDefinedExternally(); - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java index 7d76f24a5a..bd88538a53 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java @@ -32,8 +32,6 @@ 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. */ @@ -73,6 +71,10 @@ public enum PropertyDescriptorField { return element.getAttribute(attributeName); } + public void setOn(Element element, String value) { + element.setAttribute(attributeName, value); + } + /** * Returns the String name of this attribute. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 2e789b4e2e..507e89603c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -10,7 +10,6 @@ import java.util.Map; import java.util.function.Function; import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; -import net.sourceforge.pmd.properties.internal.StringParser; import net.sourceforge.pmd.properties.internal.XmlSyntax; import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; @@ -28,7 +27,6 @@ import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; * See {@link PropertyDescriptor} for more info about property framework changes with 7.0.0. * * @author Clรฉment Fournier - * @see PropertyDescriptorExternalBuilder * @since 6.0.0 */ public enum PropertyTypeId { @@ -93,48 +91,6 @@ public enum PropertyTypeId { return factory.apply(name); } - /** - * Returns true if the property corresponding to this factory takes - * lists of values as its value. - * - * @return whether the property is multivalue - * - * @deprecated see {@link PropertyDescriptor#isMultiValue()} - */ - @Deprecated - public boolean isPropertyMultivalue() { - return factory.isMultiValue(); - } - - - /** - * Returns the value type of the property corresponding to this factory. - * This is the component type of the list if the property is multivalued. - * - * @return The value type of the property - * - * @deprecated see {@link PropertyDescriptor#type()} - */ - @Deprecated - public Class propertyValueType() { - return factory.valueType(); - } - - - /** - * Gets the object used to parse the values of this property from a string. - * If the property is multivalued, the parser only parses individual components - * of the list. A list parser can be obtained with {@link ValueParserConstants#multi(StringParser, char)}. - * - * @return The value parser - * - * @deprecated see {@link PropertyDescriptor#valueFrom(String)} - */ - @Deprecated - public StringParser getStringParser() { - return stringParser; - } - /** * Returns the full mappings from type ids to enum constants. @@ -146,22 +102,6 @@ public enum PropertyTypeId { } - /** - * Gets the factory for the descriptor identified by the string id. - * - * @param stringId The identifier of the type - * - * @return The factory used to build new instances of a descriptor - * - * @deprecated See {@link PropertyDescriptorExternalBuilder} - */ - @Deprecated - public static PropertyDescriptorExternalBuilder factoryFor(String stringId) { - PropertyTypeId cons = CONSTANTS_BY_MNEMONIC.get(stringId); - return cons == null ? null : cons.factory; - } - - /** * Gets the enum constant corresponding to the given mnemonic. * @@ -174,25 +114,4 @@ public enum PropertyTypeId { } - /** - * 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 string id - * - * @deprecated The signature will probably be altered in 7.0.0 but a similar functionality will be available - */ - @Deprecated - public static String typeIdFor(Class valueType, boolean multiValue) { - for (Map.Entry entry : CONSTANTS_BY_MNEMONIC.entrySet()) { - if (entry.getValue().propertyValueType() == valueType - && entry.getValue().isPropertyMultivalue() == multiValue) { - return entry.getKey(); - } - } - return null; - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java index 5df12be389..51fad870e1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java @@ -45,9 +45,6 @@ public abstract class PropertyDescriptorBuilderConversionWrapper fields) { builder.desc(fields.get(PropertyDescriptorField.DESCRIPTION)); - if (fields.containsKey(PropertyDescriptorField.UI_ORDER)) { - builder.uiOrder(Float.parseFloat(fields.get(PropertyDescriptorField.UI_ORDER))); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index b7f2da816f..5850f34df0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -5,10 +5,8 @@ package net.sourceforge.pmd.properties.internal; import java.util.Objects; -import java.util.function.BiFunction; import java.util.function.Function; -import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; /** @@ -53,6 +51,7 @@ public final class ValueSyntax extends XmlSyntax { @Override public void toXml(Element container, T value) { + // TODO CDATA/ xml escape container.setTextContent(toString.apply(value)); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractNumericPropertyDescriptorTester.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractNumericPropertyDescriptorTester.java deleted file mode 100644 index c9a880f5dc..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractNumericPropertyDescriptorTester.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import static org.junit.Assert.assertNotNull; - -import java.util.Map; - -import org.junit.Test; - -import net.sourceforge.pmd.properties.builders.MultiNumericPropertyBuilder; -import net.sourceforge.pmd.properties.builders.SingleNumericPropertyBuilder; - - -/** - * @author Clรฉment Fournier - */ -public abstract class AbstractNumericPropertyDescriptorTester extends AbstractPropertyDescriptorTester { - - public AbstractNumericPropertyDescriptorTester(String typeName) { - super(typeName); - } - - - @Test - public void testLowerUpperLimit() { - assertNotNull(((NumericPropertyDescriptor) createProperty()).lowerLimit()); - assertNotNull(((NumericPropertyDescriptor) createProperty()).upperLimit()); - assertNotNull(((NumericPropertyDescriptor) createMultiProperty()).lowerLimit()); - assertNotNull(((NumericPropertyDescriptor) createMultiProperty()).upperLimit()); - } - - - @Test(expected = RuntimeException.class) - public void testMissingMinThreshold() { - Map attributes = getPropertyDescriptorValues(); - attributes.remove(PropertyDescriptorField.MIN); - getSingleFactory().build(attributes); - } - - - @Override - protected Map getPropertyDescriptorValues() { - Map attributes = super.getPropertyDescriptorValues(); - attributes.put(PropertyDescriptorField.MIN, min().toString()); - attributes.put(PropertyDescriptorField.MAX, max().toString()); - return attributes; - } - - - @Test(expected = RuntimeException.class) - public void testMissingMaxThreshold() { - Map attributes = getPropertyDescriptorValues(); - attributes.remove(PropertyDescriptorField.MAX); - getSingleFactory().build(attributes); - - } - - - @Test(expected = IllegalArgumentException.class) - public void testBadDefaultValue() { - singleBuilder().defaultValue(createBadValue()).build(); - } - - - @Test(expected = IllegalArgumentException.class) - @SuppressWarnings("unchecked") - public void testMultiBadDefaultValue() { - multiBuilder().defaultValues(createValue(), createBadValue()).build(); - } - - - protected abstract SingleNumericPropertyBuilder singleBuilder(); - - protected abstract MultiNumericPropertyBuilder multiBuilder(); - - - protected abstract T min(); - - protected abstract T max(); -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPackagedPropertyDescriptorTester.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPackagedPropertyDescriptorTester.java deleted file mode 100644 index 7190685b38..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPackagedPropertyDescriptorTester.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Map; - -import org.junit.Test; - -/** - * @author Clรฉment Fournier - */ -public abstract class AbstractPackagedPropertyDescriptorTester extends AbstractPropertyDescriptorTester { - - /* default */ AbstractPackagedPropertyDescriptorTester(String typeName) { - super(typeName); - } - - - @Test - public void testMissingPackageNames() { - Map attributes = getPropertyDescriptorValues(); - attributes.remove(PropertyDescriptorField.LEGAL_PACKAGES); - getMultiFactory().build(attributes); // no exception, null is ok - getSingleFactory().build(attributes); - } - - - @Override - protected Map getPropertyDescriptorValues() { - Map attributes = super.getPropertyDescriptorValues(); - attributes.put(PropertyDescriptorField.LEGAL_PACKAGES, "java.lang"); - return attributes; - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java deleted file mode 100644 index b80e16d8e5..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java +++ /dev/null @@ -1,365 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.regex.Pattern; - -import org.junit.Test; - -import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; - - -/** - * Base functionality for all concrete subclasses that evaluate type-specific property descriptors. Checks for error - * conditions during construction, error value detection, serialization, etc. - * - * @author Brian Remedios - */ -public abstract class AbstractPropertyDescriptorTester { - - public static final String PUNCTUATION_CHARS = "!@#$%^&*()_-+=[]{}\\|;:'\",.<>/?`~"; - public static final String WHITESPACE_CHARS = " \t\n"; - public static final String DIGIT_CHARS = "0123456789"; - public static final String ALPHA_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmniopqrstuvwxyz"; - public static final String ALPHA_NUMERIC_CHARS = DIGIT_CHARS + ALPHA_CHARS; - public static final String ALL_CHARS = PUNCTUATION_CHARS + WHITESPACE_CHARS + ALPHA_NUMERIC_CHARS; - private static final int MULTI_VALUE_COUNT = 10; - - private static final Random RANDOM = new Random(); - - protected final String typeName; - - - public AbstractPropertyDescriptorTester(String typeName) { - this.typeName = typeName; - } - - - protected abstract PropertyDescriptor> createBadMultiProperty(); - - - @Test - public void testFactorySingleValue() { - PropertyDescriptor prop = getSingleFactory().build(getPropertyDescriptorValues()); - T originalValue = createValue(); - T value = prop.valueFrom(originalValue instanceof Class ? ((Class) originalValue).getName() : String.valueOf(originalValue)); - T value2 = prop.valueFrom(prop.asDelimitedString(value)); - if (Pattern.class.equals(prop.type())) { - // Pattern.equals uses object identity... - // we're forced to do that to make it compare the string values of the pattern - assertEquals(String.valueOf(value), String.valueOf(value2)); - } else { - assertEquals(value, value2); - } - } - - - @SuppressWarnings("unchecked") - protected final PropertyDescriptorExternalBuilder getSingleFactory() { - return (PropertyDescriptorExternalBuilder) PropertyTypeId.factoryFor(typeName); - } - - - protected Map getPropertyDescriptorValues() { - Map valuesById = new HashMap<>(); - valuesById.put(PropertyDescriptorField.NAME, "test"); - valuesById.put(PropertyDescriptorField.DESCRIPTION, "desc"); - valuesById.put(PropertyDescriptorField.DEFAULT_VALUE, createProperty().asDelimitedString(createValue())); - return valuesById; - } - - - /** - * Return a legal value(s) per the general scope of the descriptor. - * - * @return Object - */ - protected abstract T createValue(); - - - @Test - public void testFactoryMultiValueDefaultDelimiter() { - PropertyDescriptorExternalBuilder> multiFactory = getMultiFactory(); - PropertyDescriptor> prop = multiFactory.build(getPropertyDescriptorValues()); - List originalValue = createMultipleValues(MULTI_VALUE_COUNT); - String asDelimitedString = prop.asDelimitedString(originalValue); - List value2 = prop.valueFrom(asDelimitedString); - assertEquals(originalValue, value2); - } - - - @SuppressWarnings("unchecked") - protected final PropertyDescriptorExternalBuilder> getMultiFactory() { - return (PropertyDescriptorExternalBuilder>) PropertyTypeId.factoryFor("List[" + typeName + "]"); - } - - - private List createMultipleValues(int count) { - List res = new ArrayList<>(); - while (count > 0) { - res.add(createValue()); - count--; - } - return res; - } - - - @Test - public void testFactoryMultiValueCustomDelimiter() { - PropertyDescriptorExternalBuilder> multiFactory = getMultiFactory(); - Map valuesById = getPropertyDescriptorValues(); - String customDelimiter = "รค"; - assertFalse(ALL_CHARS.contains(customDelimiter)); - valuesById.put(PropertyDescriptorField.DELIMITER, customDelimiter); - PropertyDescriptor> prop = multiFactory.build(valuesById); - List originalValue = createMultipleValues(MULTI_VALUE_COUNT); - String asDelimitedString = prop.asDelimitedString(originalValue); - List value2 = prop.valueFrom(asDelimitedString); - assertEquals(originalValue.toString(), value2.toString()); - assertEquals(originalValue, value2); - } - - - @Test - public void testConstructors() { - - PropertyDescriptor desc = createProperty(); - assertNotNull(desc); - - try { - createBadProperty(); - } catch (Exception ex) { - return; // caught ok - } - - fail("uncaught constructor exception"); - } - - - /** - * Creates and returns a properly configured property descriptor. - * - * @return PropertyDescriptor - */ - protected abstract PropertyDescriptor createProperty(); - - - /** - * Attempt to create a property with faulty configuration values. This method should throw an - * IllegalArgumentException if done correctly. - * - * @return PropertyDescriptor - */ - protected abstract PropertyDescriptor createBadProperty(); - - - @Test - public void testAsDelimitedString() { - - List testValue = createMultipleValues(MULTI_VALUE_COUNT); - PropertyDescriptor> pmdProp = createMultiProperty(); - - String storeValue = pmdProp.asDelimitedString(testValue); - List returnedValue = pmdProp.valueFrom(storeValue); - - assertEquals(returnedValue, testValue); - } - - - protected abstract PropertyDescriptor> createMultiProperty(); - - - @Test - public void testValueFrom() { - - T testValue = createValue(); - PropertyDescriptor pmdProp = createProperty(); - - String storeValue = pmdProp.asDelimitedString(testValue); - - T returnedValue = pmdProp.valueFrom(storeValue); - - if (Pattern.class.equals(pmdProp.type())) { - // Pattern.equals uses object identity... - // we're forced to do that to make it compare the string values of the pattern - assertEquals(String.valueOf(returnedValue), String.valueOf(testValue)); - } else { - assertEquals(returnedValue, testValue); - } - } - - - @Test - public void testErrorForCorrectSingle() { - T testValue = createValue(); - PropertyDescriptor pmdProp = createProperty(); // plain vanilla - // property & valid test value - String errorMsg = pmdProp.errorFor(testValue); - assertNull(errorMsg, errorMsg); - } - - - @Test - public void testErrorForCorrectMulti() { - List testMultiValues = createMultipleValues(MULTI_VALUE_COUNT); // multi-value property, all - // valid test values - PropertyDescriptor> multiProperty = createMultiProperty(); - String errorMsg = multiProperty.errorFor(testMultiValues); - assertNull(errorMsg, errorMsg); - - } - - - @Test - public void testErrorForBadSingle() { - T testValue = createBadValue(); - PropertyDescriptor pmdProp = createProperty(); // plain vanilla - // property & valid test value - String errorMsg = pmdProp.errorFor(testValue); - assertNotNull("uncaught bad value: " + testValue, errorMsg); - } - - - /** - * Return a value(s) that is known to be faulty per the general scope of the descriptor. - * - * @return Object - */ - protected abstract T createBadValue(); - - - @Test - public void testErrorForBadMulti() { - List testMultiValues = createMultipleBadValues(MULTI_VALUE_COUNT); // multi-value property, all - // valid test values - PropertyDescriptor> multiProperty = createMultiProperty(); - String errorMsg = multiProperty.errorFor(testMultiValues); - assertNotNull("uncaught bad value in: " + testMultiValues, errorMsg); - } - - - private List createMultipleBadValues(int count) { - List res = new ArrayList<>(); - while (count > 0) { - res.add(createBadValue()); - count--; - } - return res; - } - - - @Test - public void testIsMultiValue() { - assertFalse(createProperty().isMultiValue()); - } - - - @Test - public void testIsMultiValueMulti() { - assertTrue(createMultiProperty().isMultiValue()); - } - - @Test - public void testAddAttributes() { - Map atts = createProperty().attributeValuesById(); - assertTrue(atts.containsKey(PropertyDescriptorField.NAME)); - assertTrue(atts.containsKey(PropertyDescriptorField.DESCRIPTION)); - assertTrue(atts.containsKey(PropertyDescriptorField.DEFAULT_VALUE)); - } - - - @Test - public void testAddAttributesMulti() { - Map multiAtts = createMultiProperty().attributeValuesById(); - assertTrue(multiAtts.containsKey(PropertyDescriptorField.DELIMITER)); - assertTrue(multiAtts.containsKey(PropertyDescriptorField.NAME)); - assertTrue(multiAtts.containsKey(PropertyDescriptorField.DESCRIPTION)); - assertTrue(multiAtts.containsKey(PropertyDescriptorField.DEFAULT_VALUE)); - } - - - @Test - public void testType() { - assertNotNull(createProperty().type()); - } - - - @Test - public void testTypeMulti() { - assertNotNull(createMultiProperty().type()); - } - - static boolean randomBool() { - return RANDOM.nextBoolean(); - } - - - static char randomChar(char[] characters) { - return characters[randomInt(0, characters.length)]; - } - - - static int randomInt(int min, int max) { - return (int) randomLong(min, max); - } - - - static float randomFloat(float min, float max) { - return (float) randomDouble(min, max); - } - - - static double randomDouble(double min, double max) { - return min + RANDOM.nextDouble() * Math.abs(max - min); - } - - - static long randomLong(long min, long max) { - return min + RANDOM.nextInt((int) Math.abs(max - min)); - } - - - static T randomChoice(T[] items) { - return items[randomInt(0, items.length)]; - } - - - /** - * Method filter. - * - * @param chars char[] - * @param removeChar char - * @return char[] - */ - protected static char[] filter(char[] chars, char removeChar) { - int count = 0; - for (char c : chars) { - if (c == removeChar) { - count++; - } - } - char[] results = new char[chars.length - count]; - - int index = 0; - for (char c : chars) { - if (c != removeChar) { - results[index++] = c; - } - } - return results; - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/BooleanPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/BooleanPropertyTest.java deleted file mode 100644 index 2e5cda931b..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/BooleanPropertyTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - -import org.junit.Test; - -/** - * @author Brian Remedios - */ -public class BooleanPropertyTest extends AbstractPropertyDescriptorTester { - - public BooleanPropertyTest() { - super("Boolean"); - } - - - @Override - protected Boolean createValue() { - return randomBool(); - } - - - @Override - @Test - public void testErrorForBadSingle() { - // override, cannot create a 'bad' boolean per se - } - - - @Override - @Test - public void testErrorForBadMulti() { - // override, cannot create a 'bad' boolean per se - } - - - @Override - protected Boolean createBadValue() { - return null; - } - - - @Override - protected PropertyDescriptor createProperty() { - return new BooleanProperty("testBoolean", "Test boolean property", false, 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new BooleanMultiProperty("testBoolean", "Test boolean property", - new Boolean[] {false, true, true}, 1.0f); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new BooleanMultiProperty("", "Test boolean property", new Boolean[] {false, true, true}, 1.0f); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new BooleanProperty("testBoolean", "", false, 1.0f); - } - -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/CharacterPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/CharacterPropertyTest.java deleted file mode 100644 index d0a50c7e7b..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/CharacterPropertyTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - -import org.junit.Test; - -/** - * Evaluates the functionality of the CharacterProperty descriptor by testing - * its ability to catch creation errors (illegal args), flag invalid characters, - * and serialize/deserialize any default values. - * - * @author Brian Remedios - */ -@Deprecated -public class CharacterPropertyTest extends AbstractPropertyDescriptorTester { - - private static final char DELIMITER = '|'; - private static final char[] CHARSET = filter(ALL_CHARS.toCharArray(), DELIMITER); - - - public CharacterPropertyTest() { - super("Character"); - } - - - @Override - @Test - public void testErrorForBadSingle() { - } // not until char properties use illegal chars - - - @Override - @Test - public void testErrorForBadMulti() { - } // not until char properties use illegal chars - - - @Override - protected Character createValue() { - return randomChar(CHARSET); - } - - - @Override - protected Character createBadValue() { - return null; - } - - - @Override - protected PropertyDescriptor createProperty() { - return new CharacterProperty("testCharacter", "Test character property", 'a', 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new CharacterMultiProperty("testCharacter", "Test character property", - new Character[] {'a', 'b', 'c'}, 1.0f, DELIMITER); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new CharacterProperty("", "Test character property", 'a', 1.0f); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new CharacterMultiProperty("testCharacter", "Test character property", - new Character[] {'a', 'b', 'c'}, 1.0f, DELIMITER); - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/DoublePropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/DoublePropertyTest.java deleted file mode 100644 index 9b24dc2d0b..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/DoublePropertyTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - -/** - * Evaluates the functionality of the DoubleProperty descriptor by testing its - * ability to catch creation errors (illegal args), flag out-of-range test - * values, and serialize/deserialize groups of double values onto/from a string - * buffer. - * - * @author Brian Remedios - */ -@Deprecated -public class DoublePropertyTest extends AbstractNumericPropertyDescriptorTester { - - private static final double MIN = -10.0; - private static final double MAX = 100.0; - private static final double SHIFT = 5.0; - - - public DoublePropertyTest() { - super("Double"); - } - - - @Override - protected Double createValue() { - return randomDouble(MIN, MAX); - } - - - @Override - protected Double createBadValue() { - return randomBool() ? randomDouble(MIN - SHIFT, MIN - 0.01) : randomDouble(MAX + 0.01, MAX + SHIFT); - - } - - - protected DoubleProperty.DoublePBuilder singleBuilder() { - return DoubleProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValue(createValue()).uiOrder(1.0f); - } - - - protected DoubleMultiProperty.DoubleMultiPBuilder multiBuilder() { - return DoubleMultiProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValues(createValue(), createValue()).uiOrder(1.0f); - } - - - @Override - protected PropertyDescriptor createProperty() { - return new DoubleProperty("testDouble", "Test double property", MIN, MAX, 9.0, 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new DoubleMultiProperty("testDouble", "Test double property", MIN, MAX, - new Double[] {-1d, 0d, 1d, 2d}, 1.0f); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new DoubleProperty("testDouble", "Test double property", MAX, MIN, 9.0, 1.0f); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new DoubleMultiProperty("testDouble", "Test double property", MIN, MAX, - new Double[] {MIN - SHIFT, MIN, MIN + SHIFT, MAX + SHIFT}, 1.0f); - } - - - @Override - protected Double min() { - return MIN; - } - - - @Override - protected Double max() { - return MAX; - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/IntegerPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/IntegerPropertyTest.java deleted file mode 100644 index 437bcfc422..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/IntegerPropertyTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - - -/** - * Evaluates the functionality of the IntegerProperty descriptor by testing its - * ability to catch creation errors (illegal args), flag out-of-range test - * values, and serialize/deserialize groups of integers onto/from a string - * buffer. - * - * @author Brian Remedios - */ -@Deprecated -public class IntegerPropertyTest extends AbstractNumericPropertyDescriptorTester { - - private static final int MIN = 1; - private static final int MAX = 12; - private static final int SHIFT = 4; - - - public IntegerPropertyTest() { - super("Integer"); - } - - - /* @Override - @Test - public void testErrorForBadSingle() { - } // not until int properties get ranges - - @Override - @Test - public void testErrorForBadMulti() { - } // not until int properties get ranges - - */ - @Override - protected Integer createValue() { - return randomInt(MIN, MAX); - } - - - @Override - protected Integer createBadValue() { - return randomBool() ? randomInt(MIN - SHIFT, MIN - 1) : randomInt(MAX + 1, MAX + SHIFT); - } - - - protected IntegerProperty.IntegerPBuilder singleBuilder() { - return IntegerProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValue(createValue()).uiOrder(1.0f); - } - - - protected IntegerMultiProperty.IntegerMultiPBuilder multiBuilder() { - return IntegerMultiProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValues(createValue(), createValue()).uiOrder(1.0f); - } - - - @Override - protected PropertyDescriptor createProperty() { - return new IntegerProperty("testInteger", "Test integer property", MIN, MAX, MAX - 1, 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new IntegerMultiProperty("testInteger", "Test integer property", MIN, MAX, - new Integer[] {MIN, MIN + 1, MAX - 1, MAX}, 1.0f); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new IntegerProperty("", "Test integer property", MIN, MAX, MAX + 1, 1.0f); - - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new IntegerMultiProperty("testInteger", "", MIN, MAX, new Integer[] {MIN - 1, MAX}, 1.0f); - } - - - @Override - protected Integer min() { - return MIN; - } - - - @Override - protected Integer max() { - return MAX; - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/LongPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/LongPropertyTest.java deleted file mode 100644 index 878ec3f4c8..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/LongPropertyTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - - -/** - * @author Clรฉment Fournier - */ -@Deprecated -public class LongPropertyTest extends AbstractNumericPropertyDescriptorTester { - - private static final long MIN = 10L; - private static final long MAX = 11000L; - private static final long SHIFT = 300L; - - - public LongPropertyTest() { - super("Long"); - } - - - @Override - protected Long createValue() { - return randomLong(MIN, MAX); - } - - - @Override - protected Long createBadValue() { - return randomBool() ? randomLong(MIN - SHIFT, MIN) : randomLong(MAX + 1, MAX + SHIFT); - } - - - @Override - protected LongProperty.LongPBuilder singleBuilder() { - return LongProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValue(createValue()).uiOrder(1.0f); - } - - - @Override - protected LongMultiProperty.LongMultiPBuilder multiBuilder() { - return LongMultiProperty.named("test").desc("foo") - .range(MIN, MAX).defaultValues(createValue(), createValue()).uiOrder(1.0f); - } - - - @Override - protected PropertyDescriptor createProperty() { - return new LongProperty("testFloat", "Test float property", MIN, MAX, 90L, 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new LongMultiProperty("testFloat", "Test float property", MIN, MAX, - new Long[]{1000L, 10L, 100L, 20L}, 1.0f); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new LongProperty("testFloat", "Test float property", 200L, -400L, 900L, 1.0f); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new LongMultiProperty("testFloat", "Test float property", 0L, 5L, - new Long[]{-1000L, 0L, 100L, 20L}, 1.0f); - } - - - @Override - protected Long min() { - return MIN; - } - - - @Override - protected Long max() { - return MAX; - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/RegexPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/RegexPropertyTest.java deleted file mode 100644 index 09d9442eff..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/RegexPropertyTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; -import java.util.regex.Pattern; - - -/** - * Since there's no RegexMultiProperty the base class is only partially implemented, - * and some tests are overridden with no-op ones. - * - * @author Clรฉment Fournier - * @since 6.2.0 - */ -@Deprecated -public class RegexPropertyTest extends AbstractPropertyDescriptorTester { - public RegexPropertyTest() { - super("Regex"); - } - - - @Override - protected Pattern createValue() { - return Pattern.compile("abc++"); - } - - - @Override - protected Pattern createBadValue() { - return null; - } - - - @Override - protected PropertyDescriptor createProperty() { - return RegexProperty.named("foo").defaultValue("(ec|sa)+").desc("the description").build(); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return RegexProperty.named("foo").defaultValue("(ec|sa").desc("the description").build(); - } - - - // The following are deliberately unimplemented, since they are only relevant to the tests of the multiproperty - - @Override - protected PropertyDescriptor> createMultiProperty() { - throw new UnsupportedOperationException(); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - throw new UnsupportedOperationException(); - } - - @Override - public void testAddAttributesMulti() { - } - - - @Override - public void testAsDelimitedString() { - } - - - @Override - public void testErrorForBadMulti() { - } - - - @Override - public void testErrorForCorrectMulti() { - } - - - @Override - public void testFactoryMultiValueDefaultDelimiter() { - } - - - @Override - public void testFactoryMultiValueCustomDelimiter() { - } - - - @Override - public void testTypeMulti() { - } - - - @Override - public void testIsMultiValueMulti() { - } - - -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/StringPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/StringPropertyTest.java deleted file mode 100644 index 8385f22a19..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/StringPropertyTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - -/** - * Evaluates the functionality of the StringProperty descriptor by testing its - * ability to catch creation errors (illegal args), flag invalid strings per any - * specified expressions, and serialize/deserialize groups of strings onto/from - * a string buffer. - * - * @author Brian Remedios - */ -public class StringPropertyTest extends AbstractPropertyDescriptorTester { - - private static final int MAX_STRING_LENGTH = 52; - private static final char DELIMITER = '|'; - private static final char[] CHARSET = filter(ALL_CHARS.toCharArray(), DELIMITER); - - - public StringPropertyTest() { - super("String"); - } - - - @Override - protected String createValue() { - return newString(); - } - - - /** - * Method newString. - * - * @return String - */ - private String newString() { - - int strLength = randomInt(1, MAX_STRING_LENGTH); - - char[] chars = new char[strLength]; - for (int i = 0; i < chars.length; i++) { - chars[i] = randomCharIn(CHARSET); - } - return new String(chars); - } - - - /** - * Method randomCharIn. - * - * @param chars char[] - * - * @return char - */ - private char randomCharIn(char[] chars) { - return randomChar(chars); - } - - - @Override - protected String createBadValue() { - return null; - } - - - @Override - protected PropertyDescriptor createProperty() { - return new StringProperty("testString", "Test string property", "brian", 1.0f); - } - - - @Override - protected PropertyDescriptor> createMultiProperty() { - return new StringMultiProperty("testString", "Test string property", - new String[] {"hello", "world"}, 1.0f, DELIMITER); - } - - - @Override - protected PropertyDescriptor createBadProperty() { - return new StringProperty("", "Test string property", "brian", 1.0f); - } - - - @Override - protected PropertyDescriptor> createBadMultiProperty() { - return new StringMultiProperty("testString", "Test string property", - new String[] {"hello", "world", "a" + DELIMITER + "b"}, 1.0f, DELIMITER); - } -} From 95c40f671b21a13583ef37f36e3c8714595d7743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 12 Sep 2019 23:53:42 +0200 Subject: [PATCH 014/347] Fix ruleset writer --- .../net/sourceforge/pmd/RuleSetWriter.java | 103 +++++++----------- .../pmd/properties/internal/SeqSyntax.java | 8 +- .../pmd/properties/internal/SyntaxSet.java | 13 +-- .../pmd/properties/internal/ValueSyntax.java | 11 +- .../pmd/properties/internal/XmlSyntax.java | 5 +- .../sourceforge/pmd/rules/RuleFactory.java | 3 +- 6 files changed, 61 insertions(+), 82 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index dcbeb3d427..0f628084e8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -8,7 +8,6 @@ import java.io.OutputStream; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -25,6 +24,7 @@ import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.IOUtils; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.CDATASection; import org.w3c.dom.DOMException; import org.w3c.dom.Document; @@ -37,6 +37,7 @@ import net.sourceforge.pmd.lang.rule.ImmutableLanguage; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; +import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.internal.XmlSyntax; @@ -190,23 +191,20 @@ public class RuleSetWriter { String externalInfoUrl = ruleReference.getOverriddenExternalInfoUrl(); String description = ruleReference.getOverriddenDescription(); RulePriority priority = ruleReference.getOverriddenPriority(); - List> propertyDescriptors = ruleReference.getOverriddenPropertyDescriptors(); - Map, Object> propertiesByPropertyDescriptor = ruleReference - .getOverriddenPropertiesByPropertyDescriptor(); List examples = ruleReference.getOverriddenExamples(); return createSingleRuleElement(language, minimumLanguageVersion, maximumLanguageVersion, deprecated, - name, null, ref, message, externalInfoUrl, null, description, priority, - propertyDescriptors, propertiesByPropertyDescriptor, examples); + name, null, ref, message, externalInfoUrl, null, description, priority, + ruleReference, examples); } } else { return createSingleRuleElement(rule instanceof ImmutableLanguage ? null : rule.getLanguage(), - rule.getMinimumLanguageVersion(), rule.getMaximumLanguageVersion(), rule.isDeprecated(), - rule.getName(), rule.getSince(), null, rule.getMessage(), rule.getExternalInfoUrl(), - rule.getRuleClass(), - rule.getDescription(), - rule.getPriority(), rule.getPropertyDescriptors(), rule.getPropertiesByPropertyDescriptor(), - rule.getExamples()); + rule.getMinimumLanguageVersion(), rule.getMaximumLanguageVersion(), rule.isDeprecated(), + rule.getName(), rule.getSince(), null, rule.getMessage(), rule.getExternalInfoUrl(), + rule.getRuleClass(), + rule.getDescription(), + rule.getPriority(), rule, + rule.getExamples()); } } @@ -217,10 +215,9 @@ public class RuleSetWriter { } private Element createSingleRuleElement(Language language, LanguageVersion minimumLanguageVersion, - LanguageVersion maximumLanguageVersion, Boolean deprecated, String name, String since, String ref, - String message, String externalInfoUrl, String clazz, - String description, RulePriority priority, List> propertyDescriptors, - Map, Object> propertiesByPropertyDescriptor, List examples) { + LanguageVersion maximumLanguageVersion, Boolean deprecated, String name, String since, String ref, + String message, String externalInfoUrl, String clazz, + String description, RulePriority priority, PropertySource propertySource, List examples) { Element ruleElement = createRuleElement(); if (language != null) { ruleElement.setAttribute("language", language.getTerseName()); @@ -248,7 +245,7 @@ public class RuleSetWriter { Element priorityElement = createPriorityElement(priority); ruleElement.appendChild(priorityElement); } - Element propertiesElement = createPropertiesElement(propertyDescriptors, propertiesByPropertyDescriptor); + Element propertiesElement = createPropertiesElement(propertySource); if (propertiesElement != null) { ruleElement.appendChild(propertiesElement); } @@ -271,68 +268,42 @@ public class RuleSetWriter { return ruleSetReferenceElement; } - @SuppressWarnings("PMD.CompareObjectsWithEquals") - private Element createPropertiesElement(List> propertyDescriptors, - Map, Object> propertiesByPropertyDescriptor) { + @Nullable + private Element createPropertiesElement(PropertySource propertySource) { Element propertiesElement = null; - if (propertyDescriptors != null) { + List> overridden = propertySource.getOverriddenPropertyDescriptors(); + List> defined = propertySource.getPropertyDescriptors(); - for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { - // For each provided PropertyDescriptor + for (PropertyDescriptor descriptor : defined) { + // For each provided PropertyDescriptor - PropertyTypeId typeId = propertyDescriptor.getTypeId(); + PropertyTypeId typeId = descriptor.getTypeId(); - if (typeId != null) { - // Any externally defined property needs to go out as a definition. - if (propertiesElement == null) { - propertiesElement = createPropertiesElement(); - } + if (typeId == null // not defined externally + && !overridden.contains(descriptor)) { + continue; + } - Element propertyElement = createPropertyDefinitionElementBR(propertyDescriptor, typeId); - propertiesElement.appendChild(propertyElement); - } else { - if (propertiesByPropertyDescriptor != null) { - // Otherwise, any property which has a value different than the default needs to go out as a value. - Object defaultValue = propertyDescriptor.defaultValue(); - Object value = propertiesByPropertyDescriptor.get(propertyDescriptor); - if (!Objects.equals(value, defaultValue)) { - if (propertiesElement == null) { - propertiesElement = createPropertiesElement(); - } + if (propertiesElement == null) { + propertiesElement = createPropertiesElement(); + } - Element propertyElement = createPropertyValueElement(propertyDescriptor, value); - propertiesElement.appendChild(propertyElement); - } - } - } + if (typeId != null) { + propertiesElement.appendChild(createPropertyDefinitionElementBR(descriptor, typeId)); + } else { + propertiesElement.appendChild(propertyElementWithValue(propertySource, descriptor)); } } - if (propertiesByPropertyDescriptor != null) { - // Then, for each PropertyDescriptor not explicitly provided - for (Map.Entry, Object> entry : propertiesByPropertyDescriptor.entrySet()) { - // If not explicitly given... - PropertyDescriptor propertyDescriptor = entry.getKey(); - if (!propertyDescriptors.contains(propertyDescriptor)) { - // Otherwise, any property which has a value different than - // the - // default needs to go out as a value. - Object defaultValue = propertyDescriptor.defaultValue(); - Object value = entry.getValue(); - if (!Objects.equals(value, defaultValue)) { - if (propertiesElement == null) { - propertiesElement = createPropertiesElement(); - } - Element propertyElement = createPropertyValueElement(propertyDescriptor, value); - propertiesElement.appendChild(propertyElement); - } - } - } - } return propertiesElement; } + @NonNull + private Element propertyElementWithValue(PropertySource propertySource, PropertyDescriptor descriptor) { + return createPropertyValueElement(descriptor, propertySource.getProperty(descriptor)); + } + private Element createPropertyValueElement(PropertyDescriptor propertyDescriptor, T value) { Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property"); PropertyDescriptorField.NAME.setOn(element, propertyDescriptor.name()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java index 83e20d9d3f..980dc5efaa 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.properties.internal; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.function.Supplier; import org.w3c.dom.Element; @@ -51,10 +53,10 @@ public final class SeqSyntax> extends XmlSyntax { } @Override - public String example() { - return "<" + getWriteElementName() + ">\n" + public List examples() { + return Collections.singletonList("<" + getWriteElementName() + ">\n" + " " + itemSyntax.toString() + "\n" + " ..." - + ""; + + ""); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index 8800f47e5c..cc5325b5bb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.properties.internal; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -61,12 +62,11 @@ public final class SyntaxSet extends XmlSyntax { } @Override - public @Nullable T fromString(Element owner, String attributeData, XmlErrorReporter err) { + public @Nullable T fromString(String attributeData) { for (XmlSyntax syntax : supportedReadStrategies()) { if (syntax.supportsFromString()) { - // do not catch any exception, it will already have been reported on the error reporter. - return syntax.fromString(owner, attributeData, err); + return syntax.fromString(attributeData); } } @@ -121,10 +121,7 @@ public final class SyntaxSet extends XmlSyntax { private static String enquote(String it) {return "'" + it + "'";} @Override - public String example() { - if (readIndex.size() == 1) { - return readIndex.values().iterator().next().example(); - } - return "One of:\n" + readIndex.values().stream().map(XmlSyntax::example).collect(Collectors.joining("\nor\n")); + public List examples() { + return readIndex.values().stream().flatMap(it -> it.examples().stream()).collect(Collectors.toList()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index 5850f34df0..99b0418393 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.properties.internal; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -40,6 +42,11 @@ public final class ValueSyntax extends XmlSyntax { this.fromString = fromString; } + @Override + public boolean supportsFromString() { + return true; + } + @Override public T fromString(String attributeData) { return fromString.apply(attributeData); @@ -65,8 +72,8 @@ public final class ValueSyntax extends XmlSyntax { } @Override - public String example() { - return "<" + getWriteElementName() + ">data"; + public List examples() { + return Collections.singletonList("<" + getWriteElementName() + ">data"); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java index 5c90fd33dc..f1514bba17 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.properties.internal; import java.util.Collections; +import java.util.List; import java.util.Set; import org.w3c.dom.Element; @@ -59,10 +60,10 @@ public abstract class XmlSyntax { return readNames; } - public abstract String example(); + public abstract List examples(); @Override public String toString() { - return example(); + return examples().get(0); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 3114a7ed2e..5bbaec83ec 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -15,6 +15,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Element; @@ -360,7 +361,7 @@ public class RuleFactory { } else { throw err.error(propertyElement.getAttributeNode(DEFAULT_VALUE.attributeName()), "Type " + typeId + " cannot be parsed from a string, use a nested element, e.g. " - + syntax.example()); + + String.join("\nor\n", syntax.examples())); } } else { NodeList children = propertyElement.getElementsByTagName(DEFAULT_VALUE.attributeName()); From 007fe9113a23985789cc86e5fbd2b2e6ed30fb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 00:02:07 +0200 Subject: [PATCH 015/347] Remove unsupported tests --- .../net/sourceforge/pmd/RuleSetFactoryTest.java | 16 ---------------- .../pmd/renderers/AbstractRendererTest.java | 3 +-- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 1a3aa47c96..d2d5332458 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -192,22 +192,6 @@ public class RuleSetFactoryTest { assertEquals(Arrays.asList("com.aptsssss", "com.abc"), values); } - @Test - public void testStringMultiPropertyDelimiter() throws Exception { - Rule r = loadFirstRule("\n" + "\n " - + " ruleset desc\n " - + "\n" - + " Please move your class to the right folder(rest \nfolder)\n" - + " 2\n \n \n" - + " " + ""); - PropertyDescriptor> prop = (PropertyDescriptor>) r.getPropertyDescriptor("packageRegEx"); - List values = r.getProperty(prop); - assertEquals(Arrays.asList("com.aptsssss", "com.abc"), values); - } - @Test public void testRuleSetWithDeprecatedRule() throws Exception { RuleSet rs = loadRuleSet("\n" + "\n" diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java index cc959120ea..8520293771 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java @@ -94,8 +94,7 @@ public abstract class AbstractRendererTest { ctx.setSourceCodeFile(new File(getSourceCodeFilename())); Report report = new Report(); RuleWithProperties theRule = new RuleWithProperties(); - theRule.setProperty(RuleWithProperties.STRING_PROPERTY_DESCRIPTOR, - "the string value\nsecond line with \"quotes\""); + theRule.setProperty(RuleWithProperties.STRING_PROPERTY_DESCRIPTOR, "the string value\nsecond line with \"quotes\""); report.addRuleViolation(new ParametricRuleViolation(theRule, ctx, node, "blah")); String rendered = ReportTest.render(getRenderer(), report); assertEquals(filter(getExpectedWithProperties()), filter(rendered)); From af40bad2a6b77a1299c5895fd99423363f0f2ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 00:18:10 +0200 Subject: [PATCH 016/347] Delete more property types --- .../net/sourceforge/pmd/RuleSetWriter.java | 8 +- .../pmd/properties/DoubleMultiProperty.java | 115 ---------------- .../pmd/properties/IntegerMultiProperty.java | 123 ------------------ .../pmd/properties/LongMultiProperty.java | 121 ----------------- .../pmd/properties/LongProperty.java | 116 ----------------- 5 files changed, 1 insertion(+), 482 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleMultiProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerMultiProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/LongMultiProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/LongProperty.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 0f628084e8..7ca76d7c97 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd; import java.io.OutputStream; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -324,12 +323,7 @@ public class RuleSetWriter { PropertyDescriptorField.NAME.setOn(element, propertyDescriptor.name()); PropertyDescriptorField.TYPE.setOn(element, typeId.getStringId()); PropertyDescriptorField.DESCRIPTION.setOn(element, propertyDescriptor.description()); - - Map propertyValuesById = propertyDescriptor.attributeValuesById(); - for (Map.Entry entry : propertyValuesById.entrySet()) { - element.setAttribute(entry.getKey().attributeName(), entry.getValue()); - } - + // TODO support property constraints in XML return element; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleMultiProperty.java deleted file mode 100644 index 8040a66051..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleMultiProperty.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Arrays; -import java.util.List; - -import net.sourceforge.pmd.properties.builders.MultiNumericPropertyBuilder; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; - - -/** - * Multi-valued double property. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * - * @deprecated Use a {@code PropertyDescriptor>} instead. A builder is available from {@link PropertyFactory#doubleListProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class DoubleMultiProperty extends AbstractMultiNumericProperty { - - /** - * Constructor using an array of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues Array of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated use {@link PropertyFactory#doubleListProperty(String)} - */ - @Deprecated - public DoubleMultiProperty(String theName, String theDescription, Double min, Double max, - Double[] defaultValues, float theUIOrder) { - this(theName, theDescription, min, max, Arrays.asList(defaultValues), theUIOrder, false); - } - - - /** Master constructor. */ - private DoubleMultiProperty(String theName, String theDescription, Double min, Double max, - List defaultValues, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, min, max, defaultValues, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues List of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated use {@link PropertyFactory#doubleListProperty(String)} - */ - @Deprecated - public DoubleMultiProperty(String theName, String theDescription, Double min, Double max, - List defaultValues, float theUIOrder) { - this(theName, theDescription, min, max, defaultValues, theUIOrder, false); - } - - - @Override - public Class type() { - return Double.class; - } - - - @Override - protected Double createFrom(String value) { - return Double.valueOf(value); - } - - - static PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric(Double.class, ValueParserConstants.DOUBLE_PARSER) { - @Override - protected DoubleMultiPBuilder newBuilder(String name) { - return new DoubleMultiPBuilder(name); - } - }; - } - - /** @deprecated use {@link PropertyFactory#doubleListProperty(String)} */ - @Deprecated - public static DoubleMultiPBuilder named(String name) { - return new DoubleMultiPBuilder(name); - } - - - /** @deprecated use {@link PropertyFactory#doubleListProperty(String)} */ - @Deprecated - public static final class DoubleMultiPBuilder extends MultiNumericPropertyBuilder { - private DoubleMultiPBuilder(String name) { - super(name); - } - - - @Override - public DoubleMultiProperty build() { - return new DoubleMultiProperty(name, description, lowerLimit, upperLimit, defaultValues, uiOrder, isDefinedInXML); - } - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerMultiProperty.java deleted file mode 100644 index fbdb753d39..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerMultiProperty.java +++ /dev/null @@ -1,123 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Arrays; -import java.util.List; - -import net.sourceforge.pmd.properties.builders.MultiNumericPropertyBuilder; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; - - -/** - * Multi-valued integer property. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * - * - * @deprecated Use a {@code PropertyDescriptor>} instead. A builder is available from {@link PropertyFactory#intListProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class IntegerMultiProperty extends AbstractMultiNumericProperty { - - - /** - * Constructor using an array of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues Array of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#intListProperty(String)} - */ - @Deprecated - public IntegerMultiProperty(String theName, String theDescription, Integer min, Integer max, - Integer[] defaultValues, float theUIOrder) { - this(theName, theDescription, min, max, Arrays.asList(defaultValues), theUIOrder, false); - } - - - /** Master constructor. */ - private IntegerMultiProperty(String theName, String theDescription, Integer min, Integer max, - List defaultValues, float theUIOrder, boolean isDefinedExternally) { - - super(theName, theDescription, min, max, defaultValues, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues List of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#intListProperty(String)} - */ - @Deprecated - public IntegerMultiProperty(String theName, String theDescription, Integer min, Integer max, - List defaultValues, float theUIOrder) { - - this(theName, theDescription, min, max, defaultValues, theUIOrder, false); - } - - - @Override - public Class type() { - return Integer.class; - } - - - @Override - protected Integer createFrom(String toParse) { - return Integer.valueOf(toParse); - } - - - static PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric(Integer.class, ValueParserConstants.INTEGER_PARSER) { - @Override - protected IntegerMultiPBuilder newBuilder(String name) { - return new IntegerMultiPBuilder(name); - } - }; - } - - - /** - * @deprecated Use {@link PropertyFactory#intListProperty(String)} - */ - @Deprecated - public static IntegerMultiPBuilder named(String name) { - return new IntegerMultiPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#intListProperty(String)} - */ - @Deprecated - public static final class IntegerMultiPBuilder extends MultiNumericPropertyBuilder { - private IntegerMultiPBuilder(String name) { - super(name); - } - - - @Override - public IntegerMultiProperty build() { - return new IntegerMultiProperty(name, description, lowerLimit, upperLimit, defaultValues, uiOrder, isDefinedInXML); - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/LongMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/LongMultiProperty.java deleted file mode 100644 index 3634c506b9..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/LongMultiProperty.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Arrays; -import java.util.List; - -import net.sourceforge.pmd.properties.builders.MultiNumericPropertyBuilder; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; - - -/** - * Multi-valued long property. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * - * @deprecated Use a {@code PropertyDescriptor>} instead. A builder is available from {@link PropertyFactory#longIntListProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class LongMultiProperty extends AbstractMultiNumericProperty { - - - /** - * Constructor using an array of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues Array of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#longIntListProperty(String)} - */ - @Deprecated - public LongMultiProperty(String theName, String theDescription, Long min, Long max, - Long[] defaultValues, float theUIOrder) { - this(theName, theDescription, min, max, Arrays.asList(defaultValues), theUIOrder, false); - } - - - /** Master constructor. */ - private LongMultiProperty(String theName, String theDescription, Long min, Long max, - List defaultValues, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, min, max, defaultValues, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param defaultValues List of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#longIntListProperty(String)} - */ - @Deprecated - public LongMultiProperty(String theName, String theDescription, Long min, Long max, - List defaultValues, float theUIOrder) { - this(theName, theDescription, min, max, defaultValues, theUIOrder, false); - } - - - @Override - public Class type() { - return Long.class; - } - - - @Override - protected Long createFrom(String value) { - return Long.valueOf(value); - } - - - static PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric(Long.class, ValueParserConstants.LONG_PARSER) { - @Override - protected LongMultiPBuilder newBuilder(String name) { - return new LongMultiPBuilder(name); - } - }; - } - - - /** @deprecated Use {@link PropertyFactory#longIntListProperty(String)} */ - @Deprecated - public static LongMultiPBuilder named(String name) { - return new LongMultiPBuilder(name); - } - - - /** @deprecated Use {@link PropertyFactory#longIntListProperty(String)} */ - @Deprecated - public static final class LongMultiPBuilder - extends MultiNumericPropertyBuilder { - - protected LongMultiPBuilder(String name) { - super(name); - } - - - @Override - public LongMultiProperty build() { - return new LongMultiProperty(name, description, lowerLimit, upperLimit, - defaultValues, uiOrder, isDefinedInXML); - } - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/LongProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/LongProperty.java deleted file mode 100644 index e420a3b739..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/LongProperty.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; -import net.sourceforge.pmd.properties.builders.SingleNumericPropertyBuilder; - - -/** - * Single valued long property. - * - * @author Brian Remedios - * @author Clรฉment Fournier - * @version Refactored June 2017 (6.0.0) - * - * @deprecated Use a {@code PropertyDescriptor} instead. A builder is available from {@link PropertyFactory#longIntProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class LongProperty extends AbstractNumericProperty { - - - /** - * Constructor for LongProperty that limits itself to a single value within the specified limits. Converts string - * arguments into the Long values. - * - * @param theName Name - * @param theDescription Description - * @param minStr Minimum value of the property - * @param maxStr Maximum value of the property - * @param defaultStr Default value - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#longIntProperty(String)} - */ - @Deprecated - public LongProperty(String theName, String theDescription, String minStr, String maxStr, String defaultStr, - float theUIOrder) { - this(theName, theDescription, Long.valueOf(minStr), Long.valueOf(maxStr), - Long.valueOf(defaultStr), theUIOrder, false); - } - - - /** Master constructor. */ - private LongProperty(String theName, String theDescription, Long min, Long max, Long theDefault, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, min, max, theDefault, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor that limits itself to a single value within the specified limits. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param theDefault Default value - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#longIntProperty(String)} - */ - @Deprecated - public LongProperty(String theName, String theDescription, Long min, Long max, Long theDefault, float theUIOrder) { - this(theName, theDescription, min, max, theDefault, theUIOrder, false); - } - - - @Override - public Class type() { - return Long.class; - } - - - @Override - protected Long createFrom(String toParse) { - return Long.valueOf(toParse); - } - - - static PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric(Long.class, ValueParserConstants.LONG_PARSER) { - @Override - protected LongPBuilder newBuilder(String name) { - return new LongPBuilder(name); - } - }; - } - - - /** @deprecated Use {@link PropertyFactory#longIntProperty(String)} */ - @Deprecated - public static LongPBuilder named(String name) { - return new LongPBuilder(name); - } - - - /** @deprecated Use {@link PropertyFactory#longIntProperty(String)} */ - @Deprecated - public static final class LongPBuilder extends SingleNumericPropertyBuilder { - private LongPBuilder(String name) { - super(name); - } - - - @Override - public LongProperty build() { - return new LongProperty(name, description, lowerLimit, upperLimit, defaultValue, uiOrder, isDefinedInXML); - } - } - -} From 6c3a3b1c5e44df60e9607a3b71941a06c3c37eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 00:25:00 +0200 Subject: [PATCH 017/347] Remove attribute values from property descriptor --- .../sourceforge/pmd/properties/AbstractProperty.java | 9 --------- .../sourceforge/pmd/properties/PropertyDescriptor.java | 10 ---------- 2 files changed, 19 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java index 1ceddd36d3..9bcf47a54d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java @@ -8,7 +8,6 @@ import static net.sourceforge.pmd.properties.PropertyDescriptorField.DEFAULT_VAL import static net.sourceforge.pmd.properties.PropertyDescriptorField.DESCRIPTION; import static net.sourceforge.pmd.properties.PropertyDescriptorField.NAME; -import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; @@ -102,14 +101,6 @@ import org.apache.commons.lang3.StringUtils; } - @Override - public final Map attributeValuesById() { - Map values = new HashMap<>(); - addAttributesTo(values); - return values; - } - - /** * Adds this property's attributes to the map. Subclasses can override this to add more {@link * PropertyDescriptorField}. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index d4d2f9ffb6..5c37381b91 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -173,14 +173,4 @@ public interface PropertyDescriptor { String propertyErrorFor(Rule rule); - /** - * Returns a map representing all the property attributes of the receiver in string form. - * - * @deprecated Will be removed with 7.0.0 - * @return map - */ - @Deprecated - Map attributeValuesById(); - - } From 453129a438a72eeb04eca57c34704ad90d9bc5a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 00:30:51 +0200 Subject: [PATCH 018/347] Remove variable naming conventions rule --- .../pmd/properties/BooleanMultiProperty.java | 112 ---------- .../pmd/properties/BooleanProperty.java | 11 - .../properties/CharacterMultiProperty.java | 140 ------------ .../pmd/properties/CharacterProperty.java | 11 - .../pmd/properties/DoubleProperty.java | 11 - .../pmd/properties/FileProperty.java | 11 - .../pmd/properties/IntegerProperty.java | 11 - .../pmd/properties/PropertyDescriptor.java | 4 - .../pmd/properties/PropertyTypeId.java | 1 - .../pmd/properties/RegexProperty.java | 12 - .../pmd/properties/StringMultiProperty.java | 11 - .../pmd/properties/StringProperty.java | 11 - ...rtyDescriptorBuilderConversionWrapper.java | 207 ------------------ 13 files changed, 553 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanMultiProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterMultiProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanMultiProperty.java deleted file mode 100644 index 1a2f6d4028..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanMultiProperty.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * 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.BOOLEAN_PARSER; - -import java.util.Arrays; -import java.util.List; - -import net.sourceforge.pmd.properties.builders.MultiValuePropertyBuilder; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; - - -/** - * Defines a property type that supports multiple Boolean values. - * - * @author Brian Remedios - * @deprecated Not useful, will be removed with 7.0.0 - */ -@Deprecated -public final class BooleanMultiProperty extends AbstractMultiValueProperty { - - - /** - * Constructor using an array of defaults. - * - * @param theName Name - * @param theDescription Description - * @param defaultValues List of defaults - * @param theUIOrder UI order - * - * @deprecated Not useful, will be removed with 7.0.0 - */ - @Deprecated - public BooleanMultiProperty(String theName, String theDescription, Boolean[] defaultValues, float theUIOrder) { - this(theName, theDescription, Arrays.asList(defaultValues), theUIOrder, false); - } - - - /** Master constructor. */ - private BooleanMultiProperty(String theName, String theDescription, List defaultValues, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, defaultValues, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param defaultValues List of defaults - * @param theUIOrder UI order - * - * @deprecated Not useful, will be removed with 7.0.0 - */ - @Deprecated - public BooleanMultiProperty(String theName, String theDescription, List defaultValues, float theUIOrder) { - this(theName, theDescription, defaultValues, theUIOrder, false); - } - - - @Override - protected Boolean createFrom(String toParse) { - return BOOLEAN_PARSER.valueOf(toParse); - } - - - @Override - public Class type() { - return Boolean.class; - } - - - static PropertyDescriptorBuilderConversionWrapper.MultiValue extractor() { - return new PropertyDescriptorBuilderConversionWrapper.MultiValue(Boolean.class, ValueParserConstants.BOOLEAN_PARSER) { - @Override - protected BooleanMultiPBuilder newBuilder(String name) { - return new BooleanMultiPBuilder(name); - } - }; - } - - - /** - * @deprecated Not useful, will be removed with 7.0.0 - */ - @Deprecated - public static BooleanMultiPBuilder named(String name) { - return new BooleanMultiPBuilder(name); - } - - - /** - * @deprecated Not useful, will be removed with 7.0.0 - */ - @Deprecated - public static final class BooleanMultiPBuilder extends MultiValuePropertyBuilder { - private BooleanMultiPBuilder(String name) { - super(name); - } - - - @Override - public BooleanMultiProperty build() { - return new BooleanMultiProperty(this.name, this.description, this.defaultValues, this.uiOrder, isDefinedInXML); - } - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java index 7614a2fa15..eb216ff73a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.properties.ValueParserConstants.BOOLEAN_PARSER; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; @@ -71,16 +70,6 @@ public final class BooleanProperty extends AbstractSingleValueProperty } - static PropertyDescriptorBuilderConversionWrapper.SingleValue extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue(Boolean.class, ValueParserConstants.BOOLEAN_PARSER) { - @Override - protected BooleanPBuilder newBuilder(String name) { - return new BooleanPBuilder(name); - } - }; - } - - /** * @deprecated Use {@link PropertyFactory#booleanProperty(String)} or its overloads. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterMultiProperty.java deleted file mode 100644 index db45c30273..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterMultiProperty.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - -import net.sourceforge.pmd.properties.builders.MultiValuePropertyBuilder; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; - - -/** - * Multi-valued character property. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor>}. A builder is available from {@link PropertyFactory#charListProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class CharacterMultiProperty extends AbstractMultiValueProperty { - - - /** - * Constructor using an array of defaults. - * - * @param theName Name - * @param theDescription Description - * @param defaultValues Array of defaults - * @param theUIOrder UI order - * @param delimiter The delimiter to use - * - * @throws IllegalArgumentException if the delimiter is in the default values - * @deprecated Use {@link PropertyFactory#charListProperty(String)} - */ - @Deprecated - public CharacterMultiProperty(String theName, String theDescription, Character[] defaultValues, float theUIOrder, char delimiter) { - this(theName, theDescription, Arrays.asList(defaultValues), theUIOrder, delimiter, false); - } - - - /** Master constructor. */ - private CharacterMultiProperty(String theName, String theDescription, List defaultValues, float theUIOrder, - char delimiter, boolean isDefinedExternally) { - super(theName, theDescription, defaultValues, theUIOrder, delimiter, isDefinedExternally); - - if (defaultValues != null) { - for (Character c : defaultValues) { - if (c == delimiter) { - throw new IllegalArgumentException("Cannot include the delimiter in the set of defaults"); - } - } - } - } - - - /** - * Constructor using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param defaultValues List of defaults - * @param theUIOrder UI order - * @param delimiter The delimiter to use - * - * @throws IllegalArgumentException if the delimiter is in the default values - * @deprecated Use {@link PropertyFactory#charListProperty(String)} - */ - @Deprecated - public CharacterMultiProperty(String theName, String theDescription, List defaultValues, float theUIOrder, char delimiter) { - this(theName, theDescription, defaultValues, theUIOrder, delimiter, false); - } - - - @Override - protected Character createFrom(String toParse) { - return CharacterProperty.charFrom(toParse); - } - - - @Override - public Class type() { - return Character.class; - } - - - @Override - public List valueFrom(String valueString) throws IllegalArgumentException { - String[] values = StringUtils.split(valueString, multiValueDelimiter()); - - List chars = new ArrayList<>(values.length); - for (int i = 0; i < values.length; i++) { - chars.add(values[i].charAt(0)); - } - return chars; - } - - - static PropertyDescriptorBuilderConversionWrapper.MultiValue extractor() { - return new PropertyDescriptorBuilderConversionWrapper.MultiValue(Character.class, ValueParserConstants.CHARACTER_PARSER) { - @Override - protected CharacterMultiPBuilder newBuilder(String name) { - return new CharacterMultiPBuilder(name); - } - }; - } - - - /** - * @deprecated Use {@link PropertyFactory#charListProperty(String)} - */ - @Deprecated - public static CharacterMultiPBuilder named(String name) { - return new CharacterMultiPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#charListProperty(String)} - */ - @Deprecated - public static final class CharacterMultiPBuilder extends MultiValuePropertyBuilder { - private CharacterMultiPBuilder(String name) { - super(name); - } - - - @Override - public CharacterMultiProperty build() { - return new CharacterMultiProperty(this.name, this.description, this.defaultValues, this.uiOrder, multiValueDelimiter, isDefinedInXML); - } - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java index 5cb8752b5d..e22e5f874b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.properties.ValueParserConstants.CHARACTER_PARSER; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; @@ -84,16 +83,6 @@ public final class CharacterProperty extends AbstractSingleValueProperty extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue(Character.class, ValueParserConstants.CHARACTER_PARSER) { - @Override - protected CharacterPBuilder newBuilder(String name) { - return new CharacterPBuilder(name); - } - }; - } - - /** * @deprecated Use {@link PropertyFactory#charProperty(String)} */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java index 0fdabe9da4..6faae6e74a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.properties.ValueParserConstants.DOUBLE_PARSER; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.SingleNumericPropertyBuilder; @@ -95,16 +94,6 @@ public final class DoubleProperty extends AbstractNumericProperty { } - static PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric(Double.class, ValueParserConstants.DOUBLE_PARSER) { - @Override - protected DoublePBuilder newBuilder(String name) { - return new DoublePBuilder(name); - } - }; - } - - /** * @deprecated Use {@link PropertyFactory#doubleProperty(String)}. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java index ec833682d2..0594528d6a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java @@ -8,7 +8,6 @@ import java.io.File; import org.apache.commons.lang3.StringUtils; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; @@ -54,16 +53,6 @@ public final class FileProperty extends AbstractSingleValueProperty { } - static PropertyDescriptorBuilderConversionWrapper.SingleValue extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue(File.class, ValueParserConstants.FILE_PARSER) { - @Override - protected FilePBuilder newBuilder(String name) { - return new FilePBuilder(name); - } - }; - } - - public static FilePBuilder named(String name) { return new FilePBuilder(name); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java index e7628d38c4..bce52b0cbe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.properties.ValueParserConstants.INTEGER_PARSER; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.SingleNumericPropertyBuilder; @@ -62,16 +61,6 @@ public final class IntegerProperty extends AbstractNumericProperty { } - static PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric(Integer.class, ValueParserConstants.INTEGER_PARSER) { - @Override - protected IntegerPBuilder newBuilder(String name) { - return new IntegerPBuilder(name); - } - }; - } - - /** * @deprecated Use {@link PropertyFactory#intProperty(String)} */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 5c37381b91..458f0511a6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -4,13 +4,9 @@ package net.sourceforge.pmd.properties; -import java.util.Map; - import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.RuleSetWriter; -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.properties.internal.ValueSyntax; import net.sourceforge.pmd.properties.internal.XmlSyntax; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 507e89603c..1279ce795c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -9,7 +9,6 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; import net.sourceforge.pmd.properties.internal.XmlSyntax; import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java index 3904893b33..93c7e9858e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.properties; import java.util.regex.Pattern; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper.SingleValue; import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; @@ -41,16 +39,6 @@ public final class RegexProperty extends AbstractSingleValueProperty { } - static SingleValue extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue(Pattern.class, ValueParserConstants.REGEX_PARSER) { - @Override - protected RegexPBuilder newBuilder(String name) { - return new RegexPBuilder(name); - } - }; - } - - /** * Creates a new builder for a regex property. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java index 6ef59d7bb6..5940c2e64d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java @@ -10,7 +10,6 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.builders.MultiValuePropertyBuilder; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; /** @@ -144,16 +143,6 @@ public final class StringMultiProperty extends AbstractMultiValueProperty extractor() { - return new PropertyDescriptorBuilderConversionWrapper.MultiValue(String.class, ValueParserConstants.STRING_PARSER) { - @Override - protected StringMultiPBuilder newBuilder(String name) { - return new StringMultiPBuilder(name); - } - }; - } - - /** * @deprecated Use {@link PropertyFactory#stringListProperty(String)} */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java index d48c78187c..563f2e5e8e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.properties; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; @@ -54,16 +53,6 @@ public final class StringProperty extends AbstractSingleValueProperty { } - static PropertyDescriptorBuilderConversionWrapper.SingleValue extractor() { - return new PropertyDescriptorBuilderConversionWrapper.SingleValue(String.class, ValueParserConstants.STRING_PARSER) { - @Override - protected StringPBuilder newBuilder(String name) { - return new StringPBuilder(name); - } - }; - } - - /** * @deprecated Use {@link PropertyFactory#stringProperty(String)} */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java deleted file mode 100644 index 51fad870e1..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilderConversionWrapper.java +++ /dev/null @@ -1,207 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.builders; - - -import static net.sourceforge.pmd.properties.PropertyDescriptorField.DELIMITER; -import static net.sourceforge.pmd.properties.PropertyDescriptorField.LEGAL_PACKAGES; - -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; - -import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyDescriptorField; -import net.sourceforge.pmd.properties.internal.StringParser; -import net.sourceforge.pmd.properties.ValueParserConstants; - - -/** - * Wraps a property builder and maps its inputs from strings to the target types of the descriptor. - * - * @param Value type of the descriptor - * @param Concrete type of the underlying builder - * - * @deprecated This was not public API and will be removed by 7.0.0 - * @author Clรฉment Fournier - * @since 6.0.0 - */ -@Deprecated -public abstract class PropertyDescriptorBuilderConversionWrapper> - implements PropertyDescriptorExternalBuilder { - - private final Class valueType; - - - protected PropertyDescriptorBuilderConversionWrapper(Class valueType) { - this.valueType = valueType; - } - - - /** Populates the builder with extracted fields. To be overridden. */ - protected void populate(T builder, Map fields) { - builder.desc(fields.get(PropertyDescriptorField.DESCRIPTION)); - } - - - @Override - public abstract boolean isMultiValue(); - - - @Override - public Class valueType() { - return valueType; - } - - - protected abstract T newBuilder(String name); // FUTURE 1.8: use a Supplier constructor parameter - - - @Override - public PropertyDescriptor build(Map fields) { - T builder = newBuilder(fields.get(PropertyDescriptorField.NAME)); - populate(builder, fields); - builder.isDefinedInXML = true; - return builder.build(); - } - - - private static char delimiterIn(Map valuesById, char defalt) { - String characterStr = ""; - if (valuesById.containsKey(DELIMITER)) { - characterStr = valuesById.get(DELIMITER).trim(); - } - - if (StringUtils.isBlank(characterStr)) { - return defalt; - } - - if (characterStr.length() != 1) { - throw new RuntimeException("Ambiguous delimiter character, must have length 1: \"" + characterStr + "\""); - } - return characterStr.charAt(0); - } - - - /** - * For multi-value properties. - * - * @param Element type of the list - * @param Concrete type of the underlying builder - */ - public abstract static class MultiValue> - extends PropertyDescriptorBuilderConversionWrapper, T> { - - protected final StringParser parser; - - - protected MultiValue(Class valueType, StringParser parser) { - super(valueType); - this.parser = parser; - } - - - @Override - protected void populate(T builder, Map fields) { - super.populate(builder, fields); - char delim = delimiterIn(fields, builder.multiValueDelimiter); - builder.delim(delim).defaultValues(ValueParserConstants.multi(parser, delim) - .valueOf(fields.get(PropertyDescriptorField.DEFAULT_VALUE))); - } - - - @Override - public boolean isMultiValue() { - return true; - } - - - /** - * For multi-value numeric properties. - * - * @param Element type of the list - * @param Concrete type of the underlying builder - */ - public abstract static class Numeric> - extends MultiValue { - - protected Numeric(Class valueType, StringParser parser) { - super(valueType, parser); - } - - - @Override - protected void populate(T builder, Map fields) { - super.populate(builder, fields); - V min = parser.valueOf(fields.get(PropertyDescriptorField.MIN)); - V max = parser.valueOf(fields.get(PropertyDescriptorField.MAX)); - builder.range(min, max); - } - } - - - } - - - /** - * For single-value properties. - * - * @param Value type of the property - * @param Concrete type of the underlying builder - */ - public abstract static class SingleValue> - extends PropertyDescriptorBuilderConversionWrapper { - - protected final StringParser parser; - - - protected SingleValue(Class valueType, StringParser parser) { - super(valueType); - this.parser = parser; - } - - - @Override - protected void populate(T builder, Map fields) { - super.populate(builder, fields); - builder.defaultValue(parser.valueOf(fields.get(PropertyDescriptorField.DEFAULT_VALUE))); - } - - - @Override - public boolean isMultiValue() { - return false; - } - - - /** - * For single-value numeric properties. - * - * @param Element type of the list - * @param Concrete type of the underlying builder - */ - public abstract static class Numeric> - extends SingleValue { - - protected Numeric(Class valueType, StringParser parser) { - super(valueType, parser); - } - - - @Override - protected void populate(T builder, Map fields) { - super.populate(builder, fields); - V min = parser.valueOf(fields.get(PropertyDescriptorField.MIN)); - V max = parser.valueOf(fields.get(PropertyDescriptorField.MAX)); - builder.range(min, max); - } - } - - - } - -} From f13f250e9205e68f68bf03e856070e3cd0e59683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 01:04:18 +0200 Subject: [PATCH 019/347] Remove more stuff --- .../pmd/lang/rule/AbstractDelegateRule.java | 6 - .../AbstractMultiNumericProperty.java | 77 ------- .../AbstractMultiValueProperty.java | 201 ------------------ .../pmd/properties/AbstractProperty.java | 9 - .../properties/AbstractPropertySource.java | 9 - .../AbstractSingleValueProperty.java | 21 -- .../pmd/properties/BooleanProperty.java | 6 - .../pmd/properties/CharacterProperty.java | 6 - .../pmd/properties/DoubleProperty.java | 33 --- .../pmd/properties/FileProperty.java | 6 - .../GenericMultiValuePropertyDescriptor.java | 84 -------- .../properties/GenericPropertyDescriptor.java | 50 +++-- .../pmd/properties/IntegerProperty.java | 33 --- .../MultiValuePropertyDescriptor.java | 11 +- .../pmd/properties/PropertyBuilder.java | 59 ++--- .../pmd/properties/PropertyDescriptor.java | 56 +---- .../pmd/properties/PropertyFactory.java | 16 +- .../pmd/properties/PropertySource.java | 14 -- .../pmd/properties/RegexProperty.java | 6 - .../SingleValuePropertyDescriptor.java | 4 - .../pmd/properties/StringMultiProperty.java | 170 --------------- .../pmd/properties/StringProperty.java | 6 - .../builders/MultiNumericPropertyBuilder.java | 47 ---- .../builders/MultiValuePropertyBuilder.java | 76 ------- .../builders/PropertyDescriptorBuilder.java | 1 - .../SingleNumericPropertyBuilder.java | 41 ---- .../pmd/properties/internal/SyntaxSet.java | 6 +- .../pmd/properties/internal/ValueSyntax.java | 3 +- .../pmd/properties/internal/XmlSyntax.java | 14 +- .../properties/internal/XmlSyntaxUtils.java | 10 +- .../sourceforge/pmd/rules/RuleFactory.java | 3 +- .../pmd/docs/RuleDocGenerator.java | 9 +- .../pmd/AbstractRuleSetFactoryTest.java | 2 +- 33 files changed, 118 insertions(+), 977 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiNumericProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiValueProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiNumericPropertyBuilder.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiValuePropertyBuilder.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SingleNumericPropertyBuilder.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java index f34e442045..7c15a8adfc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java @@ -14,7 +14,6 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertySource; @@ -213,11 +212,6 @@ public abstract class AbstractDelegateRule implements Rule { } - @Override - public void setProperty(MultiValuePropertyDescriptor propertyDescriptor, V... values) { - rule.setProperty(propertyDescriptor, values); - } - @Override public boolean isPropertyOverridden(PropertyDescriptor propertyDescriptor) { return rule.isPropertyOverridden(propertyDescriptor); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiNumericProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiNumericProperty.java deleted file mode 100644 index 2f2d212aff..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiNumericProperty.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; -import java.util.Map; - -import net.sourceforge.pmd.properties.modules.NumericPropertyModule; - - -/** - * Base class for multi-valued numeric properties. - * - * @param The type of number - * - * @author Brian Remedios - * @author Clรฉment Fournier - * @version Refactored June 2017 (6.0.0) - */ -@Deprecated -/* default */ abstract class AbstractMultiNumericProperty extends AbstractMultiValueProperty - implements NumericPropertyDescriptor> { - - private final NumericPropertyModule module; - - - /** - * Constructor for a multi-valued numeric property using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param lower Minimum value of the property - * @param upper Maximum value of the property - * @param theDefault List of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if lower > upper, or one of them is null, or one of the defaults is not between - * the bounds - */ - AbstractMultiNumericProperty(String theName, String theDescription, T lower, T upper, List theDefault, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, theDefault, theUIOrder, isDefinedExternally); - - module = new NumericPropertyModule<>(lower, upper); - for (T num : theDefault) { - module.checkNumber(num); - } - } - - - @Override - protected String valueErrorFor(T value) { - return module.valueErrorFor(value); - } - - - @Override - public Number lowerLimit() { - return module.getLowerLimit(); - } - - - @Override - public Number upperLimit() { - return module.getUpperLimit(); - } - - - @Override - protected void addAttributesTo(Map attributes) { - super.addAttributesTo(attributes); - module.addAttributesTo(attributes); - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiValueProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiValueProperty.java deleted file mode 100644 index c1b2df0fe7..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractMultiValueProperty.java +++ /dev/null @@ -1,201 +0,0 @@ -/** - * 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.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; - -import net.sourceforge.pmd.Rule; - - -/** - * Multi-valued property. - * - * @param The type of the individual values. The multiple values are wrapped into a list. - * - * @author Clรฉment Fournier - * @version 6.0.0 - */ -@Deprecated -/* default */ abstract class AbstractMultiValueProperty extends AbstractProperty> - implements MultiValuePropertyDescriptor { - - - /** The default value. */ - private final List defaultValue; - private final char multiValueDelimiter; - - - /** - * Creates a multi valued property using the default delimiter {@link #DEFAULT_DELIMITER}. - * - * @param theName Name of the property (must not be empty) - * @param theDescription Description (must not be empty) - * @param theDefault Default value - * @param theUIOrder UI order (must be positive or zero) - * - * @throws IllegalArgumentException If name or description are empty, or UI order is negative. - */ - AbstractMultiValueProperty(String theName, String theDescription, List theDefault, float theUIOrder, - boolean isDefinedExternally) { - this(theName, theDescription, theDefault, theUIOrder, DEFAULT_DELIMITER, isDefinedExternally); - } - - - /** - * Creates a multi valued property using a custom delimiter. - * - * @param theName Name of the property (must not be empty) - * @param theDescription Description (must not be empty) - * @param theDefault Default value - * @param theUIOrder UI order (must be positive or zero) - * @param delimiter The delimiter to separate multiple values - * - * @throws IllegalArgumentException If name or description are empty, or UI order is negative. - */ - AbstractMultiValueProperty(String theName, String theDescription, List theDefault, - float theUIOrder, char delimiter, boolean isDefinedExternally) { - - super(theName, theDescription, theUIOrder, isDefinedExternally); - defaultValue = Collections.unmodifiableList(theDefault); - multiValueDelimiter = delimiter; - } - - - @Override - public final boolean isMultiValue() { - return true; - } - - - /* This is the one overridden in PropertyDescriptor */ - @Override - public String propertyErrorFor(Rule rule) { - List realValues = rule.getProperty(this); - return realValues == null ? null : errorFor(realValues); - } - - - @Override - public String errorFor(List values) { - - String err; - for (V value2 : values) { - err = valueErrorFor(value2); - if (err != null) { - return err; - } - } - - return null; - } - - - /** - * Checks a single value for a "missing value" error. - * - * @param value Value to check - * - * @return A descriptive String of the error or null if there was none - */ - protected String valueErrorFor(V value) { - return value != null || defaultHasNullValue() ? null : "missing value"; - } - - - private boolean defaultHasNullValue() { - return defaultValue == null || defaultValue.contains(null); - } - - - /** - * Returns a string representation of the default value. - * - * @return A string representation of the default value. - */ - @Override - protected String defaultAsString() { - return asDelimitedString(defaultValue(), multiValueDelimiter()); - } - - - private String asDelimitedString(List values, char delimiter) { - if (values == null) { - return ""; - } - - StringBuilder sb = new StringBuilder(); - for (V value : values) { - sb.append(asString(value)).append(delimiter); - } - if (sb.length() > 0) { - sb.deleteCharAt(sb.length() - 1); - } - - return sb.toString(); - } - - - @Override - public List defaultValue() { - return defaultValue; - } - - - @Override - public char multiValueDelimiter() { - return multiValueDelimiter; - } - - - /** - * Returns a string representation of the value, even if it's null. - * - * @param value The value to describe - * - * @return A string representation of the value - */ - protected String asString(V value) { - return value == null ? "" : value.toString(); - } - - - @Override - public final String asDelimitedString(List values) { - return asDelimitedString(values, multiValueDelimiter()); - } - - - @Override - public List valueFrom(String valueString) throws IllegalArgumentException { - if (StringUtils.isBlank(valueString)) { - return Collections.emptyList(); - } - - return ValueParserConstants.parseListWithEscapes(valueString, multiValueDelimiter(), this::createFrom); - } - - - /** - * Parse a string and returns an instance of a single value (not a list). - * - * @param toParse String to parse - * - * @return An instance of a value - */ - protected abstract V createFrom(String toParse); - - - @Override - protected void addAttributesTo(Map attributes) { - super.addAttributesTo(attributes); - attributes.put(PropertyDescriptorField.DELIMITER, Character.toString(multiValueDelimiter())); - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java index 9bcf47a54d..e1bf08d02d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java @@ -58,14 +58,6 @@ import org.apache.commons.lang3.StringUtils; } - @Override - public float uiOrder() { - return uiOrder; - } - - - - @Override public boolean equals(Object obj) { if (this == obj) { @@ -90,7 +82,6 @@ import org.apache.commons.lang3.StringUtils; @Override public String toString() { return "[PropertyDescriptor: name=" + name() + ',' - + " type=" + (isMultiValue() ? "List<" + type() + '>' : type()) + ',' + " value=" + defaultValue() + ']'; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java index 6af714011b..f360bf6a4e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.properties; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -151,14 +150,6 @@ public abstract class AbstractPropertySource implements PropertySource { } - @Override - @Deprecated - public void setProperty(MultiValuePropertyDescriptor propertyDescriptor, V... values) { - checkValidPropertyDescriptor(propertyDescriptor); - propertyValuesByDescriptor.put(propertyDescriptor, Collections.unmodifiableList(Arrays.asList(values))); - } - - /** * Checks whether this property descriptor is defined for this property source. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java index d3908f6c4d..3d18d07c0a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.properties; -import net.sourceforge.pmd.Rule; - /** * Single value property. @@ -71,31 +69,12 @@ import net.sourceforge.pmd.Rule; } - @Override - public String propertyErrorFor(Rule rule) { - T realValue = rule.getProperty(this); - return realValue == null ? null : errorFor(realValue); - } - - @Override public String errorFor(T value) { - String typeError = typeErrorFor(value); - if (typeError != null) { - return typeError; - } return valueErrorFor(value); } - private String typeErrorFor(T value) { - if (value != null && !type().isAssignableFrom(value.getClass())) { - return value + " is not an instance of " + type(); - } - return null; - } - - /** * Checks the value for an error. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java index eb216ff73a..c97ddc06c6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java @@ -58,12 +58,6 @@ public final class BooleanProperty extends AbstractSingleValueProperty } - @Override - public Class type() { - return Boolean.class; - } - - @Override public Boolean createFrom(String propertyString) throws IllegalArgumentException { return BOOLEAN_PARSER.valueOf(propertyString); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java index e22e5f874b..8bad03353a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java @@ -58,12 +58,6 @@ public final class CharacterProperty extends AbstractSingleValueProperty type() { - return Character.class; - } - - @Override public Character createFrom(String valueString) throws IllegalArgumentException { return charFrom(valueString); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java index 6faae6e74a..3e3da86c41 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.properties.ValueParserConstants.DOUBLE_PARSER; -import net.sourceforge.pmd.properties.builders.SingleNumericPropertyBuilder; - /** * Defines a property type that support single double-type property values within an upper and lower boundary. @@ -70,12 +68,6 @@ public final class DoubleProperty extends AbstractNumericProperty { } - @Override - public Class type() { - return Double.class; - } - - @Override protected Double createFrom(String value) { return doubleFrom(value); @@ -94,29 +86,4 @@ public final class DoubleProperty extends AbstractNumericProperty { } - /** - * @deprecated Use {@link PropertyFactory#doubleProperty(String)}. - */ - @Deprecated - public static DoublePBuilder named(String name) { - return new DoublePBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#doubleProperty(String)}. - */ - @Deprecated - public static final class DoublePBuilder extends SingleNumericPropertyBuilder { - private DoublePBuilder(String name) { - super(name); - } - - - @Override - public DoubleProperty build() { - return new DoubleProperty(name, description, lowerLimit, upperLimit, defaultValue, uiOrder, isDefinedInXML); - } - } - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java index 0594528d6a..f6380adb6a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java @@ -41,12 +41,6 @@ public final class FileProperty extends AbstractSingleValueProperty { } - @Override - public Class type() { - return File.class; - } - - @Override public File createFrom(String propertyString) { return StringUtils.isBlank(propertyString) ? null : new File(propertyString); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java deleted file mode 100644 index 74c433fa90..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericMultiValuePropertyDescriptor.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.StringParser; - - -/** - * If we implement schema changes to properties, delimiter logic will probably be scrapped, - * and hence the divide between multi-value and single-value property descriptors. We can then - * use a single class for all property descriptors. - * - * @author Clรฉment Fournier - * @since 6.10.0 - */ -final class GenericMultiValuePropertyDescriptor> extends AbstractMultiValueProperty { - - - private final Set> listValidators; - private final StringParser parser; - private final Class type; - - - GenericMultiValuePropertyDescriptor(String name, String description, float uiOrder, - Collection defaultValue, - Set> listValidators, - StringParser parser, - char delim, - Class type) { - // this cast is safe until 7.0.0 - super(name, description, (List) defaultValue, uiOrder, delim, false); - this.listValidators = listValidators; - this.parser = parser; - this.type = type; - - String dftValueError = errorFor(new ArrayList<>(defaultValue)); - if (dftValueError != null) { - throw new IllegalArgumentException(dftValueError); - } - } - - - @SuppressWarnings("unchecked") - @Override - public String errorFor(List values) { - for (PropertyConstraint lv : listValidators) { - // Note: the unchecked cast is safe because pre-7.0.0, - // we only allow building property descriptors for lists. - // C is thus always List, and the cast doesn't fail - - // Post-7.0.0, the multi-value property classes will be removed - // and C will be the actual type parameter of the returned property - // descriptor - - String error = lv.validate((C) values); - if (error != null) { - return error; - } - } - return null; - } - - - @Override - protected V createFrom(String toParse) { - return parser.valueOf(toParse); - } - - - @Override - public Class type() { - return type; - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 0eff12903b..7504c87801 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -9,7 +9,7 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.StringParser; +import net.sourceforge.pmd.properties.internal.XmlSyntax; /** @@ -18,29 +18,30 @@ import net.sourceforge.pmd.properties.internal.StringParser; * @author Clรฉment Fournier * @since 6.10.0 */ -final class GenericPropertyDescriptor extends AbstractSingleValueProperty { +final class GenericPropertyDescriptor implements PropertyDescriptor { - private final StringParser parser; + private final XmlSyntax parser; private final PropertyTypeId typeId; - private final Class type; + private final String name; + private final String description; + private final T defaultValue; private final Set> constraints; GenericPropertyDescriptor(String name, String description, - float uiOrder, T defaultValue, Set> constraints, - StringParser parser, - @Nullable PropertyTypeId typeId, - Class type) { + XmlSyntax parser, + @Nullable PropertyTypeId typeId) { - super(name, description, defaultValue, uiOrder, typeId != null); + this.name = name; + this.description = description; + this.defaultValue = defaultValue; this.constraints = constraints; this.parser = parser; this.typeId = typeId; - this.type = type; String dftValueError = errorFor(defaultValue); if (dftValueError != null) { @@ -61,15 +62,34 @@ final class GenericPropertyDescriptor extends AbstractSingleValueProperty return null; } - @Override - public Class type() { - return type; + public String name() { + return name; } + @Override + public String description() { + return description; + } @Override - protected T createFrom(String toParse) { - return parser.valueOf(toParse); + public T defaultValue() { + return defaultValue; + } + + @Override + public boolean isMultiValue() { + return false; + } + + @Override + public XmlSyntax xmlStrategy() { + return parser; + } + + @Nullable + @Override + public PropertyTypeId getTypeId() { + return typeId; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java index bce52b0cbe..552e43fe19 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.properties.ValueParserConstants.INTEGER_PARSER; -import net.sourceforge.pmd.properties.builders.SingleNumericPropertyBuilder; - /** * Defines a datatype that supports single Integer property values within an upper and lower boundary. @@ -49,41 +47,10 @@ public final class IntegerProperty extends AbstractNumericProperty { } - @Override - public Class type() { - return Integer.class; - } - - @Override protected Integer createFrom(String value) { return INTEGER_PARSER.valueOf(value); } - /** - * @deprecated Use {@link PropertyFactory#intProperty(String)} - */ - @Deprecated - public static IntegerPBuilder named(String name) { - return new IntegerPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#intProperty(String)} - */ - @Deprecated - public static final class IntegerPBuilder extends SingleNumericPropertyBuilder { - private IntegerPBuilder(String name) { - super(name); - } - - - @Override - public IntegerProperty build() { - return new IntegerProperty(name, description, lowerLimit, upperLimit, defaultValue, uiOrder, isDefinedInXML); - } - } - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java index 49d710b40c..4122ed8928 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java @@ -8,13 +8,7 @@ import java.util.List; /** - * Specializes property descriptors for multi valued descriptors. For this type of property, the return value of the - * {@link #type()} method must be the class literal of the type parameter of this interface, which is the type of the - * components of the list (not the type of the list). Notice that for implementors, the type parameter of this interface - * is not the same as the type parameter of {@link PropertyDescriptor} they inherit! - * - * @param The type of value this descriptor works with. This is the type of the list's component. - * + * Specializes property descriptors for multi valued descriptors. * @author Clรฉment Fournier * @since 6.0.0 * @@ -42,7 +36,4 @@ public interface MultiValuePropertyDescriptor extends PropertyDescriptor type(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index bf70262923..03ea35355b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -18,7 +18,8 @@ import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.StringParser; +import net.sourceforge.pmd.properties.internal.ValueSyntax; +import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; // @formatter:off /** @@ -203,19 +204,20 @@ public abstract class PropertyBuilder, T> { // then the syntax should be the only one available. // This would allow specifying eg lists of numbers as 1,2,3, for which the syntax would look clumsy abstract static class BaseSinglePropertyBuilder, T> extends PropertyBuilder { - private final StringParser parser; + + private final ValueSyntax parser; private final Class type; // Class is not final but a package-private constructor restricts inheritance - BaseSinglePropertyBuilder(String name, StringParser parser, Class type) { + BaseSinglePropertyBuilder(String name, ValueSyntax parser, Class type) { super(name); this.parser = parser; this.type = type; } - protected StringParser getParser() { + protected ValueSyntax getParser() { return parser; } @@ -242,7 +244,7 @@ public abstract class PropertyBuilder, T> { } GenericCollectionPropertyBuilder> result = - new GenericCollectionPropertyBuilder<>(getName(), getParser(), ArrayList::new, getType()); + new GenericCollectionPropertyBuilder<>(getName(), getParser(), ArrayList::new); for (PropertyConstraint validator : getConstraints()) { result.require(validator.toCollectionConstraint()); @@ -258,12 +260,10 @@ public abstract class PropertyBuilder, T> { return new GenericPropertyDescriptor<>( getName(), getDescription(), - 0f, getDefaultValue(), getConstraints(), parser, - typeId, - type + typeId ); } } @@ -279,7 +279,7 @@ public abstract class PropertyBuilder, T> { // Note: This type is used to fix the first type parameter for classes that don't need more API. public static final class GenericPropertyBuilder extends BaseSinglePropertyBuilder, T> { - GenericPropertyBuilder(String name, StringParser parser, Class type) { + GenericPropertyBuilder(String name, ValueSyntax parser, Class type) { super(name, parser, type); } } @@ -295,7 +295,7 @@ public abstract class PropertyBuilder, T> { public static final class RegexPropertyBuilder extends BaseSinglePropertyBuilder { RegexPropertyBuilder(String name) { - super(name, ValueParserConstants.REGEX_PARSER, Pattern.class); + super(name, XmlSyntaxUtils.REGEX, Pattern.class); } @@ -351,23 +351,21 @@ public abstract class PropertyBuilder, T> { * @since 6.10.0 */ public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { - private final StringParser parser; + + private final ValueSyntax parser; private final Supplier emptyCollSupplier; - private final Class type; - private char multiValueDelimiter = MultiValuePropertyDescriptor.DEFAULT_DELIMITER; + private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; /** * Builds a new builder for a collection type. Package-private. */ GenericCollectionPropertyBuilder(String name, - StringParser parser, - Supplier emptyCollSupplier, - Class type) { + ValueSyntax parser, + Supplier emptyCollSupplier) { super(name); this.parser = parser; this.emptyCollSupplier = emptyCollSupplier; - this.type = type; } @@ -464,15 +462,24 @@ public abstract class PropertyBuilder, T> { // and C will be the actual type parameter of the returned property // descriptor - return (PropertyDescriptor) new GenericMultiValuePropertyDescriptor<>( - getName(), - getDescription(), - 0f, - getDefaultValue(), - getConstraints(), - parser, - multiValueDelimiter, - type + /* + (String name, + String description, + float uiOrder, + T defaultValue, + Set> constraints, + StringParser parser, + @Nullable PropertyTypeId typeId, + Class type + */ + + return new GenericPropertyDescriptor<>( + getName(), + getDescription(), + getDefaultValue(), + getConstraints(), + XmlSyntaxUtils.withSeq(parser, emptyCollSupplier, false, Character.toString(multiValueDelimiter)), + typeId ); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 458f0511a6..65635cae57 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -71,20 +71,9 @@ public interface PropertyDescriptor { * @deprecated PMD 7.0.0 will change the return type to {@code Optional} */ @Deprecated - String errorFor(T value); // TODO Java 1.8 make optional - - - /** - * Denotes the value datatype. For multi value properties, this is not the List class but the list's component - * class. - * - * @return Class literal of the value type - * - * @deprecated This method is mainly used for documentation, but will not prove general enough - * to support PMD 7.0.0's improved property types. - */ - @Deprecated - Class type(); + default String errorFor(T value) { + return null; + } /** @@ -110,21 +99,6 @@ public interface PropertyDescriptor { boolean isMultiValue(); - /** - * Denotes the relative order the property field should occupy if we are using an auto-generated UI to display and - * edit property values. If the value returned has a non-zero fractional part then this is can be used to place - * adjacent fields on the same row. - * - * @return The relative order compared to other properties of the same rule - * - * @deprecated This method confuses the presentation layer and the business logic. The order of the - * property in a UI is irrelevant to the functioning of the property in PMD. With PMD 7.0.0, this - * method will be removed. UI and documentation tools will decide on their own convention. - */ - @Deprecated - float uiOrder(); - - /** * Returns the value represented by this string. * @@ -133,11 +107,14 @@ public interface PropertyDescriptor { * @return The value represented by the string * * @throws IllegalArgumentException if the given string cannot be parsed + * @throws UnsupportedOperationException If operation is not supported * @deprecated PMD 7.0.0 will use a more powerful scheme to represent values than * simple strings, this method won't be general enough */ @Deprecated - T valueFrom(String propertyString) throws IllegalArgumentException; + default T valueFrom(String propertyString) throws IllegalArgumentException { + return xmlStrategy().fromString(propertyString); + } /** @@ -151,22 +128,9 @@ public interface PropertyDescriptor { * simple strings, this method won't be general enough */ @Deprecated - String asDelimitedString(T value); - - - /** - * A convenience method that returns an error string if the rule holds onto a property value that has a problem. - * Returns null otherwise. - * - * @param rule Rule - * - * @return String - * - * @deprecated Used nowhere, and fails if the rule doesn't define the property descriptor - * A better solution will be added on property source - */ - @Deprecated - String propertyErrorFor(Rule rule); + default String asDelimitedString(T value) { + return xmlStrategy().toString(value); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 0979406b3e..3f17761911 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -86,6 +86,14 @@ import net.sourceforge.pmd.properties.constraints.PropertyConstraint; //@formatter:on public final class PropertyFactory { + + /** Default delimiter for multi-valued properties other than numeric ones. */ + static final char DEFAULT_DELIMITER = '|'; + + /** Default delimiter for numeric multi-valued properties. */ + static final char DEFAULT_NUMERIC_DELIMITER = ','; + + private PropertyFactory() { } @@ -122,7 +130,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> intListProperty(String name) { - return intProperty(name).toList().delim(MultiValuePropertyDescriptor.DEFAULT_NUMERIC_DELIMITER); + return intProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); } @@ -159,7 +167,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> longIntListProperty(String name) { - return longIntProperty(name).toList().delim(MultiValuePropertyDescriptor.DEFAULT_NUMERIC_DELIMITER); + return longIntProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); } @@ -191,7 +199,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> doubleListProperty(String name) { - return doubleProperty(name).toList().delim(MultiValuePropertyDescriptor.DEFAULT_NUMERIC_DELIMITER); + return doubleProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); } @@ -346,7 +354,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> enumListProperty(String name, Map nameToValue) { - return enumProperty(name, nameToValue).toList().delim(MultiValuePropertyDescriptor.DEFAULT_DELIMITER); + return enumProperty(name, nameToValue).toList(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java index 66c9251299..66915a62db 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java @@ -104,20 +104,6 @@ public interface PropertySource { void setProperty(PropertyDescriptor propertyDescriptor, T value); - /** - * Sets the value of a multi value property descriptor with a variable number of arguments. - * This is also referred to as "overriding" the (default) value of a property. - * - * @param propertyDescriptor The property descriptor for which to add a value - * @param values Values - * @param The type of the values - * - * @deprecated {@link MultiValuePropertyDescriptor} is deprecated - */ - @Deprecated - void setProperty(MultiValuePropertyDescriptor propertyDescriptor, V... values); - - /** * Returns an unmodifiable map of descriptors to property values * for the current receiver. The returned map has an entry for diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java index 93c7e9858e..5155f3c09d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java @@ -33,12 +33,6 @@ public final class RegexProperty extends AbstractSingleValueProperty { } - @Override - public Class type() { - return Pattern.class; - } - - /** * Creates a new builder for a regex property. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java index 4ca0ac6337..6e00664c3b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.properties; /** * Specializes property descriptors for single valued descriptors. For this type of property, the return value of the - * {@link #type()} method must be the class literal of the type parameter of the interface {@link PropertyDescriptor}. * * @param The type of value this descriptor works with. Cannot be a list. * @@ -18,7 +17,4 @@ package net.sourceforge.pmd.properties; @Deprecated public interface SingleValuePropertyDescriptor extends PropertyDescriptor { - @Override - @Deprecated - Class type(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java deleted file mode 100644 index 5940c2e64d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringMultiProperty.java +++ /dev/null @@ -1,170 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - -import net.sourceforge.pmd.properties.builders.MultiValuePropertyBuilder; - - -/** - * Defines a datatype that supports multiple String values. Note that all strings must be filtered by the delimiter - * character. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor>}. A builder is available from {@link PropertyFactory#stringListProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class StringMultiProperty extends AbstractMultiValueProperty { - - - /** - * Constructor using an array of defaults. - * - * @param theName Name - * @param theDescription Description - * @param defaultValues Array of defaults - * @param theUIOrder UI order - * @param delimiter The delimiter to use - * - * @throws IllegalArgumentException if a default value contains the delimiter - * @throws NullPointerException if the defaults array is null - * @deprecated Use {@link PropertyFactory#stringListProperty(String)} - */ - @Deprecated - public StringMultiProperty(String theName, String theDescription, String[] defaultValues, float theUIOrder, - char delimiter) { - this(theName, theDescription, Arrays.asList(defaultValues), theUIOrder, delimiter); - } - - - /** - * Constructor using a list of defaults. - * - * @param theName Name - * @param theDescription Description - * @param defaultValues List of defaults - * @param theUIOrder UI order - * @param delimiter The delimiter to useg - * - * @throws IllegalArgumentException if a default value contains the delimiter - * @throws NullPointerException if the defaults array is null - * @deprecated Use {@link PropertyFactory#stringListProperty(String)} - */ - @Deprecated - public StringMultiProperty(String theName, String theDescription, List defaultValues, float theUIOrder, - char delimiter) { - this(theName, theDescription, defaultValues, theUIOrder, delimiter, false); - } - - - /** Master constructor. */ - private StringMultiProperty(String theName, String theDescription, List defaultValues, float theUIOrder, - char delimiter, boolean isDefinedExternally) { - super(theName, theDescription, defaultValues, theUIOrder, delimiter, isDefinedExternally); - - checkDefaults(defaultValues, delimiter); - } - - - @Override - public Class type() { - return String.class; - } - - - @Override - public List valueFrom(String valueString) { - return Arrays.asList(StringUtils.split(valueString, multiValueDelimiter())); - } - - - @Override - protected String valueErrorFor(String value) { - - if (value == null) { - return "Missing value"; - } - - if (containsDelimiter(value)) { - return "Value cannot contain the '" + multiValueDelimiter() + "' character"; - } - - // TODO - eval against regex checkers - - return null; - } - - - /** - * Returns true if the multi value delimiter is present in the string. - * - * @param value String - * - * @return boolean - */ - private boolean containsDelimiter(String value) { - return value.indexOf(multiValueDelimiter()) >= 0; - } - - - @Override - protected String createFrom(String toParse) { - return toParse; - } - - - /** - * Checks if the values are valid. - * - * @param defaultValue The default value - * @param delim The delimiter - * - * @throws IllegalArgumentException if one value contains the delimiter - */ - private static void checkDefaults(List defaultValue, char delim) { - - if (defaultValue == null) { - return; - } - - for (String aDefaultValue : defaultValue) { - if (aDefaultValue.indexOf(delim) >= 0) { - throw new IllegalArgumentException("Cannot include the delimiter in the set of defaults"); - } - } - } - - - /** - * @deprecated Use {@link PropertyFactory#stringListProperty(String)} - */ - @Deprecated - public static StringMultiPBuilder named(String name) { - return new StringMultiPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#stringListProperty(String)} - */ - @Deprecated - public static final class StringMultiPBuilder extends MultiValuePropertyBuilder { - private StringMultiPBuilder(String name) { - super(name); - } - - - @Override - public StringMultiProperty build() { - return new StringMultiProperty(this.name, this.description, this.defaultValues, this.uiOrder, this.multiValueDelimiter, isDefinedInXML); - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java index 563f2e5e8e..2697c6544b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java @@ -41,12 +41,6 @@ public final class StringProperty extends AbstractSingleValueProperty { } - @Override - public Class type() { - return String.class; - } - - @Override public String createFrom(String valueString) { return valueString; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiNumericPropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiNumericPropertyBuilder.java deleted file mode 100644 index f63f41ecc7..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiNumericPropertyBuilder.java +++ /dev/null @@ -1,47 +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.MultiValuePropertyDescriptor; - - -/** - * For multi-value numeric properties. - * - * @param Element type of the list - * @param Concrete type of the underlying builder - * @deprecated see {@link net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder} - */ -@Deprecated -public abstract class MultiNumericPropertyBuilder> - extends MultiValuePropertyBuilder { - - - protected V lowerLimit; - protected V upperLimit; - - - protected MultiNumericPropertyBuilder(String name) { - super(name); - multiValueDelimiter = MultiValuePropertyDescriptor.DEFAULT_NUMERIC_DELIMITER; - } - - - /** - * Specify the range of acceptable values. - * - * @param min Lower bound, inclusive - * @param max Upper bound, inclusive - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T range(V min, V max) { - this.lowerLimit = min; - this.upperLimit = max; - return (T) this; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiValuePropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiValuePropertyBuilder.java deleted file mode 100644 index d433d0ea9d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/MultiValuePropertyBuilder.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.builders; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; - - -/** - * For multi-value properties. - * - * @param Element type of the list - * @param Concrete type of the underlying builder - * @deprecated see {@link net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder} - */ -@Deprecated -public abstract class MultiValuePropertyBuilder> - extends PropertyDescriptorBuilder, T> { - - protected List defaultValues; - protected char multiValueDelimiter = MultiValuePropertyDescriptor.DEFAULT_DELIMITER; - - - protected MultiValuePropertyBuilder(String name) { - super(name); - } - - - /** - * Specify a default value. - * - * @param val List of values - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T defaultValues(Collection val) { - this.defaultValues = new ArrayList<>(val); - return (T) this; - } - - - /** - * Specify default values. - * - * @param val List of values - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T defaultValues(V... val) { - this.defaultValues = Arrays.asList(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; - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java index 4f3d4ac117..fbf06dd78c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java @@ -63,7 +63,6 @@ public abstract class PropertyDescriptorBuilder> - extends SingleValuePropertyBuilder { - - - protected V lowerLimit; - protected V upperLimit; - - - public SingleNumericPropertyBuilder(String name) { - super(name); - } - - - /** - * Specify the range of acceptable values. - * - * @param min Lower bound - * @param max Upper bound - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T range(V min, V max) { - this.lowerLimit = min; - this.upperLimit = max; - return (T) this; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index cc5325b5bb..619a821c6f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -65,7 +65,7 @@ public final class SyntaxSet extends XmlSyntax { public @Nullable T fromString(String attributeData) { for (XmlSyntax syntax : supportedReadStrategies()) { - if (syntax.supportsFromString()) { + if (syntax.supportsStringMapping()) { return syntax.fromString(attributeData); } } @@ -78,8 +78,8 @@ public final class SyntaxSet extends XmlSyntax { } @Override - public boolean supportsFromString() { - return supportedReadStrategies().stream().anyMatch(XmlSyntax::supportsFromString); + public boolean supportsStringMapping() { + return supportedReadStrategies().stream().anyMatch(XmlSyntax::supportsStringMapping); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index 99b0418393..00b6dd4112 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -43,7 +43,7 @@ public final class ValueSyntax extends XmlSyntax { } @Override - public boolean supportsFromString() { + public boolean supportsStringMapping() { return true; } @@ -52,6 +52,7 @@ public final class ValueSyntax extends XmlSyntax { return fromString.apply(attributeData); } + @Override public String toString(T data) { return toString.apply(data); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java index f1514bba17..ce8e512d4d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java @@ -37,19 +37,29 @@ public abstract class XmlSyntax { /** Write the value into the given XML element. */ public abstract void toXml(Element container, T value); - public boolean supportsFromString() { + public boolean supportsStringMapping() { return false; } /** * Read the value from a string. - * @throws UnsupportedOperationException if unsupported, see {@link #supportsFromString()} + * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()} * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) */ public T fromString(String attributeData) { throw new UnsupportedOperationException(); } + /** + * Format the value to a string. + * + * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()} + * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) + */ + public String toString(T value) { + throw new UnsupportedOperationException(); + } + /** Get the preferred name used to write elements. */ public final String getWriteElementName() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java index 8cb66bac12..f9e1ec0e12 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java @@ -57,15 +57,15 @@ public final class XmlSyntaxUtils { private static XmlSyntax> otherList(ValueSyntax valueSyntax) { return withSeq(valueSyntax, ArrayList::new, - true, + true, // for now "|" ); } - static > XmlSyntax withSeq(ValueSyntax itemSyntax, - Supplier emptyCollSupplier, - boolean preferOldSyntax, - String delimiter) { + public static > XmlSyntax withSeq(ValueSyntax itemSyntax, + Supplier emptyCollSupplier, + boolean preferOldSyntax, + String delimiter) { return new SyntaxSet<>( new SeqSyntax<>(itemSyntax, emptyCollSupplier), delimitedString(itemSyntax::toString, itemSyntax::fromString, delimiter, emptyCollSupplier), diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 5bbaec83ec..9a6f821c97 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -15,7 +15,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Element; @@ -356,7 +355,7 @@ public class RuleFactory { String defaultAttr = DEFAULT_VALUE.getOptional(propertyElement); if (!StringUtils.isBlank(defaultAttr)) { - if (syntax.supportsFromString()) { + if (syntax.supportsStringMapping()) { defaultValue = syntax.fromString(defaultAttr); } else { throw err.error(propertyElement.getAttributeNode(DEFAULT_VALUE.attributeName()), diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index f62400b1a9..fc2ce7b910 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -525,17 +525,14 @@ public class RuleDocGenerator { && propertyDescriptor.description().toLowerCase(Locale.ROOT).startsWith(DEPRECATED_RULE_PROPERTY_MARKER); } - private String determineDefaultValueAsString(PropertyDescriptor propertyDescriptor, Rule rule, boolean pad) { + private String determineDefaultValueAsString(PropertyDescriptor propertyDescriptor, Rule rule, boolean pad) { String defaultValue = ""; - Object realDefaultValue = rule.getProperty(propertyDescriptor); - @SuppressWarnings("unchecked") // just force it, we know it's the right type - PropertyDescriptor captured = (PropertyDescriptor) propertyDescriptor; + T realDefaultValue = rule.getProperty(propertyDescriptor); if (realDefaultValue != null) { - defaultValue = captured.asDelimitedString(realDefaultValue); + defaultValue = propertyDescriptor.asDelimitedString(realDefaultValue); if (pad && propertyDescriptor.isMultiValue()) { - @SuppressWarnings("unchecked") // multi valued properties are using a List MultiValuePropertyDescriptor> multiPropertyDescriptor = (MultiValuePropertyDescriptor>) propertyDescriptor; // surround the delimiter with spaces, so that the browser can wrap diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java index bfa7f0a765..a2266b2705 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -486,7 +486,7 @@ public abstract class AbstractRuleSetFactoryTest { Object value1 = rule1.getProperty(propertyDescriptors1.get(j)); Object value2 = rule2.getProperty(propertyDescriptors2.get(j)); // special case for Pattern, there is no equals method - if (propertyDescriptors1.get(j).type() == Pattern.class) { + if (value1 instanceof Pattern && value2 instanceof Pattern) { value1 = ((Pattern) value1).pattern(); value2 = ((Pattern) value2).pattern(); } From b70f79bc95bda7fdd47370be79009d88f969178a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 01:07:12 +0200 Subject: [PATCH 020/347] Remove numeric properties --- .../properties/AbstractNumericProperty.java | 79 ---------------- .../pmd/properties/DoubleProperty.java | 89 ------------------ .../pmd/properties/IntegerProperty.java | 56 ----------- .../modules/NumericPropertyModule.java | 93 ------------------- 4 files changed, 317 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractNumericProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/NumericPropertyModule.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractNumericProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractNumericProperty.java deleted file mode 100644 index 866d88fcca..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractNumericProperty.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Map; - -import net.sourceforge.pmd.properties.modules.NumericPropertyModule; - - -/** - * Maintains a pair of boundary limit values between which all values managed by the subclasses must fit. - * - * @param The type of value. - * - * @author Brian Remedios - * @author Clรฉment Fournier - * @version Refactored June 2017 (6.0.0) - */ -@Deprecated -/* default */ abstract class AbstractNumericProperty extends AbstractSingleValueProperty - implements NumericPropertyDescriptor { - - - private final NumericPropertyModule module; - - - /** - * Constructor for a single-valued numeric property. - * - * @param theName Name - * @param theDescription Description - * @param lower Minimum value of the property - * @param upper Maximum value of the property - * @param theDefault List of defaults - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if lower > upper, or one of them is null, or the default is not between the - * bounds - */ - protected AbstractNumericProperty(String theName, String theDescription, T lower, T upper, T theDefault, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, theDefault, theUIOrder, isDefinedExternally); - - module = new NumericPropertyModule<>(lower, upper); - if (theDefault == null) { - return; // TODO: remove me when you scrap StatisticalRule (see pull #727) - } - module.checkNumber(theDefault); - } - - - @Override - protected String valueErrorFor(T value) { - return module.valueErrorFor(value); - } - - - @Override - public Number lowerLimit() { - return module.getLowerLimit(); - } - - - @Override - public Number upperLimit() { - return module.getUpperLimit(); - } - - - @Override - protected void addAttributesTo(Map attributes) { - super.addAttributesTo(attributes); - module.addAttributesTo(attributes); - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java deleted file mode 100644 index 3e3da86c41..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/DoubleProperty.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * 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.DOUBLE_PARSER; - - -/** - * Defines a property type that support single double-type property values within an upper and lower boundary. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * - * @deprecated Use a {@code PropertyDescriptor} instead. A builder is available from {@link PropertyFactory#doubleProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class DoubleProperty extends AbstractNumericProperty { - - - /** - * Constructor for DoubleProperty that limits itself to a single value within the specified limits. Converts string - * arguments into the Double values. - * - * @param theName Name - * @param theDescription Description - * @param minStr Minimum value of the property - * @param maxStr Maximum value of the property - * @param defaultStr Default value - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#doubleProperty(String)}. - */ - @Deprecated - public DoubleProperty(String theName, String theDescription, String minStr, String maxStr, String defaultStr, - float theUIOrder) { - this(theName, theDescription, doubleFrom(minStr), doubleFrom(maxStr), doubleFrom(defaultStr), theUIOrder, false); - } - - - /** Master constructor. */ - private DoubleProperty(String theName, String theDescription, Double min, Double max, Double theDefault, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, min, max, theDefault, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor that limits itself to a single value within the specified limits. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param theDefault Default value - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * @deprecated Use {@link PropertyFactory#doubleProperty(String)}. - */ - @Deprecated - public DoubleProperty(String theName, String theDescription, Double min, Double max, Double theDefault, - float theUIOrder) { - this(theName, theDescription, min, max, theDefault, theUIOrder, false); - } - - - @Override - protected Double createFrom(String value) { - return doubleFrom(value); - } - - - /** - * Parses a String into a Double. - * - * @param numberString String to parse - * - * @return Parsed Double - */ - private static Double doubleFrom(String numberString) { - return DOUBLE_PARSER.valueOf(numberString); - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java deleted file mode 100644 index 552e43fe19..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/IntegerProperty.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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.INTEGER_PARSER; - - -/** - * Defines a datatype that supports single Integer property values within an upper and lower boundary. - * - * @author Brian Remedios - * - * @deprecated Use a {@code PropertyDescriptor} instead. A builder is available from {@link PropertyFactory#intProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class IntegerProperty extends AbstractNumericProperty { - - - /** - * Constructor that limits itself to a single value within the specified limits. - * - * @param theName Name - * @param theDescription Description - * @param min Minimum value of the property - * @param max Maximum value of the property - * @param theDefault Default value - * @param theUIOrder UI order - * - * @throws IllegalArgumentException if {@literal min > max} or one of the defaults is not between the bounds - * - * @deprecated Use {@link PropertyFactory#intProperty(String)} - */ - @Deprecated - public IntegerProperty(String theName, String theDescription, Integer min, Integer max, Integer theDefault, - float theUIOrder) { - this(theName, theDescription, min, max, theDefault, theUIOrder, false); - } - - - /** Master constructor. */ - private IntegerProperty(String theName, String theDescription, Integer min, Integer max, Integer theDefault, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, min, max, theDefault, theUIOrder, isDefinedExternally); - } - - - @Override - protected Integer createFrom(String value) { - return INTEGER_PARSER.valueOf(value); - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/NumericPropertyModule.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/NumericPropertyModule.java deleted file mode 100644 index 0b8a26c988..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/modules/NumericPropertyModule.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.modules; - -import static net.sourceforge.pmd.properties.PropertyDescriptorField.MAX; -import static net.sourceforge.pmd.properties.PropertyDescriptorField.MIN; - -import java.util.Map; - -import net.sourceforge.pmd.properties.PropertyDescriptorField; - - -/** - * Common utilities for implementations of numeric property descriptors. - * - * @author Clรฉment Fournier - */ -@Deprecated -public class NumericPropertyModule { - - private final T lowerLimit; - private final T upperLimit; - - - public NumericPropertyModule(T lowerLimit, T upperLimit) { - this.lowerLimit = lowerLimit; - this.upperLimit = upperLimit; - - checkNumber(lowerLimit); - checkNumber(upperLimit); - - if (lowerLimit.doubleValue() > upperLimit.doubleValue()) { - throw new IllegalArgumentException("Lower limit cannot be greater than the upper limit"); - } - } - - - public void checkNumber(T number) { - String error = valueErrorFor(number); - if (error != null) { - throw new IllegalArgumentException(error); - } - } - - - public String valueErrorFor(T value) { - - if (value == null) { - return "Missing value"; - } - - double number = value.doubleValue(); - - if (number > upperLimit.doubleValue() || number < lowerLimit.doubleValue()) { - return value + " is out of range " + rangeString(lowerLimit, upperLimit); - } - - return null; - } - - - public T getLowerLimit() { - return lowerLimit; - } - - - public T getUpperLimit() { - return upperLimit; - } - - - public void addAttributesTo(Map attributes) { - attributes.put(MIN, lowerLimit.toString()); - attributes.put(MAX, upperLimit.toString()); - } - - - /** - * Returns a string representing the range defined by the two bounds. - * - * @param low Lower bound - * @param up Upper bound - * - * @return String - */ - private static String rangeString(Number low, Number up) { - return "(" + low + " -> " + up + ")"; - } - - -} From a9efe39944018be655cc5b958a100298443c5952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 01:14:35 +0200 Subject: [PATCH 021/347] Remove multi value properties --- .../MultiValuePropertyDescriptor.java | 39 ------------------- .../properties/NumericPropertyDescriptor.java | 34 ---------------- .../pmd/properties/PropertyBuilder.java | 4 +- .../pmd/properties/internal/SyntaxSet.java | 16 +++++++- .../pmd/docs/RuleDocGenerator.java | 34 ++++++++-------- 5 files changed, 32 insertions(+), 95 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericPropertyDescriptor.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java deleted file mode 100644 index 4122ed8928..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.List; - - -/** - * Specializes property descriptors for multi valued descriptors. - * @author Clรฉment Fournier - * @since 6.0.0 - * - * @deprecated The hard divide between multi- and single-value properties will be removed with 7.0.0 - */ -@Deprecated -public interface MultiValuePropertyDescriptor extends PropertyDescriptor> { - - /** Default delimiter for multi-valued properties other than numeric ones. */ - @Deprecated - char DEFAULT_DELIMITER = '|'; - - /** Default delimiter for numeric multi-valued properties. */ - @Deprecated - char DEFAULT_NUMERIC_DELIMITER = ','; - - - /** - * Return the character being used to delimit multiple property values within a single string. You must ensure that - * this character does not appear within any rule property values to avoid deserialization errors. - * - * @return char - */ - @Deprecated - char multiValueDelimiter(); - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericPropertyDescriptor.java deleted file mode 100644 index d8b75e4089..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericPropertyDescriptor.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -/** - * Defines a descriptor type whose instance values are required to lie within specified upper and lower limits. - * - * @param type of the property value - * - * @deprecated Will be removed with 7.0.0. In the future this interface won't exist, - * but numeric properties will still be around - * @author Brian Remedios - * @author Clรฉment Fournier - */ -@Deprecated -public interface NumericPropertyDescriptor extends PropertyDescriptor { - - /** - * Returns the maximum value that instances of the property can have. - * - * @return Number - */ - Number upperLimit(); - - - /** - * Returns the minimum value that instances of the property can have. - * - * @return Number - */ - Number lowerLimit(); -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 03ea35355b..12d2785cbd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -433,8 +433,8 @@ public abstract class PropertyBuilder, T> { /** - * Specify a delimiter character. By default it's {@link MultiValuePropertyDescriptor#DEFAULT_DELIMITER}, or {@link - * MultiValuePropertyDescriptor#DEFAULT_NUMERIC_DELIMITER} for numeric properties. + * Specify a delimiter character. By default it's {@value PropertyFactory#DEFAULT_DELIMITER}, + * or {@value PropertyFactory#DEFAULT_NUMERIC_DELIMITER} for numeric properties. * * @param delim Delimiter * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index 619a821c6f..d353bcb032 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -62,11 +62,23 @@ public final class SyntaxSet extends XmlSyntax { } @Override - public @Nullable T fromString(String attributeData) { + public @Nullable T fromString(String string) { for (XmlSyntax syntax : supportedReadStrategies()) { if (syntax.supportsStringMapping()) { - return syntax.fromString(attributeData); + return syntax.fromString(string); + } + } + + throw new UnsupportedOperationException(); + } + + @Override + public String toString(T value) { + + for (XmlSyntax syntax : supportedReadStrategies()) { + if (syntax.supportsStringMapping()) { + return syntax.toString(value); } } diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index fc2ce7b910..868428f5d2 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -28,7 +28,6 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; @@ -42,7 +41,6 @@ import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; -import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; public class RuleDocGenerator { @@ -460,12 +458,13 @@ public class RuleDocGenerator { String defaultValue = determineDefaultValueAsString(propertyDescriptor, rule, true); String multiValued = "no"; - if (propertyDescriptor.isMultiValue()) { - MultiValuePropertyDescriptor multiValuePropertyDescriptor = - (MultiValuePropertyDescriptor) propertyDescriptor; - multiValued = "yes. Delimiter is '" - + multiValuePropertyDescriptor.multiValueDelimiter() + "'."; - } + // TODO document property syntax + // if (propertyDescriptor.isMultiValue()) { + // MultiValuePropertyDescriptor multiValuePropertyDescriptor = + // (MultiValuePropertyDescriptor) propertyDescriptor; + // multiValued = "yes. Delimiter is '" + // + multiValuePropertyDescriptor.multiValueDelimiter() + "'."; + // } lines.add("|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(propertyDescriptor.name())) + "|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(defaultValue)) + "|" @@ -531,16 +530,15 @@ public class RuleDocGenerator { if (realDefaultValue != null) { defaultValue = propertyDescriptor.asDelimitedString(realDefaultValue); - - if (pad && propertyDescriptor.isMultiValue()) { - MultiValuePropertyDescriptor> multiPropertyDescriptor = (MultiValuePropertyDescriptor>) propertyDescriptor; - - // surround the delimiter with spaces, so that the browser can wrap - // the value nicely - defaultValue = defaultValue.replaceAll(Pattern.quote( - String.valueOf(multiPropertyDescriptor.multiValueDelimiter())), - " " + multiPropertyDescriptor.multiValueDelimiter() + " "); - } + // TODO document multi value properties + // if (pad && propertyDescriptor.isMultiValue()) { + // MultiValuePropertyDescriptor> multiPropertyDescriptor = (MultiValuePropertyDescriptor>) propertyDescriptor; + // // surround the delimiter with spaces, so that the browser can wrap + // // the value nicely + // defaultValue = defaultValue.replaceAll(Pattern.quote( + // String.valueOf(multiPropertyDescriptor.multiValueDelimiter())), + // " " + multiPropertyDescriptor.multiValueDelimiter() + " "); + // } } return defaultValue; } From 46c0717c1a0606bf006d4f2bd99365debaa80c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 01:22:20 +0200 Subject: [PATCH 022/347] Simplify AvoidDuplicateLiteralsRule --- .../AbstractSingleValueProperty.java | 6 - .../pmd/properties/CharacterProperty.java | 105 ------------------ .../pmd/properties/FileProperty.java | 68 ------------ .../properties/GenericPropertyDescriptor.java | 5 - .../pmd/properties/PropertyBuilder.java | 4 +- .../pmd/properties/PropertyDescriptor.java | 14 --- .../pmd/properties/RegexProperty.java | 81 -------------- 7 files changed, 3 insertions(+), 280 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java index 3d18d07c0a..6e0836bb8c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java @@ -45,12 +45,6 @@ package net.sourceforge.pmd.properties; } - @Override - public final boolean isMultiValue() { - return false; - } - - @Override public String asDelimitedString(T value) { return asString(value); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java deleted file mode 100644 index 8bad03353a..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/CharacterProperty.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * 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 net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; - - -/** - * Defines a property type that supports single Character values. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor}. A builder is available from {@link PropertyFactory#charProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class CharacterProperty extends AbstractSingleValueProperty { - - /** - * Constructor for CharacterProperty. - * - * @param theName String - * @param theDescription String - * @param defaultStr String - * @param theUIOrder float - * - * @throws IllegalArgumentException - * @deprecated Use {@link PropertyFactory#charProperty(String)} - */ - @Deprecated - public CharacterProperty(String theName, String theDescription, String defaultStr, float theUIOrder) { - this(theName, theDescription, charFrom(defaultStr), theUIOrder, false); - } - - - /** Master constructor. */ - private CharacterProperty(String theName, String theDescription, Character theDefault, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, theDefault, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor. - * - * @param theName Name - * @param theDescription Description - * @param theDefault Default value - * @param theUIOrder UI order - * @deprecated Use {@link PropertyFactory#charProperty(String)} - */ - @Deprecated - public CharacterProperty(String theName, String theDescription, Character theDefault, float theUIOrder) { - this(theName, theDescription, theDefault, theUIOrder, false); - } - - - @Override - public Character createFrom(String valueString) throws IllegalArgumentException { - return charFrom(valueString); - } - - - /** - * Parses a String into a Character. - * - * @param charStr String to parse - * - * @return Parsed Character - * @throws IllegalArgumentException if the String doesn't have length 1 - */ - public static Character charFrom(String charStr) { - return CHARACTER_PARSER.valueOf(charStr); - } - - - /** - * @deprecated Use {@link PropertyFactory#charProperty(String)} - */ - @Deprecated - public static CharacterPBuilder named(String name) { - return new CharacterPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#charProperty(String)} - */ - @Deprecated - public static final class CharacterPBuilder extends SingleValuePropertyBuilder { - private CharacterPBuilder(String name) { - super(name); - } - - - @Override - public CharacterProperty build() { - return new CharacterProperty(this.name, this.description, this.defaultValue, this.uiOrder, isDefinedInXML); - } - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java deleted file mode 100644 index f6380adb6a..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/FileProperty.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.io.File; - -import org.apache.commons.lang3.StringUtils; - -import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; - - -/** - * Property taking a File object as its value. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * @deprecated Will be removed with 7.0.0 with no scheduled replacement - */ -@Deprecated -public final class FileProperty extends AbstractSingleValueProperty { - - - /** - * Constructor for file property. - * - * @param theName Name of the property - * @param theDescription Description - * @param theDefault Default value - * @param theUIOrder UI order - */ - public FileProperty(String theName, String theDescription, File theDefault, float theUIOrder) { - super(theName, theDescription, theDefault, theUIOrder, false); - } - - - /** Master constructor. */ - private FileProperty(String theName, String theDescription, File theDefault, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, theDefault, theUIOrder, isDefinedExternally); - } - - - @Override - public File createFrom(String propertyString) { - return StringUtils.isBlank(propertyString) ? null : new File(propertyString); - } - - - public static FilePBuilder named(String name) { - return new FilePBuilder(name); - } - - - public static final class FilePBuilder extends SingleValuePropertyBuilder { - private FilePBuilder(String name) { - super(name); - } - - - @Override - public FileProperty build() { - return new FileProperty(name, description, defaultValue, uiOrder, isDefinedInXML); - } - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 7504c87801..45dca139e3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -77,11 +77,6 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { return defaultValue; } - @Override - public boolean isMultiValue() { - return false; - } - @Override public XmlSyntax xmlStrategy() { return parser; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 12d2785cbd..612a0d9953 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -15,6 +15,7 @@ import java.util.function.Supplier; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -170,7 +171,8 @@ public abstract class PropertyBuilder, T> { * @throws IllegalArgumentException If the argument is null */ @SuppressWarnings("unchecked") - public B defaultValue(T val) { + public B defaultValue(@NonNull T val) { + //noinspection ConstantConditions if (val == null) { throw new IllegalArgumentException("Property values may not be null."); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 65635cae57..dc18c98502 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -84,20 +84,6 @@ public interface PropertyDescriptor { return null; } - /** - * Returns whether the property is multi-valued, i.e. an array of strings, - * - *

As unary property rule properties will return a value of one, you must use the get/setProperty accessors when - * working with the actual values. When working with multi-value properties then the get/setProperties accessors - * must be used.

- * - * @return boolean - * - * @deprecated The hard divide between multi- and single-value properties will be removed with 7.0.0 - */ - @Deprecated - boolean isMultiValue(); - /** * Returns the value represented by this string. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java deleted file mode 100644 index 5155f3c09d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/RegexProperty.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.regex.Pattern; - -import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; - - -/** - * Property which has a regex pattern as a value. This property has no multi-valued - * variant, since it would be ambiguous whether the delimiters are part of the regex - * or not. - * - * @author Clรฉment Fournier - * @since 6.2.0 - * @deprecated Use a {@code PropertyDescriptor}. A builder is available from {@link PropertyFactory#regexProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class RegexProperty extends AbstractSingleValueProperty { - - RegexProperty(String theName, String theDescription, Pattern theDefault, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, theDefault, theUIOrder, isDefinedExternally); - } - - - @Override - protected Pattern createFrom(String toParse) { - return Pattern.compile(toParse); - } - - - /** - * Creates a new builder for a regex property. - * - * @param name The name of the property - * - * @return A new builder - * - * @deprecated Use {@link PropertyFactory#regexProperty(String)} - */ - @Deprecated - public static RegexPBuilder named(String name) { - return new RegexPBuilder(name); - } - - - /** - * Builder for a {@link RegexProperty}. - * - * @deprecated Use {@link PropertyFactory#regexProperty(String)} - */ - @Deprecated - public static final class RegexPBuilder extends SingleValuePropertyBuilder { - private RegexPBuilder(String name) { - super(name); - } - - - /** - * Specify a default pattern for the property. - * The argument must be a valid regex pattern. - * - * @param val Regex pattern - * - * @return The same builder - */ - public RegexPBuilder defaultValue(String val) { - return super.defaultValue(Pattern.compile(val)); - } - - - @Override - public RegexProperty build() { - return new RegexProperty(this.name, this.description, this.defaultValue, this.uiOrder, isDefinedInXML); - } - } -} From c5d5844c8ddb143a51487dce20d53bc2979d515e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 01:35:36 +0200 Subject: [PATCH 023/347] Update boolean property --- .../pmd/properties/BooleanProperty.java | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java deleted file mode 100644 index c97ddc06c6..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/BooleanProperty.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * 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.BOOLEAN_PARSER; - -import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; - - -/** - * Defines a property type that supports single Boolean values. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor} instead. A builder is available from {@link PropertyFactory#booleanProperty(String)} and its overloads. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class BooleanProperty extends AbstractSingleValueProperty { - - /** - * Constructor for BooleanProperty limited to a single value. Converts default argument string into a boolean. - * - * @param theName Name - * @param theDescription Description - * @param defaultBoolStr String representing the default value. - * @param theUIOrder UI order - * - * @deprecated Use {@link PropertyFactory#booleanProperty(String)} or its overloads. - */ - @Deprecated - public BooleanProperty(String theName, String theDescription, String defaultBoolStr, float theUIOrder) { - this(theName, theDescription, Boolean.parseBoolean(defaultBoolStr), theUIOrder, false); - } - - - /** Master constructor. */ - private BooleanProperty(String theName, String theDescription, boolean defaultValue, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, defaultValue, theUIOrder, isDefinedExternally); - } - - - /** - * Constructor. - * - * @param theName Name - * @param theDescription Description - * @param defaultValue Default value - * @param theUIOrder UI order - * - * @deprecated Use {@link PropertyFactory#booleanProperty(String)} or its overloads. - */ - @Deprecated - public BooleanProperty(String theName, String theDescription, boolean defaultValue, float theUIOrder) { - this(theName, theDescription, defaultValue, theUIOrder, false); - } - - - @Override - public Boolean createFrom(String propertyString) throws IllegalArgumentException { - return BOOLEAN_PARSER.valueOf(propertyString); - } - - - /** - * @deprecated Use {@link PropertyFactory#booleanProperty(String)} or its overloads. - */ - @Deprecated - public static BooleanPBuilder named(String name) { - return new BooleanPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#booleanProperty(String)} or its overloads. - */ - @Deprecated - public static final class BooleanPBuilder extends SingleValuePropertyBuilder { - private BooleanPBuilder(String name) { - super(name); - } - - - @Override - public BooleanProperty build() { - return new BooleanProperty(this.name, this.description, this.defaultValue, this.uiOrder, this.isDefinedInXML); - } - } - -} From 960dd157979ba36dbf2af002887098fd6fae2363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 01:49:08 +0200 Subject: [PATCH 024/347] Remove some string properties --- .../main/java/net/sourceforge/pmd/Rule.java | 2 +- .../pmd/properties/PropertyFactory.java | 20 +++++++------------ .../pmd/properties/ValueParserConstants.java | 8 +++++--- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java index 92b1fea733..215966f98e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java @@ -33,7 +33,7 @@ public interface Rule extends PropertySource { */ // TODO 7.0.0 use PropertyDescriptor> StringProperty VIOLATION_SUPPRESS_REGEX_DESCRIPTOR = new StringProperty("violationSuppressRegex", - "Suppress violations with messages matching a regular expression", null, Integer.MAX_VALUE - 1); + "Suppress violations with messages matching a regular expression", null, Integer.MAX_VALUE - 1); /** * Name of the property to universally suppress violations on nodes which diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 3f17761911..8b9603e265 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -4,12 +4,6 @@ package net.sourceforge.pmd.properties; -import static net.sourceforge.pmd.properties.ValueParserConstants.BOOLEAN_PARSER; -import static net.sourceforge.pmd.properties.ValueParserConstants.CHARACTER_PARSER; -import static net.sourceforge.pmd.properties.ValueParserConstants.DOUBLE_PARSER; -import static net.sourceforge.pmd.properties.ValueParserConstants.INTEGER_PARSER; -import static net.sourceforge.pmd.properties.ValueParserConstants.LONG_PARSER; -import static net.sourceforge.pmd.properties.ValueParserConstants.STRING_PARSER; import static net.sourceforge.pmd.properties.ValueParserConstants.enumerationParser; import java.util.HashMap; @@ -24,6 +18,7 @@ import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.constraints.NumericConstraints; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; //@formatter:off /** @@ -117,7 +112,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder intProperty(String name) { - return new GenericPropertyBuilder<>(name, INTEGER_PARSER, Integer.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.INTEGER, Integer.class); } @@ -154,7 +149,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder longIntProperty(String name) { - return new GenericPropertyBuilder<>(name, LONG_PARSER, Long.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.LONG, Long.class); } @@ -186,7 +181,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder doubleProperty(String name) { - return new GenericPropertyBuilder<>(name, DOUBLE_PARSER, Double.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.DOUBLE, Double.class); } @@ -234,10 +229,9 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder stringProperty(String name) { - return new GenericPropertyBuilder<>(name, STRING_PARSER, String.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.STRING, String.class); } - /** * Returns a builder for a property having as value a list of strings. The * format of the individual items is the same as for {@linkplain #stringProperty(String) stringProperty}. @@ -265,7 +259,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder charProperty(String name) { - return new GenericPropertyBuilder<>(name, CHARACTER_PARSER, Character.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.CHARACTER, Character.class); } @@ -292,7 +286,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder booleanProperty(String name) { - return new GenericPropertyBuilder<>(name, BOOLEAN_PARSER, Boolean.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.BOOLEAN, Boolean.class); } // We can add more useful factories with Java 8. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java index e4010a6bfe..b037b7e457 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java @@ -15,6 +15,8 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.properties.internal.StringParser; +import net.sourceforge.pmd.properties.internal.ValueSyntax; +import net.sourceforge.pmd.properties.internal.XmlSyntax; /** @@ -57,18 +59,18 @@ public final class ValueParserConstants { } - static StringParser enumerationParser(final Map mappings) { + static ValueSyntax enumerationParser(final Map mappings) { if (mappings.containsValue(null)) { throw new IllegalArgumentException("Map may not contain entries with null values"); } - return value -> { + return new ValueSyntax<>(value -> { if (!mappings.containsKey(value)) { throw new IllegalArgumentException("Value was not in the set " + mappings.keySet()); } return mappings.get(value); - }; + }); } From a97efaf975cc766ecb58f80745395b4c9cbf4325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 01:57:29 +0200 Subject: [PATCH 025/347] Remove external builder --- .../PropertyDescriptorExternalBuilder.java | 51 ------------------- .../pmd/properties/internal/XmlSyntax.java | 2 +- 2 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorExternalBuilder.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorExternalBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorExternalBuilder.java deleted file mode 100644 index 92885b4d5b..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorExternalBuilder.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.builders; - -import java.util.Map; - -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyDescriptorField; - - -/** - * Builds properties from a map of key value pairs, eg extracted from an XML element. - * - * @param The type of values. - * @deprecated see {@link net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder} - - * @author Clรฉment Fournier - * @since 6.0.0 - * @deprecated The property XML API will not need this anymore - */ -@Deprecated -public interface PropertyDescriptorExternalBuilder { - - - /** - * Whether this descriptor is multi-valued. - * - * @return True if this descriptor is multi-valued - */ - boolean isMultiValue(); - - - /** - * Type of the values of the descriptor, or component type if this descriptor is multi-valued. - * - * @return Type of the values - */ - Class valueType(); - - - /** - * Builds a descriptor. The descriptor returned is tagged as built externally. - * - * @param fields Key value pairs - * - * @return A builder - */ - PropertyDescriptor build(Map fields); -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java index ce8e512d4d..3620bbc077 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java @@ -12,7 +12,7 @@ import org.w3c.dom.Element; /** - * Strategy to serialize a property to and from XML. + * Strategy to serialize a value to and from XML. * * @author Clรฉment Fournier */ From 17d9f6e39db12e0fa8dd34c4754d8aef0aaa7106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Sep 2019 02:16:15 +0200 Subject: [PATCH 026/347] Remove some stuff --- .../pmd/properties/AbstractProperty.java | 30 +------------------ .../AbstractSingleValueProperty.java | 8 +---- .../pmd/properties/internal/SyntaxSet.java | 5 ++++ .../pmd/properties/internal/XmlSyntax.java | 4 +++ 4 files changed, 11 insertions(+), 36 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java index e1bf08d02d..84442e9eb6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java @@ -4,12 +4,9 @@ package net.sourceforge.pmd.properties; -import static net.sourceforge.pmd.properties.PropertyDescriptorField.DEFAULT_VALUE; import static net.sourceforge.pmd.properties.PropertyDescriptorField.DESCRIPTION; import static net.sourceforge.pmd.properties.PropertyDescriptorField.NAME; -import java.util.Map; - import org.apache.commons.lang3.StringUtils; @@ -27,8 +24,6 @@ import org.apache.commons.lang3.StringUtils; private final String name; private final String description; - private final float uiOrder; - private final boolean isDefinedExternally; /** @@ -40,15 +35,13 @@ import org.apache.commons.lang3.StringUtils; * * @throws IllegalArgumentException If name or description are empty, or UI order is negative. */ - protected AbstractProperty(String theName, String theDescription, float theUIOrder, boolean isDefinedExternally) { + protected AbstractProperty(String theName, String theDescription, float theUIOrder) { if (theUIOrder < 0) { throw new IllegalArgumentException("Property attribute 'UI order' cannot be null or blank"); } name = checkNotEmpty(theName, NAME); description = checkNotEmpty(theDescription, DESCRIPTION); - uiOrder = theUIOrder; - this.isDefinedExternally = isDefinedExternally; } @@ -92,27 +85,6 @@ import org.apache.commons.lang3.StringUtils; } - /** - * Adds this property's attributes to the map. Subclasses can override this to add more {@link - * PropertyDescriptorField}. - * - * @param attributes The map to fill - */ - protected void addAttributesTo(Map attributes) { - attributes.put(NAME, name); - attributes.put(DESCRIPTION, description); - attributes.put(DEFAULT_VALUE, defaultAsString()); - } - - - /** - * Returns a string representation of the default value. - * - * @return A string representation of the default value. - */ - protected abstract String defaultAsString(); - - private static String checkNotEmpty(String arg, PropertyDescriptorField argId) throws IllegalArgumentException { if (StringUtils.isBlank(arg)) { throw new IllegalArgumentException("Property attribute '" + argId + "' cannot be null or blank"); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java index 6e0836bb8c..504ac9282f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java @@ -33,7 +33,7 @@ package net.sourceforge.pmd.properties; */ protected AbstractSingleValueProperty(String theName, String theDescription, T theDefault, float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, theUIOrder, isDefinedExternally); + super(theName, theDescription, theUIOrder); defaultValue = theDefault; } @@ -91,12 +91,6 @@ package net.sourceforge.pmd.properties; } - @Override - protected final String defaultAsString() { - return asString(defaultValue); - } - - @Override public final T valueFrom(String valueString) throws IllegalArgumentException { return createFrom(valueString); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index d353bcb032..6aa66d869d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -36,6 +36,11 @@ public final class SyntaxSet extends XmlSyntax { } /** + * TODO This constructor is a bit too general for now. The other constructor + * is the only one that's public. The problem with publishing this constructor, + * is that there may be a SyntaxSet somewhere in the 'forRead' set, and some + * overlapping values may be unlocked + * * @param forWrite Designated strategy for writing * @param forRead Set of supported syntaxes, must have pairwise different read names */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java index 3620bbc077..6523a441ec 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java @@ -70,6 +70,10 @@ public abstract class XmlSyntax { return readNames; } + /** + * Returns some examples for what XML output this strategy produces. + * For example, {@code 1}. + */ public abstract List examples(); @Override From 1f28da5bf4352b26ab9a2380a6169afe9fa0bfae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 18 Sep 2019 23:49:13 +0200 Subject: [PATCH 027/347] Add optional syntax --- .../pmd/properties/PropertyBuilder.java | 82 ++++++++++++------- .../pmd/properties/PropertyFactory.java | 23 ++---- .../constraints/PropertyConstraint.java | 23 ++++-- .../properties/internal/OptionalSyntax.java | 62 ++++++++++++++ .../pmd/properties/internal/SeqSyntax.java | 11 ++- .../pmd/properties/internal/SyntaxSet.java | 35 +++++--- .../pmd/properties/internal/ValueSyntax.java | 4 +- .../pmd/properties/internal/XmlSyntax.java | 46 +++++++---- .../properties/internal/XmlSyntaxUtils.java | 33 ++++---- 9 files changed, 219 insertions(+), 100 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 612a0d9953..18abffabe4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import java.util.regex.Pattern; @@ -19,7 +20,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.ValueSyntax; +import net.sourceforge.pmd.properties.internal.XmlSyntax; import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; // @formatter:off @@ -80,12 +81,16 @@ public abstract class PropertyBuilder, T> { String getDescription() { - if (StringUtils.isBlank(description)) { + if (!isDescriptionSet()) { throw new IllegalArgumentException("Description must be provided"); } return description; } + boolean isDescriptionSet() { + return StringUtils.isNotBlank(description); + } + /** Returns the value, asserting it has been set. */ T getDefaultValue() { @@ -100,7 +105,6 @@ public abstract class PropertyBuilder, T> { return defaultValue != null; } - /** * Specify the description of the property. This is used for documentation. * Please describe precisely how the property may change the behaviour of the @@ -207,28 +211,21 @@ public abstract class PropertyBuilder, T> { // This would allow specifying eg lists of numbers as 1,2,3, for which the syntax would look clumsy abstract static class BaseSinglePropertyBuilder, T> extends PropertyBuilder { - private final ValueSyntax parser; - private final Class type; + private final XmlSyntax parser; // Class is not final but a package-private constructor restricts inheritance - BaseSinglePropertyBuilder(String name, ValueSyntax parser, Class type) { + BaseSinglePropertyBuilder(String name, XmlSyntax parser) { super(name); this.parser = parser; - this.type = type; } - protected ValueSyntax getParser() { + protected XmlSyntax getParser() { return parser; } - protected Class getType() { - return type; - } - - /** * Returns a new builder that can be used to build a property * handling lists of Ts. The validators already added are @@ -248,6 +245,10 @@ public abstract class PropertyBuilder, T> { GenericCollectionPropertyBuilder> result = new GenericCollectionPropertyBuilder<>(getName(), getParser(), ArrayList::new); + if (isDescriptionSet()) { + result.desc(getDescription()); + } + for (PropertyConstraint validator : getConstraints()) { result.require(validator.toCollectionConstraint()); } @@ -256,6 +257,34 @@ public abstract class PropertyBuilder, T> { } + /** + * Returns a new builder that can be used to build a property + * handling {@code Optional}. The validators already added + * are used on the validator property. If the default value was + * previously set, it is converted to an optional with {@link Optional#ofNullable(Object)}. + * + * @return A new list property builder + * + * @throws IllegalStateException if the default value has already been set + */ + public GenericPropertyBuilder> toOptional() { + return new GenericPropertyBuilder>(this.getName(), XmlSyntaxUtils.toOptional(getParser())) { + { + if (isDefaultValueSet()) { + this.defaultValue(Optional.ofNullable(BaseSinglePropertyBuilder.this.getDefaultValue())); + } + + if (isDescriptionSet()) { + this.desc(BaseSinglePropertyBuilder.this.getDescription()); + } + + for (PropertyConstraint validator : BaseSinglePropertyBuilder.this.getConstraints()) { + this.require(validator.toOptionalConstraint()); + } + } + }; + } + @Override public PropertyDescriptor build() { @@ -279,10 +308,10 @@ public abstract class PropertyBuilder, T> { * @since 6.10.0 */ // Note: This type is used to fix the first type parameter for classes that don't need more API. - public static final class GenericPropertyBuilder extends BaseSinglePropertyBuilder, T> { + public static class GenericPropertyBuilder extends BaseSinglePropertyBuilder, T> { - GenericPropertyBuilder(String name, ValueSyntax parser, Class type) { - super(name, parser, type); + GenericPropertyBuilder(String name, XmlSyntax parser) { + super(name, parser); } } @@ -297,7 +326,7 @@ public abstract class PropertyBuilder, T> { public static final class RegexPropertyBuilder extends BaseSinglePropertyBuilder { RegexPropertyBuilder(String name) { - super(name, XmlSyntaxUtils.REGEX, Pattern.class); + super(name, XmlSyntaxUtils.REGEX); } @@ -354,7 +383,7 @@ public abstract class PropertyBuilder, T> { */ public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { - private final ValueSyntax parser; + private final XmlSyntax parser; private final Supplier emptyCollSupplier; private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; @@ -363,7 +392,7 @@ public abstract class PropertyBuilder, T> { * Builds a new builder for a collection type. Package-private. */ GenericCollectionPropertyBuilder(String name, - ValueSyntax parser, + XmlSyntax parser, Supplier emptyCollSupplier) { super(name); this.parser = parser; @@ -464,23 +493,16 @@ public abstract class PropertyBuilder, T> { // and C will be the actual type parameter of the returned property // descriptor - /* - (String name, - String description, - float uiOrder, - T defaultValue, - Set> constraints, - StringParser parser, - @Nullable PropertyTypeId typeId, - Class type - */ + XmlSyntax syntax = parser.supportsStringMapping() + ? XmlSyntaxUtils.seqAndDelimited(parser, emptyCollSupplier, false, Character.toString(multiValueDelimiter)) + : XmlSyntaxUtils.onlySeq(parser, emptyCollSupplier); return new GenericPropertyDescriptor<>( getName(), getDescription(), getDefaultValue(), getConstraints(), - XmlSyntaxUtils.withSeq(parser, emptyCollSupplier, false, Character.toString(multiValueDelimiter)), + syntax, typeId ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 8b9603e265..1a55b1a4d9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -112,7 +112,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder intProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.INTEGER, Integer.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.INTEGER); } @@ -149,7 +149,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder longIntProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.LONG, Long.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.LONG); } @@ -181,7 +181,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder doubleProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.DOUBLE, Double.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.DOUBLE); } @@ -229,7 +229,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder stringProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.STRING, String.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.STRING); } /** @@ -259,7 +259,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder charProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.CHARACTER, Character.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.CHARACTER); } @@ -286,7 +286,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder booleanProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.BOOLEAN, Boolean.class); + return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.BOOLEAN); } // We can add more useful factories with Java 8. @@ -315,15 +315,11 @@ public final class PropertyFactory { // TODO find solution to document the set of possible values // At best, map that requirement to a constraint (eg make parser return null if not found, and // add a non-null constraint with the right description.) - return new GenericPropertyBuilder<>(name, enumerationParser(nameToValue), (Class) Object.class); + return new GenericPropertyBuilder<>(name, enumerationParser(nameToValue)); } public static > GenericPropertyBuilder enumProperty(String name, Class enumClass) { - return new GenericPropertyBuilder<>( - name, - enumerationParser(EnumUtils.getEnumMap(enumClass)), - enumClass - ); + return new GenericPropertyBuilder<>(name, enumerationParser(EnumUtils.getEnumMap(enumClass))); } public static > GenericPropertyBuilder enumProperty(String name, Class enumClass, Function labelMaker) { @@ -332,8 +328,7 @@ public final class PropertyFactory { labels.put(labelMaker.apply(constant), constant); } - - return new GenericPropertyBuilder<>(name, enumerationParser(labels), enumClass); + return new GenericPropertyBuilder<>(name, enumerationParser(labels)); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java index 808582cd67..d55e5f8deb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.properties.constraints; +import java.util.Optional; import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; @@ -52,15 +53,25 @@ public interface PropertyConstraint { */ String getConstraintDescription(); - /** - * Returns a constraint that validates a collection of Ts - * by checking each component conforms to this conforms. - * - * @return A collection validator + * Returns a constraint that validates an {@code Optional} + * by checking that the value conforms to this constraint if + * it is non-empty. */ @Experimental - PropertyConstraint> toCollectionConstraint(); + default PropertyConstraint> toOptionalConstraint() { + return new PropertyConstraint>() { + @Override + public @Nullable String validate(Optional value) { + return value.map(PropertyConstraint.this::validate).orElse(null); + } + + @Override + public String getConstraintDescription() { + return PropertyConstraint.this.getConstraintDescription(); + } + }; + } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java new file mode 100644 index 0000000000..12e7005d0a --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java @@ -0,0 +1,62 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.internal; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.w3c.dom.Element; + +/** + * Serialize an optional value. If the value is itself an {@code Optional}, + * then mentioning {@code } will yield a toplevel empty optional. So + * having a non-empty optional with an empty optional inside is disallowed. + */ +final class OptionalSyntax extends XmlSyntax> { + + private static final String EMPTY_NAME = "none"; + private final XmlSyntax itemSyntax; + + OptionalSyntax(XmlSyntax itemSyntax) { + this.itemSyntax = itemSyntax; + + } + + @Override + public void toXml(Element container, Optional value) { + + } + + @Override + public Optional fromXml(Element element, XmlErrorReporter err) { + if (element.getTagName().equals(EMPTY_NAME)) { + return Optional.empty(); + } else { + return Optional.ofNullable(itemSyntax.fromXml(element, err)); + } + } + + @Override + public String getWriteElementName(Optional value) { + return value.map(itemSyntax::getWriteElementName).orElse(EMPTY_NAME); + } + + @Override + public Set getSupportedReadElementNames() { + HashSet strings = new HashSet<>(itemSyntax.getSupportedReadElementNames()); + strings.add(EMPTY_NAME); + return strings; + } + + @Override + public List examples() { + ArrayList list = new ArrayList<>(itemSyntax.examples()); + list.add(""); + return list; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java index 980dc5efaa..3e3945ff31 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java @@ -18,7 +18,7 @@ import org.w3c.dom.Element; * 1 * } */ -public final class SeqSyntax> extends XmlSyntax { +public final class SeqSyntax> extends XmlSyntax.StableXmlSyntax { private final XmlSyntax itemSyntax; private final Supplier emptyCollSupplier; @@ -29,11 +29,10 @@ public final class SeqSyntax> extends XmlSyntax { this.emptyCollSupplier = emptyCollSupplier; } - @Override public void toXml(Element container, C value) { for (T v : value) { - Element item = container.getOwnerDocument().createElement(itemSyntax.getWriteElementName()); + Element item = container.getOwnerDocument().createElement(itemSyntax.getWriteElementName(v)); itemSyntax.toXml(item, v); container.appendChild(item); } @@ -54,9 +53,9 @@ public final class SeqSyntax> extends XmlSyntax { @Override public List examples() { - return Collections.singletonList("<" + getWriteElementName() + ">\n" - + " " + itemSyntax.toString() + "\n" + return Collections.singletonList("\n" + + " " + String.join("\n ", itemSyntax.examples()) + "\n" + " ..." - + ""); + + ""); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index 6aa66d869d..3d88e187e6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -45,25 +45,36 @@ public final class SyntaxSet extends XmlSyntax { * @param forRead Set of supported syntaxes, must have pairwise different read names */ private SyntaxSet(XmlSyntax forWrite, Collection> forRead) { - super(forWrite.getWriteElementName(), readNames(forRead)); + super(); this.forWrite = forWrite; if (forRead.isEmpty()) { throw new IllegalArgumentException("Empty set of reads strategies!"); } + Map> map = new LinkedHashMap<>(); + for (XmlSyntax syntax : forRead) { + for (String name : syntax.getSupportedReadElementNames()) { - this.readIndex = forRead.stream().collect(Collectors.toMap( - XmlSyntax::getWriteElementName, - it -> it, - (a, b) -> { - // merge function - throw new IllegalArgumentException( - "Duplicate name '" + a.getWriteElementName() + "', for syntaxes " + a + " and " + b - ); - }, - LinkedHashMap::new - )); + map.merge(name, syntax, (a, b) -> { + // merge function + throw new IllegalArgumentException( + "Duplicate name '" + name + "', for syntaxes " + a + " and " + b + ); + }); + } + } + this.readIndex = map; + } + + @Override + public Set getSupportedReadElementNames() { + return readIndex.keySet(); + } + + @Override + public String getWriteElementName(T value) { + return forWrite.getWriteElementName(value); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index 00b6dd4112..01ae35c20f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -23,7 +23,7 @@ import org.w3c.dom.Element; *
This class is special because it enables compatibility with the
  * pre 7.0.0 XML syntax.
  */
-public final class ValueSyntax extends XmlSyntax {
+public final class ValueSyntax extends XmlSyntax.StableXmlSyntax {
 
     private static final String VALUE_NAME = "value";
     private final Function toString;
@@ -74,7 +74,7 @@ public final class ValueSyntax extends XmlSyntax {
 
     @Override
     public List examples() {
-        return Collections.singletonList("<" + getWriteElementName() + ">data");
+        return Collections.singletonList("data");
     }
 
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
index 6523a441ec..e18a70c4d6 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
@@ -18,16 +18,7 @@ import org.w3c.dom.Element;
  */
 public abstract class XmlSyntax {
 
-    private final String eltName;
-    private final Set readNames;
-
-    /* package */ XmlSyntax(String eltName) {
-        this(eltName, Collections.singleton(eltName));
-    }
-
-    /* package */ XmlSyntax(String eltName, Set readNames) {
-        this.eltName = eltName;
-        this.readNames = readNames;
+    /* package */ XmlSyntax() {
     }
 
     /** Extract the value from an XML element. */
@@ -62,13 +53,11 @@ public abstract class XmlSyntax {
 
 
     /** Get the preferred name used to write elements. */
-    public final String getWriteElementName() {
-        return eltName;
-    }
+    public abstract String getWriteElementName(T value);
+
+
+    public abstract Set getSupportedReadElementNames();
 
-    public final Set getSupportedReadElementNames() {
-        return readNames;
-    }
 
     /**
      * Returns some examples for what XML output this strategy produces.
@@ -80,4 +69,29 @@ public abstract class XmlSyntax {
     public String toString() {
         return examples().get(0);
     }
+
+    abstract static class StableXmlSyntax extends XmlSyntax {
+
+        private final String eltName;
+        private final Set readNames;
+
+        /* package */ StableXmlSyntax(String eltName) {
+            this(eltName, Collections.singleton(eltName));
+        }
+
+        /* package */ StableXmlSyntax(String eltName, Set readNames) {
+            this.eltName = eltName;
+            this.readNames = readNames;
+        }
+
+        @Override
+        public String getWriteElementName(T value) {
+            return eltName;
+        }
+
+        @Override
+        public Set getSupportedReadElementNames() {
+            return readNames;
+        }
+    }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
index f9e1ec0e12..15c0510923 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
@@ -8,6 +8,7 @@ package net.sourceforge.pmd.properties.internal;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
@@ -47,25 +48,24 @@ public final class XmlSyntaxUtils {
 
 
     private static  XmlSyntax> numberList(ValueSyntax valueSyntax) {
-        return withSeq(valueSyntax,
-                       ArrayList::new,
-                       true,
-                       ","
-        );
+        return seqAndDelimited(valueSyntax, ArrayList::new, true, ",");
     }
 
     private static  XmlSyntax> otherList(ValueSyntax valueSyntax) {
-        return withSeq(valueSyntax,
-                       ArrayList::new,
-                       true, // for now
-                       "|"
-        );
+        return seqAndDelimited(valueSyntax, ArrayList::new, true /* for now */, "|");
     }
 
-    public static > XmlSyntax withSeq(ValueSyntax itemSyntax,
-                                                                    Supplier emptyCollSupplier,
-                                                                    boolean preferOldSyntax,
-                                                                    String delimiter) {
+    public static  XmlSyntax> toOptional(XmlSyntax itemSyntax) {
+        return new OptionalSyntax<>(itemSyntax);
+    }
+
+    public static > XmlSyntax seqAndDelimited(XmlSyntax itemSyntax,
+                                                                            Supplier emptyCollSupplier,
+                                                                            boolean preferOldSyntax,
+                                                                            String delimiter) {
+        if (!itemSyntax.supportsStringMapping()) {
+            throw new IllegalArgumentException("Item syntax does not support string mapping " + itemSyntax);
+        }
         return new SyntaxSet<>(
             new SeqSyntax<>(itemSyntax, emptyCollSupplier),
             delimitedString(itemSyntax::toString, itemSyntax::fromString, delimiter, emptyCollSupplier),
@@ -73,6 +73,11 @@ public final class XmlSyntaxUtils {
         );
     }
 
+    public static > XmlSyntax onlySeq(XmlSyntax itemSyntax,
+                                                                    Supplier emptyCollSupplier) {
+        return new SeqSyntax<>(itemSyntax, emptyCollSupplier);
+    }
+
 
     public static > ValueSyntax delimitedString(
         Function toString,

From 99cb4bfd6f4918fbab2c9e5f2abdb4a3a0c6e3c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Thu, 19 Sep 2019 00:02:40 +0200
Subject: [PATCH 028/347] Turn violation suppression descriptors into Optional

---
 .../main/java/net/sourceforge/pmd/Rule.java   | 20 ++++++++++++----
 .../net/sourceforge/pmd/RuleSetWriter.java    |  2 +-
 .../pmd/properties/internal/SyntaxSet.java    | 24 ++++---------------
 .../properties/internal/XmlSyntaxUtils.java   | 15 ++++++++++++
 .../pmd/properties/internal/XmlUtils.java     |  4 ++--
 .../pmd/renderers/HTMLRenderer.java           | 12 ++++++----
 .../java/net/sourceforge/pmd/ReportTest.java  |  6 +++--
 .../pmd/AbstractRuleSetFactoryTest.java       |  4 ++--
 8 files changed, 51 insertions(+), 36 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
index 215966f98e..9005643592 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
@@ -5,6 +5,7 @@
 package net.sourceforge.pmd;
 
 import java.util.List;
+import java.util.Optional;
 
 import net.sourceforge.pmd.annotation.Experimental;
 import net.sourceforge.pmd.lang.Language;
@@ -13,8 +14,9 @@ import net.sourceforge.pmd.lang.ParserOptions;
 import net.sourceforge.pmd.lang.ast.AstProcessingStage;
 import net.sourceforge.pmd.lang.ast.Node;
 import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+import net.sourceforge.pmd.properties.PropertyFactory;
 import net.sourceforge.pmd.properties.PropertySource;
-import net.sourceforge.pmd.properties.StringProperty;
 
 /**
  * This is the basic Rule interface for PMD rules.
@@ -32,16 +34,24 @@ public interface Rule extends PropertySource {
      * matching a regular expression.
      */
     // TODO 7.0.0 use PropertyDescriptor>
-    StringProperty VIOLATION_SUPPRESS_REGEX_DESCRIPTOR = new StringProperty("violationSuppressRegex",
-                                                                            "Suppress violations with messages matching a regular expression", null, Integer.MAX_VALUE - 1);
+    PropertyDescriptor> VIOLATION_SUPPRESS_REGEX_DESCRIPTOR =
+        PropertyFactory.stringProperty("violationSuppressRegex")
+                       .desc("Suppress violations with messages matching a regular expression")
+                       .toOptional()
+                       .defaultValue(Optional.empty())
+                       .build();
 
     /**
      * Name of the property to universally suppress violations on nodes which
      * match a given relative XPath expression.
      */
     // TODO 7.0.0 use PropertyDescriptor>
-    StringProperty VIOLATION_SUPPRESS_XPATH_DESCRIPTOR = new StringProperty("violationSuppressXPath",
-            "Suppress violations on nodes which match a given relative XPath expression.", null, Integer.MAX_VALUE - 2);
+    PropertyDescriptor> VIOLATION_SUPPRESS_XPATH_DESCRIPTOR =
+        PropertyFactory.stringProperty("violationSuppressXPath")
+                       .desc("Suppress violations on nodes which match a given relative XPath expression.")
+                       .toOptional()
+                       .defaultValue(Optional.empty())
+                       .build();
 
     /**
      * Get the Language of this Rule.
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
index 7ca76d7c97..f2c2c3371f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
@@ -309,7 +309,7 @@ public class RuleSetWriter {
 
         XmlSyntax xmlStrategy = propertyDescriptor.xmlStrategy();
 
-        Element valueElt = createPropertyValueElement(xmlStrategy.getWriteElementName());
+        Element valueElt = createPropertyValueElement(xmlStrategy.getWriteElementName(value));
         xmlStrategy.toXml(valueElt, value);
         element.appendChild(valueElt);
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java
index 3d88e187e6..1fc91b607c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java
@@ -4,6 +4,8 @@
 
 package net.sourceforge.pmd.properties.internal;
 
+import static net.sourceforge.pmd.properties.internal.XmlSyntaxUtils.enquote;
+
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -116,7 +118,8 @@ public final class SyntaxSet extends XmlSyntax {
         if (syntax == null) {
             throw err.error(
                 element,
-                "Unexpected element name " + enquote(element.getTagName()) + ", expecting " + formatPossibilities()
+                "Unexpected element name " + enquote(element.getTagName()) + ", expecting "
+                    + XmlSyntaxUtils.formatPossibilities(readIndex.keySet())
             );
         } else {
             return syntax.fromXml(element, err);
@@ -128,25 +131,6 @@ public final class SyntaxSet extends XmlSyntax {
         forWrite.toXml(container, value);
     }
 
-    // nullable
-    private String formatPossibilities() {
-        Set strings = readIndex.keySet();
-        if (strings.isEmpty()) {
-            return null;
-        } else if (strings.size() == 1) {
-            return enquote(strings.iterator().next());
-        } else {
-            return "one of " + strings.stream().map(SyntaxSet::enquote).collect(Collectors.joining(", "));
-        }
-    }
-
-    private static Set readNames(Collection> syntaxes) {
-        return syntaxes.stream()
-                       .flatMap(it -> it.getSupportedReadElementNames().stream())
-                       .collect(Collectors.toSet());
-    }
-
-    private static String enquote(String it) {return "'" + it + "'";}
 
     @Override
     public List examples() {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
index 15c0510923..633f3cd301 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
@@ -97,4 +98,18 @@ public final class XmlSyntaxUtils {
             }
         );
     }
+
+    static String enquote(String it) {return "'" + it + "'";}
+
+
+    // nullable
+    static String formatPossibilities(Set names) {
+        if (names.isEmpty()) {
+            return null;
+        } else if (names.size() == 1) {
+            return enquote(names.iterator().next());
+        } else {
+            return "one of " + names.stream().map(XmlSyntaxUtils::enquote).collect(Collectors.joining(", "));
+        }
+    }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java
index d644867c5c..a070d73385 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java
@@ -34,8 +34,8 @@ public final class XmlUtils {
 
     public static  T expectElement(XmlErrorReporter err, Element elt, XmlSyntax syntax) {
 
-        if (!elt.getTagName().equals(syntax.getWriteElementName())) {
-            err.warn(elt, "Expecting an element with name '" + syntax.getWriteElementName() + "'");
+        if (!syntax.getSupportedReadElementNames().contains(elt.getTagName())) {
+            err.warn(elt, "Wrong name, expect " + XmlSyntaxUtils.formatPossibilities(syntax.getSupportedReadElementNames()));
         } else {
             return syntax.fromXml(elt, err);
         }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java
index 21bda3970f..d50f25d76a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java
@@ -8,6 +8,7 @@ import java.io.IOException;
 import java.io.Writer;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -32,8 +33,11 @@ public class HTMLRenderer extends AbstractIncrementingRenderer {
     public static final String NAME = "html";
 
     // TODO use PropertyDescriptor> : we need a "blank" default value
-    public static final StringProperty LINE_PREFIX = new StringProperty("linePrefix",
-                                                                        "Prefix for line number anchor in the source file.", null, 1);
+    public static final PropertyDescriptor> LINE_PREFIX =
+        PropertyFactory.stringProperty("linePrefix").desc("Prefix for line number anchor in the source file.")
+                       .toOptional()
+                       .defaultValue(Optional.empty())
+                       .build();
 
     public static final PropertyDescriptor LINK_PREFIX =
         PropertyFactory.stringProperty("linkPrefix").desc("Path to HTML source.").defaultValue("").build();
@@ -74,7 +78,7 @@ public class HTMLRenderer extends AbstractIncrementingRenderer {
      */
     public void renderBody(Writer writer, Report report) throws IOException {
         linkPrefix = getProperty(LINK_PREFIX);
-        linePrefix = getProperty(LINE_PREFIX);
+        linePrefix = getProperty(LINE_PREFIX).orElse(null);
         replaceHtmlExtension = getProperty(HTML_EXTENSION);
 
         writer.write("

PMD report

"); @@ -94,7 +98,7 @@ public class HTMLRenderer extends AbstractIncrementingRenderer { @Override public void start() throws IOException { linkPrefix = getProperty(LINK_PREFIX); - linePrefix = getProperty(LINE_PREFIX); + linePrefix = getProperty(LINE_PREFIX).orElse(null); replaceHtmlExtension = getProperty(HTML_EXTENSION); writer.write("PMD" + PMD.EOL); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java index d6f0be3acc..f5a58668cb 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java @@ -8,6 +8,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.util.Optional; + import org.junit.Test; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -30,7 +32,7 @@ public class ReportTest extends RuleTst { public void testExclusionsInReportWithRuleViolationSuppressRegex() { Report rpt = new Report(); Rule rule = new FooRule(); - rule.setProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR, ".*No Foo.*"); + rule.setProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR, Optional.of(".*No Foo.*")); runTestFromString(TEST1, rule, rpt, defaultLanguage); assertTrue(rpt.getViolations().isEmpty()); assertEquals(1, rpt.getSuppressedViolations().size()); @@ -40,7 +42,7 @@ public class ReportTest extends RuleTst { public void testExclusionsInReportWithRuleViolationSuppressXPath() { Report rpt = new Report(); Rule rule = new FooRule(); - rule.setProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR, ".[@SimpleName = 'Foo']"); + rule.setProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR, Optional.of(".[@SimpleName = 'Foo']")); runTestFromString(TEST1, rule, rpt, defaultLanguage); assertTrue(rpt.getViolations().isEmpty()); assertEquals(1, rpt.getSuppressedViolations().size()); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java index a2266b2705..866166a7b5 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -176,7 +176,7 @@ public abstract class AbstractRuleSetFactoryTest { .append(PMD.EOL); } // Should not have violation suppress regex property - if (rule.getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR) != null) { + if (rule.getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR).isPresent()) { invalidRegexSuppress++; messages.append("Rule ") .append(fileName) @@ -188,7 +188,7 @@ public abstract class AbstractRuleSetFactoryTest { .append(PMD.EOL); } // Should not have violation suppress xpath property - if (rule.getProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR) != null) { + if (rule.getProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR).isPresent()) { invalidXPathSuppress++; messages.append("Rule ").append(fileName).append("/").append(rule.getName()).append(" should not have '").append(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR.name()).append("', this is intended for end user customization only.").append(PMD.EOL); } From 87a3bed802ee00d92682c6aeae3efb9d175b31ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 00:10:34 +0200 Subject: [PATCH 029/347] Remove last remnants of old implementations --- .../pmd/properties/AbstractProperty.java | 96 --------------- .../AbstractSingleValueProperty.java | 110 ------------------ .../SingleValuePropertyDescriptor.java | 20 ---- .../pmd/properties/StringProperty.java | 74 ------------ .../constraints/PropertyConstraint.java | 2 + .../properties/internal/OptionalSyntax.java | 4 +- .../pmd/properties/internal/SyntaxSet.java | 4 +- .../pmd/properties/internal/XmlSyntax.java | 7 +- .../pmd/properties/internal/XmlUtils.java | 4 +- 9 files changed, 12 insertions(+), 309 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java deleted file mode 100644 index 84442e9eb6..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractProperty.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import static net.sourceforge.pmd.properties.PropertyDescriptorField.DESCRIPTION; -import static net.sourceforge.pmd.properties.PropertyDescriptorField.NAME; - -import org.apache.commons.lang3.StringUtils; - - -/** - * Abstract class for properties. - * - * @param The type of the property's value. This is a list type for multi-valued properties - * - * @author Brian Remedios - * @author Clรฉment Fournier - * @version Refactored June 2017 (6.0.0) - */ -// @Deprecated // will be replaced by another base class in the next PR -/* default */ abstract class AbstractProperty implements PropertyDescriptor { - - private final String name; - private final String description; - - - /** - * Constructor for an abstract property. - * - * @param theName Name of the property - * @param theDescription Description - * @param theUIOrder UI order - * - * @throws IllegalArgumentException If name or description are empty, or UI order is negative. - */ - protected AbstractProperty(String theName, String theDescription, float theUIOrder) { - if (theUIOrder < 0) { - throw new IllegalArgumentException("Property attribute 'UI order' cannot be null or blank"); - } - - name = checkNotEmpty(theName, NAME); - description = checkNotEmpty(theDescription, DESCRIPTION); - } - - - @Override - public String description() { - return description; - } - - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (obj instanceof PropertyDescriptor) { - return name.equals(((PropertyDescriptor) obj).name()); - } - return false; - } - - - @Override - public int hashCode() { - return name.hashCode(); - } - - - @Override - public String toString() { - return "[PropertyDescriptor: name=" + name() + ',' - + " value=" + defaultValue() + ']'; - } - - - @Override - public String name() { - return name; - } - - - private static String checkNotEmpty(String arg, PropertyDescriptorField argId) throws IllegalArgumentException { - if (StringUtils.isBlank(arg)) { - throw new IllegalArgumentException("Property attribute '" + argId + "' cannot be null or blank"); - } - return arg; - } - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java deleted file mode 100644 index 504ac9282f..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractSingleValueProperty.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - - -/** - * Single value property. - * - * @param The type of the value. - * - * @author Clรฉment Fournier - */ -@Deprecated -/* default */ abstract class AbstractSingleValueProperty extends AbstractProperty - implements SingleValuePropertyDescriptor { - - /** Default value. */ - private T defaultValue; - - - /** - * Creates a single value property. - * - * @param theName Name of the property - * @param theDescription Description - * @param theUIOrder UI order - * @param theDefault Default value - * @param isDefinedExternally Whether the property is defined in the XML (by a XPath rule) or not - * - * @throws IllegalArgumentException If name or description are empty, or UI order is negative. - */ - protected AbstractSingleValueProperty(String theName, String theDescription, T theDefault, - float theUIOrder, boolean isDefinedExternally) { - super(theName, theDescription, theUIOrder); - - defaultValue = theDefault; - } - - - @Override - public final T defaultValue() { - return defaultValue; - } - - - @Override - public String asDelimitedString(T value) { - return asString(value); - } - - - /** - * Returns a string representation of the value, even if it's null. - * - * @param value The value to describe - * - * @return A string representation of the value - */ - protected String asString(T value) { - return value == null ? "" : value.toString(); - } - - - @Override - public String errorFor(T value) { - return valueErrorFor(value); - } - - - /** - * Checks the value for an error. - * - * @param value Value to check - * - * @return A diagnostic error message, or null if there's no problem - */ - protected String valueErrorFor(T value) { - return value != null || defaultHasNullValue() ? null : "missing value"; - } - - - /** - * Returns true if the default value is {@code null}. - * - * @return True if the default value is {@code null}. - */ - private boolean defaultHasNullValue() { - return defaultValue == null; - } - - - @Override - public final T valueFrom(String valueString) throws IllegalArgumentException { - return createFrom(valueString); - } - - - /** - * Parse a string and returns an instance of a value. - * - * @param toParse String to parse - * - * @return An instance of a value - */ - protected abstract T createFrom(String toParse); // this is there to be symmetrical to multi values - - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java deleted file mode 100644 index 6e00664c3b..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SingleValuePropertyDescriptor.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -/** - * Specializes property descriptors for single valued descriptors. For this type of property, the return value of the - * - * @param The type of value this descriptor works with. Cannot be a list. - * - * @author Clรฉment Fournier - * @since 6.0.0 - * - * @deprecated The hard divide between multi- and single-value properties will be removed with 7.0.0 - */ -@Deprecated -public interface SingleValuePropertyDescriptor extends PropertyDescriptor { - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java deleted file mode 100644 index 2697c6544b..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/StringProperty.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import net.sourceforge.pmd.properties.builders.SingleValuePropertyBuilder; - - -/** - * Defines a datatype that supports single String values. - * - * @author Brian Remedios - * @version Refactored June 2017 (6.0.0) - * @deprecated Use a {@code PropertyDescriptor}. A builder is available from {@link PropertyFactory#stringProperty(String)}. - * This class will be removed in 7.0.0. - */ -@Deprecated -public final class StringProperty extends AbstractSingleValueProperty { - - /** - * Constructor. - * - * @param theName Name - * @param theDescription Description - * @param defaultValue Default value - * @param theUIOrder UI order - * - * @deprecated Use {@link PropertyFactory#stringProperty(String)} - */ - @Deprecated - public StringProperty(String theName, String theDescription, String defaultValue, float theUIOrder) { - this(theName, theDescription, defaultValue, theUIOrder, false); - } - - - /** Master constructor. */ - private StringProperty(String theName, String theDescription, String defaultValue, float theUIOrder, boolean - isDefinedExternally) { - super(theName, theDescription, defaultValue, theUIOrder, isDefinedExternally); - } - - - @Override - public String createFrom(String valueString) { - return valueString; - } - - - /** - * @deprecated Use {@link PropertyFactory#stringProperty(String)} - */ - @Deprecated - public static StringPBuilder named(String name) { - return new StringPBuilder(name); - } - - - /** - * @deprecated Use {@link PropertyFactory#stringProperty(String)} - */ - @Deprecated - public static final class StringPBuilder extends SingleValuePropertyBuilder { - private StringPBuilder(String name) { - super(name); - } - - - @Override - public StringProperty build() { - return new StringProperty(this.name, this.description, this.defaultValue, this.uiOrder, isDefinedInXML); - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java index d55e5f8deb..679e4325c5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.properties.constraints; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.function.Predicate; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java index 12e7005d0a..f78cdd2a14 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java @@ -47,8 +47,8 @@ final class OptionalSyntax extends XmlSyntax> { } @Override - public Set getSupportedReadElementNames() { - HashSet strings = new HashSet<>(itemSyntax.getSupportedReadElementNames()); + public Set getReadElementNames() { + HashSet strings = new HashSet<>(itemSyntax.getReadElementNames()); strings.add(EMPTY_NAME); return strings; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index 1fc91b607c..651fb0b515 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -56,7 +56,7 @@ public final class SyntaxSet extends XmlSyntax { Map> map = new LinkedHashMap<>(); for (XmlSyntax syntax : forRead) { - for (String name : syntax.getSupportedReadElementNames()) { + for (String name : syntax.getReadElementNames()) { map.merge(name, syntax, (a, b) -> { // merge function @@ -70,7 +70,7 @@ public final class SyntaxSet extends XmlSyntax { } @Override - public Set getSupportedReadElementNames() { + public Set getReadElementNames() { return readIndex.keySet(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java index e18a70c4d6..ad8c915274 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java @@ -52,11 +52,12 @@ public abstract class XmlSyntax { } - /** Get the preferred name used to write elements. */ + /** Get the name that should be used for the element to represent [value]. */ public abstract String getWriteElementName(T value); - public abstract Set getSupportedReadElementNames(); + /** Get all names that can be read using this syntax. */ + public abstract Set getReadElementNames(); /** @@ -90,7 +91,7 @@ public abstract class XmlSyntax { } @Override - public Set getSupportedReadElementNames() { + public Set getReadElementNames() { return readNames; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java index a070d73385..600e7d0489 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java @@ -34,8 +34,8 @@ public final class XmlUtils { public static T expectElement(XmlErrorReporter err, Element elt, XmlSyntax syntax) { - if (!syntax.getSupportedReadElementNames().contains(elt.getTagName())) { - err.warn(elt, "Wrong name, expect " + XmlSyntaxUtils.formatPossibilities(syntax.getSupportedReadElementNames())); + if (!syntax.getReadElementNames().contains(elt.getTagName())) { + err.warn(elt, "Wrong name, expect " + XmlSyntaxUtils.formatPossibilities(syntax.getReadElementNames())); } else { return syntax.fromXml(elt, err); } From 66d437b7882585f1381b898dcca87543923ace1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 00:43:26 +0200 Subject: [PATCH 030/347] Cleanup API --- .../pmd/properties/PropertyBuilder.java | 2 +- .../pmd/properties/PropertyDescriptor.java | 6 +- .../properties/PropertyDescriptorField.java | 27 +-- .../pmd/properties/PropertyFactory.java | 2 +- .../pmd/properties/ValueParserConstants.java | 160 ------------------ .../pmd/properties/internal/SeqSyntax.java | 2 +- .../pmd/properties/internal/SyntaxSet.java | 4 +- .../pmd/properties/internal/ValueSyntax.java | 8 +- .../pmd/properties/internal/XmlSyntax.java | 8 +- .../properties/internal/XmlSyntaxUtils.java | 83 ++++++++- .../properties/PropertyDescriptorTest.java | 4 +- 11 files changed, 95 insertions(+), 211 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 18abffabe4..3ba18ddb76 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -494,7 +494,7 @@ public abstract class PropertyBuilder, T> { // descriptor XmlSyntax syntax = parser.supportsStringMapping() - ? XmlSyntaxUtils.seqAndDelimited(parser, emptyCollSupplier, false, Character.toString(multiValueDelimiter)) + ? XmlSyntaxUtils.seqAndDelimited(parser, emptyCollSupplier, false, multiValueDelimiter) : XmlSyntaxUtils.onlySeq(parser, emptyCollSupplier); return new GenericPropertyDescriptor<>( diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index dc18c98502..9541bfd3ab 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.properties; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.properties.internal.ValueSyntax; import net.sourceforge.pmd.properties.internal.XmlSyntax; @@ -55,9 +53,7 @@ public interface PropertyDescriptor { * Returns the strategy used to read and write this property to XML. * May support strings too. */ - default XmlSyntax xmlStrategy() { - return new ValueSyntax<>(this::asDelimitedString, this::valueFrom); - } + XmlSyntax xmlStrategy(); /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java index bd88538a53..05bbc1b2f1 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.properties; -import java.util.Objects; - import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; @@ -35,19 +33,7 @@ public enum PropertyDescriptorField { /** The default value. */ DEFAULT_VALUE("value"), /** For multi-valued properties, this defines the delimiter of the single values. */ - DELIMITER("delimiter"), - /** The minimum allowed value for numeric properties. */ - MIN("min"), - /** The maximum allowed value for numeric properties. */ - MAX("max"), - /** To limit the range of valid values, package names. */ - LEGAL_PACKAGES("legalPackages"), - /** Labels for enumerated properties. */ - LABELS("labels"), - /** Choices for enumerated properties. */ - CHOICES("choices"), - /** Default index for enumerated properties. */ - DEFAULT_INDEX("defaultIndex"); + DELIMITER("delimiter"); private final String attributeName; @@ -60,7 +46,7 @@ public enum PropertyDescriptorField { public String getOrThrow(Element element, XmlErrorReporter err) { String attribute = element.getAttribute(attributeName); if (attribute == null) { - throw err.error(element, "Missing attribute '" + attributeName + "'"); + throw err.error(element, "Attribute '" + attributeName + "' is required, but missing"); } return attribute; @@ -91,13 +77,4 @@ public enum PropertyDescriptorField { } - public static PropertyDescriptorField getConstant(String name) { - for (PropertyDescriptorField f : values()) { - if (Objects.equals(f.attributeName, name)) { - return f; - } - } - return null; - } - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 1a55b1a4d9..4335273e48 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.properties; -import static net.sourceforge.pmd.properties.ValueParserConstants.enumerationParser; +import static net.sourceforge.pmd.properties.internal.XmlSyntaxUtils.enumerationParser; import java.util.HashMap; import java.util.List; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java deleted file mode 100644 index b037b7e457..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueParserConstants.java +++ /dev/null @@ -1,160 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.regex.Pattern; - -import org.apache.commons.lang3.StringUtils; - -import net.sourceforge.pmd.properties.internal.StringParser; -import net.sourceforge.pmd.properties.internal.ValueSyntax; -import net.sourceforge.pmd.properties.internal.XmlSyntax; - - -/** - * This class will be completely scrapped with 7.0.0. It only hid away the syntactic - * overhead caused by the lack of lambdas in Java 7. - * - * @author Clรฉment Fournier - * @since 6.0.0 - */ -public final class ValueParserConstants { - - - /** Extracts characters. */ - static final StringParser CHARACTER_PARSER = value -> { - if (value == null || value.length() != 1) { - throw new IllegalArgumentException("missing/ambiguous character value for string \"" + value + "\""); - } - return value.charAt(0); - }; - /** Extracts strings. That's a dummy used to return a list in StringMultiProperty. */ - static final StringParser STRING_PARSER = value -> value; - /** Extracts integers. */ - static final StringParser INTEGER_PARSER = Integer::valueOf; - /** Extracts booleans. */ - static final StringParser BOOLEAN_PARSER = Boolean::valueOf; - /** Extracts floats. */ - static final StringParser FLOAT_PARSER = Float::valueOf; - /** Extracts longs. */ - static final StringParser LONG_PARSER = Long::valueOf; - /** Extracts doubles. */ - static final StringParser DOUBLE_PARSER = Double::valueOf; - /** Extracts files */ - static final StringParser FILE_PARSER = File::new; - /** Compiles a regex. */ - static final StringParser REGEX_PARSER = Pattern::compile; - - - private ValueParserConstants() { - - } - - - static ValueSyntax enumerationParser(final Map mappings) { - - if (mappings.containsValue(null)) { - throw new IllegalArgumentException("Map may not contain entries with null values"); - } - - return new ValueSyntax<>(value -> { - if (!mappings.containsKey(value)) { - throw new IllegalArgumentException("Value was not in the set " + mappings.keySet()); - } - return mappings.get(value); - }); - } - - - /** - * 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 Element type of the target list - * - * @return A list of values - */ - public static StringParser> multi(final StringParser parser, final char delimiter) { - return value -> parsePrimitives(value, delimiter, parser); - } - - - /** - * Parses a string into a list of values of type {@literal }. - * - * @param toParse The string to parse - * @param delimiter The delimiter to use - * @param extractor The function mapping a string to an instance of {@code } - * @param The type of the values to parse - * - * @return A list of values - */ - private static List parsePrimitives(String toParse, char delimiter, Function extractor) { - String[] values = StringUtils.split(toParse, delimiter); - List result = new ArrayList<>(); - for (String s : values) { - result.add(extractor.apply(s)); - } - return result; - } - - private static final char ESCAPE_CHAR = '\\'; - - /** - * Parse a list delimited with the given delimiter, converting individual - * values to type {@code } with the given extractor. Any character is - * escaped with a backslash. This is useful to escape the delimiter, and - * to escape the backslash. For example: - *
{@code
-     *
-     * "a,c"  -> [ "a", "c" ]
-     * "a\,c" -> [ "a,c" ]
-     * "a\c"  -> [ "ac" ]
-     * "a\\c" -> [ "a\c" ]
-     * "a\"   -> [ "a\"  ]   (a backslash at the end of the string is just a backslash)
-     *
-     * }
- */ - static List parseListWithEscapes(String str, char delimiter, ValueParser extractor) { - if (str.isEmpty()) { - return Collections.emptyList(); - } - - List result = new ArrayList<>(); - StringBuilder currentToken = new StringBuilder(); - boolean inEscapeMode = false; - - for (int i = 0; i < str.length(); i++) { - char c = str.charAt(i); - - if (inEscapeMode) { - inEscapeMode = false; - currentToken.append(c); - } else if (c == delimiter) { - result.add(extractor.valueOf(currentToken.toString())); - currentToken = new StringBuilder(); - } else if (c == ESCAPE_CHAR && i < str.length() - 1) { - // this is ordered this way so that if the delimiter is - // itself a backslash, no escapes are processed. - inEscapeMode = true; - } else { - currentToken.append(c); - } - } - - if (currentToken.length() > 0) { - result.add(extractor.valueOf(currentToken.toString())); - } - return result; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java index 3e3945ff31..9b9bc3b0b4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java @@ -18,7 +18,7 @@ import org.w3c.dom.Element; * 1 * }
*/ -public final class SeqSyntax> extends XmlSyntax.StableXmlSyntax { +final class SeqSyntax> extends XmlSyntax.StableXmlSyntax { private final XmlSyntax itemSyntax; private final Supplier emptyCollSupplier; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java index 651fb0b515..4a124de3e1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java @@ -23,7 +23,7 @@ import net.sourceforge.pmd.util.CollectionUtil; * A set of syntaxes for read and write. One special syntax is designated * as the one used to write elements, the others are used to read. */ -public final class SyntaxSet extends XmlSyntax { +final class SyntaxSet extends XmlSyntax { private final XmlSyntax forWrite; private final Map> readIndex; @@ -32,7 +32,7 @@ public final class SyntaxSet extends XmlSyntax { * @param newSyntax Newer syntax (eg seq) * @param compat Value syntax (eg delimited string for sequence) */ - public SyntaxSet(XmlSyntax newSyntax, ValueSyntax compat, boolean preferNew) { + SyntaxSet(XmlSyntax newSyntax, ValueSyntax compat, boolean preferNew) { // the set here prunes duplicates this(preferNew ? newSyntax : compat, CollectionUtil.setOf(newSyntax, compat)); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java index 01ae35c20f..61ddeb5d8f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java @@ -23,20 +23,20 @@ import org.w3c.dom.Element; *
This class is special because it enables compatibility with the
  * pre 7.0.0 XML syntax.
  */
-public final class ValueSyntax extends XmlSyntax.StableXmlSyntax {
+final class ValueSyntax extends XmlSyntax.StableXmlSyntax {
 
     private static final String VALUE_NAME = "value";
     private final Function toString;
     private final Function fromString;
 
-    public ValueSyntax(Function toString,
-                       Function fromString) {
+    ValueSyntax(Function toString,
+                Function fromString) {
         super(VALUE_NAME);
         this.toString = toString;
         this.fromString = fromString;
     }
 
-    public ValueSyntax(Function fromString) {
+    ValueSyntax(Function fromString) {
         super(VALUE_NAME);
         this.toString = Objects::toString;
         this.fromString = fromString;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
index ad8c915274..f5e9c4c16b 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java
@@ -28,6 +28,10 @@ public abstract class XmlSyntax {
     /** Write the value into the given XML element. */
     public abstract void toXml(Element container, T value);
 
+    /**
+     * Returns true if this syntax knows how to map values of type {@code T}
+     * from and to a simple string, without XML.
+     */
     public boolean supportsStringMapping() {
         return false;
     }
@@ -38,7 +42,7 @@ public abstract class XmlSyntax {
      * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter)
      */
     public T fromString(String attributeData) {
-        throw new UnsupportedOperationException();
+        throw new UnsupportedOperationException("Check #supportsStringMapping()");
     }
 
     /**
@@ -48,7 +52,7 @@ public abstract class XmlSyntax {
      * @throws IllegalArgumentException      if something goes wrong (but should be reported on the error reporter)
      */
     public String toString(T value) {
-        throw new UnsupportedOperationException();
+        throw new UnsupportedOperationException("Check #supportsStringMapping()");
     }
 
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
index 633f3cd301..6288e454dd 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java
@@ -7,7 +7,9 @@ package net.sourceforge.pmd.properties.internal;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
@@ -49,11 +51,11 @@ public final class XmlSyntaxUtils {
 
 
     private static  XmlSyntax> numberList(ValueSyntax valueSyntax) {
-        return seqAndDelimited(valueSyntax, ArrayList::new, true, ",");
+        return seqAndDelimited(valueSyntax, ArrayList::new, true, ',');
     }
 
     private static  XmlSyntax> otherList(ValueSyntax valueSyntax) {
-        return seqAndDelimited(valueSyntax, ArrayList::new, true /* for now */, "|");
+        return seqAndDelimited(valueSyntax, ArrayList::new, true /* for now */, '|');
     }
 
     public static  XmlSyntax> toOptional(XmlSyntax itemSyntax) {
@@ -63,7 +65,7 @@ public final class XmlSyntaxUtils {
     public static > XmlSyntax seqAndDelimited(XmlSyntax itemSyntax,
                                                                             Supplier emptyCollSupplier,
                                                                             boolean preferOldSyntax,
-                                                                            String delimiter) {
+                                                                            char delimiter) {
         if (!itemSyntax.supportsStringMapping()) {
             throw new IllegalArgumentException("Item syntax does not support string mapping " + itemSyntax);
         }
@@ -83,22 +85,71 @@ public final class XmlSyntaxUtils {
     public static > ValueSyntax delimitedString(
         Function toString,
         Function fromString,
-        String delimiter,
+        char delimiter,
         Supplier emptyCollSupplier
     ) {
-
+        String delim = "" + delimiter;
         return new ValueSyntax<>(
-            coll -> coll.stream().map(toString).collect(Collectors.joining(delimiter)),
+            coll -> coll.stream().map(toString).collect(Collectors.joining(delim)),
             string -> {
                 C coll = emptyCollSupplier.get();
-                for (String item : string.split(Pattern.quote(delimiter))) {
-                    coll.add(fromString.apply(item));
-                }
+                coll.addAll(parseListWithEscapes(string, delimiter, fromString));
                 return coll;
             }
         );
     }
 
+    private static final char ESCAPE_CHAR = '\\';
+
+    /**
+     * Parse a list delimited with the given delimiter, converting individual
+     * values to type {@code } with the given extractor. Any character is
+     * escaped with a backslash. This is useful to escape the delimiter, and
+     * to escape the backslash. For example:
+     * 
{@code
+     *
+     * "a,c"  -> [ "a", "c" ]
+     * "a\,c" -> [ "a,c" ]
+     * "a\c"  -> [ "ac" ]
+     * "a\\c" -> [ "a\c" ]
+     * "a\"   -> [ "a\"  ]   (a backslash at the end of the string is just a backslash)
+     *
+     * }
+ */ + public static List parseListWithEscapes(String str, char delimiter, Function extractor) { + if (str.isEmpty()) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + StringBuilder currentToken = new StringBuilder(); + boolean inEscapeMode = false; + + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + + if (inEscapeMode) { + inEscapeMode = false; + currentToken.append(c); + } else if (c == delimiter) { + result.add(extractor.apply(currentToken.toString())); + currentToken = new StringBuilder(); + } else if (c == ESCAPE_CHAR && i < str.length() - 1) { + // this is ordered this way so that if the delimiter is + // itself a backslash, no escapes are processed. + inEscapeMode = true; + } else { + currentToken.append(c); + } + } + + if (currentToken.length() > 0) { + result.add(extractor.apply(currentToken.toString())); + } + return result; + } + + static String enquote(String it) {return "'" + it + "'";} @@ -112,4 +163,18 @@ public final class XmlSyntaxUtils { return "one of " + names.stream().map(XmlSyntaxUtils::enquote).collect(Collectors.joining(", ")); } } + + public static ValueSyntax enumerationParser(final Map mappings) { + + if (mappings.containsValue(null)) { + throw new IllegalArgumentException("Map may not contain entries with null values"); + } + + return new ValueSyntax<>(value -> { + if (!mappings.containsKey(value)) { + throw new IllegalArgumentException("Value was not in the set " + mappings.keySet()); + } + return mappings.get(value); + }); + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index c6276c5ddc..1a8f1fb2be 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -34,6 +35,7 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; /** @@ -337,7 +339,7 @@ public class PropertyDescriptorTest { private static List parseEscaped(String s, char d) { - return ValueParserConstants.parseListWithEscapes(s, d, ValueParserConstants.STRING_PARSER); + return XmlSyntaxUtils.parseListWithEscapes(s, d, Function.identity()); } @Test From b59b3ad2bf083986e674501c2cfe032b7a5a247e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 01:04:05 +0200 Subject: [PATCH 031/347] Remove old property builders --- .../pmd/properties/PropertyBuilder.java | 4 - .../builders/PropertyDescriptorBuilder.java | 91 ------------------- .../builders/SingleValuePropertyBuilder.java | 40 -------- 3 files changed, 135 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SingleValuePropertyBuilder.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 3ba18ddb76..7da7298a5b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -18,7 +18,6 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; -import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.internal.XmlSyntax; import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; @@ -44,9 +43,6 @@ import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; * stage during the build process to indicate invalid input. It usually tries * to do so as early as possible, rather than waiting for the call to {@link #build()}. * - *

Note: from 7.0.0 on, all property builders will - * extend this class instead of {@link PropertyDescriptorBuilder}. - * * @param Concrete type of this builder instance * @param Type of values the property handles * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java deleted file mode 100644 index fbf06dd78c..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/PropertyDescriptorBuilder.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.builders; - -import org.apache.commons.lang3.StringUtils; - -import net.sourceforge.pmd.properties.PropertyBuilder; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyFactory; - - -/** - * Base class for property builders. - * - * @param Value type of the built descriptor - * @param 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. - * - * @deprecated From 7.0.0 on, the only supported way to build properties will be through {@link PropertyFactory}. - * This class hierarchy is replaced by the newer {@link PropertyBuilder}. - * @author Clรฉment Fournier - * @since 6.0.0 - */ -@Deprecated -public abstract class PropertyDescriptorBuilder> { - - protected String name; - protected String description; - protected float uiOrder = 0f; - protected boolean isDefinedInXML = false; - - - protected PropertyDescriptorBuilder(String name) { - if (StringUtils.isBlank(name)) { - throw new IllegalArgumentException("Name must be provided"); - } - this.name = name; - } - - - /** - * Specify the description of the property. - * - * @param desc The description - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T desc(String desc) { - if (StringUtils.isBlank(desc)) { - throw new IllegalArgumentException("Description must be provided"); - } - this.description = desc; - return (T) this; - } - - - /** - * Specify the UI order of the property. - * - * @param f The UI order - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - @Deprecated - public T uiOrder(float f) { - this.uiOrder = f; - return (T) this; - } - - - /** - * Builds the descriptor and returns it. - * - * @return The built descriptor - * @throws IllegalArgumentException if parameters are incorrect - */ - public abstract PropertyDescriptor build(); - - - /** - * Returns the name of the property to be built. - */ - public String getName() { - return name; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SingleValuePropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SingleValuePropertyBuilder.java deleted file mode 100644 index 53e726509d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/builders/SingleValuePropertyBuilder.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.builders; - -/** - * For single-value property descriptors. - * - * @deprecated see {@link net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilder} - * @param Value type of the built descriptor - * @param Concrete type of this builder instance. - */ -@Deprecated -public abstract class SingleValuePropertyBuilder> - extends PropertyDescriptorBuilder { - - protected E defaultValue; - - - protected SingleValuePropertyBuilder(String name) { - super(name); - } - - - /** - * Specify a default value. - * - * @param val Value - * - * @return The same builder - */ - @SuppressWarnings("unchecked") - public T defaultValue(E val) { - this.defaultValue = val; - return (T) this; - } - - -} From b7deb3b30ecbdfab6c1ee1675cec02bd4aae68c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 01:08:16 +0200 Subject: [PATCH 032/347] Rename internal package --- pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java | 2 +- .../sourceforge/pmd/properties/GenericPropertyDescriptor.java | 2 +- .../java/net/sourceforge/pmd/properties/PropertyBuilder.java | 4 ++-- .../net/sourceforge/pmd/properties/PropertyDescriptor.java | 2 +- .../sourceforge/pmd/properties/PropertyDescriptorField.java | 2 +- .../java/net/sourceforge/pmd/properties/PropertyFactory.java | 4 ++-- .../java/net/sourceforge/pmd/properties/PropertyTypeId.java | 4 ++-- .../pmd/properties/{internal => xml}/OptionalSyntax.java | 2 +- .../pmd/properties/{internal => xml}/SeqSyntax.java | 2 +- .../pmd/properties/{internal => xml}/StringParser.java | 2 +- .../pmd/properties/{internal => xml}/SyntaxSet.java | 4 ++-- .../pmd/properties/{internal => xml}/ValueSyntax.java | 2 +- .../pmd/properties/{internal => xml}/XmlErrorReporter.java | 2 +- .../pmd/properties/{internal => xml}/XmlSyntax.java | 2 +- .../pmd/properties/{internal => xml}/XmlSyntaxUtils.java | 2 +- .../pmd/properties/{internal => xml}/XmlUtils.java | 2 +- .../src/main/java/net/sourceforge/pmd/rules/RuleFactory.java | 4 ++-- 17 files changed, 22 insertions(+), 22 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/OptionalSyntax.java (97%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/SeqSyntax.java (97%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/StringParser.java (93%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/SyntaxSet.java (97%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/ValueSyntax.java (97%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/XmlErrorReporter.java (94%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/XmlSyntax.java (98%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/XmlSyntaxUtils.java (99%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{internal => xml}/XmlUtils.java (96%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index f2c2c3371f..02b81c76cd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -38,7 +38,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.PropertyTypeId; -import net.sourceforge.pmd.properties.internal.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlSyntax; /** * This class represents a way to serialize a RuleSet to an XML configuration diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 45dca139e3..4b55325d11 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -9,7 +9,7 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlSyntax; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 7da7298a5b..30478aa93c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -19,8 +19,8 @@ import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.XmlSyntax; -import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; +import net.sourceforge.pmd.properties.xml.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; // @formatter:off /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 9541bfd3ab..d44e6bf0f5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.properties; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.properties.internal.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlSyntax; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java index 05bbc1b2f1..b2bb4a0980 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java @@ -9,7 +9,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.properties.internal.XmlErrorReporter; +import net.sourceforge.pmd.properties.xml.XmlErrorReporter; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 4335273e48..a526f774e5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.properties; -import static net.sourceforge.pmd.properties.internal.XmlSyntaxUtils.enumerationParser; +import static net.sourceforge.pmd.properties.xml.XmlSyntaxUtils.enumerationParser; import java.util.HashMap; import java.util.List; @@ -18,7 +18,7 @@ import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.constraints.NumericConstraints; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; +import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; //@formatter:off /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 1279ce795c..de909a2512 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -9,8 +9,8 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import net.sourceforge.pmd.properties.internal.XmlSyntax; -import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; +import net.sourceforge.pmd.properties.xml.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index f78cdd2a14..1a5975af09 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import java.util.ArrayList; import java.util.HashSet; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index 9b9bc3b0b4..2c03934f0a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import java.util.Collection; import java.util.Collections; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/StringParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/StringParser.java similarity index 93% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/StringParser.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/StringParser.java index a2280eb02b..9908307a4d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/StringParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/StringParser.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import java.util.function.Function; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java index 4a124de3e1..a129a39e7e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java @@ -2,9 +2,9 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; -import static net.sourceforge.pmd.properties.internal.XmlSyntaxUtils.enquote; +import static net.sourceforge.pmd.properties.xml.XmlSyntaxUtils.enquote; import java.util.Collection; import java.util.LinkedHashMap; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 61ddeb5d8f..d933c70269 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import java.util.Collections; import java.util.List; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java similarity index 94% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java index 8d1f30dca2..b491978f72 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import org.w3c.dom.Node; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntax.java similarity index 98% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntax.java index f5e9c4c16b..e890c91e8d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import java.util.Collections; import java.util.List; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java similarity index 99% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 6288e454dd..f9eb14a0e3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import java.util.ArrayList; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java similarity index 96% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java index 600e7d0489..5606bb8fb0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/internal/XmlUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.internal; +package net.sourceforge.pmd.properties.xml; import java.util.ArrayList; import java.util.List; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 9a6f821c97..98398cfa82 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -30,8 +30,8 @@ import net.sourceforge.pmd.properties.PropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; import net.sourceforge.pmd.properties.PropertyTypeId; -import net.sourceforge.pmd.properties.internal.XmlErrorReporter; -import net.sourceforge.pmd.properties.internal.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlErrorReporter; +import net.sourceforge.pmd.properties.xml.XmlSyntax; import net.sourceforge.pmd.util.ResourceLoader; From 24814daf34d83a196e39a5ccd4700a447d54acdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 01:16:25 +0200 Subject: [PATCH 033/347] Doc --- .../pmd/properties/xml/StringParser.java | 34 ------------------- .../pmd/properties/xml/XmlErrorReporter.java | 2 +- .../pmd/properties/xml/XmlSyntaxUtils.java | 25 ++++++++++++-- .../properties/PropertyDescriptorTest.java | 2 +- 4 files changed, 24 insertions(+), 39 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/StringParser.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/StringParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/StringParser.java deleted file mode 100644 index 9908307a4d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/StringParser.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties.xml; - -import java.util.function.Function; - -/** - * Parses a value from a string. - * - * @param The type of the value to parse - */ -@FunctionalInterface -public interface StringParser extends Function { - - /** An alias for {@link #valueOf(String)}. */ - @Override - default U apply(String s) throws IllegalArgumentException { - return valueOf(s); - } - - - /** - * Extracts a primitive from a string. - * - * @param value The string to parse - * - * @return The primitive found - * @throws IllegalArgumentException if the value couldn't be parsed - */ - U valueOf(String value) throws IllegalArgumentException; - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java index b491978f72..1dbdf2a894 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java @@ -10,7 +10,7 @@ import org.w3c.dom.Node; * Reports errors in an XML document. Implementations have a way to * associate nodes with their location in the document. * - * TODO this is an interface I ripped off another project. It's a placeholder for now + * TODO this is a placeholder for now. */ public interface XmlErrorReporter { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index f9eb14a0e3..22c418a7f9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -17,6 +17,10 @@ import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; +import net.sourceforge.pmd.annotation.InternalApi; + + +@InternalApi public final class XmlSyntaxUtils { public static final ValueSyntax STRING = new ValueSyntax<>(Function.identity()); @@ -40,7 +44,6 @@ public final class XmlSyntaxUtils { public static final XmlSyntax> CHAR_LIST = otherList(CHARACTER); public static final XmlSyntax> STRING_LIST = otherList(STRING); - public static final XmlSyntax> BOOLEAN_LIST = otherList(BOOLEAN); public static final XmlSyntax> REGEX_LIST = new SeqSyntax<>(REGEX, ArrayList::new); @@ -62,6 +65,20 @@ public final class XmlSyntaxUtils { return new OptionalSyntax<>(itemSyntax); } + /** + * Builds an XML syntax that understands a {@code } syntax and + * a delimited {@code } syntax. + * + * @param itemSyntax Serializer for the items, must support string mapping + * @param emptyCollSupplier Supplier for the collection + * @param preferOldSyntax If true, the property will be written with {@code }, + * otherwise with {@code }. + * @param delimiter Delimiter for the {@code } syntax + * @param Type of items + * @param Type of collection to handle + * + * @throws IllegalArgumentException If the item syntax doesn't support string mapping + */ public static > XmlSyntax seqAndDelimited(XmlSyntax itemSyntax, Supplier emptyCollSupplier, boolean preferOldSyntax, @@ -82,7 +99,7 @@ public final class XmlSyntaxUtils { } - public static > ValueSyntax delimitedString( + private static > ValueSyntax delimitedString( Function toString, Function fromString, char delimiter, @@ -150,7 +167,9 @@ public final class XmlSyntaxUtils { } - static String enquote(String it) {return "'" + it + "'";} + static String enquote(String it) { + return "'" + it + "'"; + } // nullable diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index 1a8f1fb2be..4e87d65f58 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -35,7 +35,7 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.internal.XmlSyntaxUtils; +import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; /** From e909edffd2c74c4b3a4975007007fc7842f4ac7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 01:34:56 +0200 Subject: [PATCH 034/347] Improve xml errors --- .../net/sourceforge/pmd/RuleSetWriter.java | 10 +++---- .../SchemaConstants.java} | 29 +++++++------------ .../pmd/properties/xml/SyntaxSet.java | 8 +---- .../pmd/properties/xml/XmlUtils.java | 4 +++ .../sourceforge/pmd/rules/RuleFactory.java | 24 +++++++-------- 5 files changed, 32 insertions(+), 43 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{PropertyDescriptorField.java => xml/SchemaConstants.java} (60%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 02b81c76cd..2e55ecb859 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -35,7 +35,7 @@ import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.ImmutableLanguage; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyDescriptorField; +import net.sourceforge.pmd.properties.xml.SchemaConstants; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.xml.XmlSyntax; @@ -305,7 +305,7 @@ public class RuleSetWriter { private Element createPropertyValueElement(PropertyDescriptor propertyDescriptor, T value) { Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property"); - PropertyDescriptorField.NAME.setOn(element, propertyDescriptor.name()); + SchemaConstants.NAME.setOn(element, propertyDescriptor.name()); XmlSyntax xmlStrategy = propertyDescriptor.xmlStrategy(); @@ -320,9 +320,9 @@ public class RuleSetWriter { final Element element = createPropertyValueElement(propertyDescriptor, propertyDescriptor.defaultValue()); - PropertyDescriptorField.NAME.setOn(element, propertyDescriptor.name()); - PropertyDescriptorField.TYPE.setOn(element, typeId.getStringId()); - PropertyDescriptorField.DESCRIPTION.setOn(element, propertyDescriptor.description()); + SchemaConstants.NAME.setOn(element, propertyDescriptor.name()); + SchemaConstants.TYPE.setOn(element, typeId.getStringId()); + SchemaConstants.DESCRIPTION.setOn(element, propertyDescriptor.description()); // TODO support property constraints in XML return element; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SchemaConstants.java similarity index 60% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SchemaConstants.java index b2bb4a0980..5e976995c7 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SchemaConstants.java @@ -1,28 +1,19 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties; + +package net.sourceforge.pmd.properties.xml; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.properties.xml.XmlErrorReporter; - /** - * Field names for parsing the properties out of the ruleset xml files. These are intended to be used as the keys to a - * map of fields to values. Most property descriptors can be built directly from such a map using their factory. - * - * @author Brian Remedios - * @see RuleSetFactory - * @see PropertyTypeId - * @deprecated Will be removed with 7.0.0 + * Constants of the ruleset schema. */ -@Deprecated -public enum PropertyDescriptorField { +public enum SchemaConstants { /** The type of the property. */ TYPE("type"), @@ -31,29 +22,29 @@ public enum PropertyDescriptorField { /** The description of the property. */ DESCRIPTION("description"), /** The default value. */ - DEFAULT_VALUE("value"), + PROPERTY_VALUE("value"), /** For multi-valued properties, this defines the delimiter of the single values. */ DELIMITER("delimiter"); private final String attributeName; - PropertyDescriptorField(String attributeName) { + SchemaConstants(String attributeName) { this.attributeName = attributeName; } @NonNull - public String getOrThrow(Element element, XmlErrorReporter err) { + public String getAttributeOrThrow(Element element, XmlErrorReporter err) { String attribute = element.getAttribute(attributeName); if (attribute == null) { - throw err.error(element, "Attribute '" + attributeName + "' is required, but missing"); + throw err.error(element, XmlUtils.MISSING_REQUIRED_ATTRIBUTE, attributeName); } return attribute; } @Nullable - public String getOptional(Element element) { + public String getAttributeOpt(Element element) { return element.getAttribute(attributeName); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java index a129a39e7e..49605f0b58 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.properties.xml; -import static net.sourceforge.pmd.properties.xml.XmlSyntaxUtils.enquote; - import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -116,11 +114,7 @@ final class SyntaxSet extends XmlSyntax { public T fromXml(Element element, XmlErrorReporter err) { XmlSyntax syntax = readIndex.get(element.getTagName()); if (syntax == null) { - throw err.error( - element, - "Unexpected element name " + enquote(element.getTagName()) + ", expecting " - + XmlSyntaxUtils.formatPossibilities(readIndex.keySet()) - ); + throw err.error(element, XmlUtils.UNEXPECTED_ELEMENT, element.getTagName(), XmlSyntaxUtils.formatPossibilities(readIndex.keySet())); } else { return syntax.fromXml(element, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java index 5606bb8fb0..124f38ec88 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java @@ -14,6 +14,10 @@ import org.w3c.dom.NodeList; public final class XmlUtils { + static final String UNEXPECTED_ELEMENT = "Unexpected element '%s', expecting %s"; + static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '%s' is missing"; + static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '%s' is missing"; + private XmlUtils() { } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 98398cfa82..631546b63a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.rules; -import static net.sourceforge.pmd.properties.PropertyDescriptorField.DEFAULT_VALUE; +import static net.sourceforge.pmd.properties.xml.SchemaConstants.PROPERTY_VALUE; import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; @@ -28,7 +28,7 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyDescriptorField; +import net.sourceforge.pmd.properties.xml.SchemaConstants; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.xml.XmlSyntax; @@ -274,7 +274,7 @@ public class RuleFactory { * @return An entry of property name to its value */ private Entry getPropertyValue(Element propertyElement) { - String name = propertyElement.getAttribute(PropertyDescriptorField.NAME.attributeName()); + String name = propertyElement.getAttribute(SchemaConstants.NAME.attributeName()); return new SimpleEntry<>(name, valueFrom(propertyElement)); } @@ -310,7 +310,7 @@ public class RuleFactory { * @return True if this element defines a new property, false if this is just stating a value */ private static boolean isPropertyDefinition(Element node) { - return node.hasAttribute(PropertyDescriptorField.TYPE.attributeName()); + return node.hasAttribute(SchemaConstants.TYPE.attributeName()); } /** @@ -321,9 +321,9 @@ public class RuleFactory { * @return The property descriptor */ private static PropertyDescriptor parsePropertyDefinition(Element propertyElement) { - XmlErrorReporter err = new XmlErrorReporter() {}; + XmlErrorReporter err = new XmlErrorReporter() { }; // TODO this is a fake instance, should be provided by context - String typeId = PropertyDescriptorField.TYPE.getOrThrow(propertyElement, err); + String typeId = SchemaConstants.TYPE.getAttributeOrThrow(propertyElement, err); PropertyTypeId factory = PropertyTypeId.lookupMnemonic(typeId); if (factory == null) { @@ -331,9 +331,9 @@ public class RuleFactory { } PropertyBuilder builder = - factory.newBuilder(PropertyDescriptorField.NAME.getOrThrow(propertyElement, err)); + factory.newBuilder(SchemaConstants.NAME.getAttributeOrThrow(propertyElement, err)); - builder.desc(PropertyDescriptorField.DESCRIPTION.getOrThrow(propertyElement, err)); + builder.desc(SchemaConstants.DESCRIPTION.getAttributeOrThrow(propertyElement, err)); propertyValueCapture(propertyElement, typeId, factory.getXmlSyntax(), builder, err); @@ -353,17 +353,17 @@ public class RuleFactory { T defaultValue; - String defaultAttr = DEFAULT_VALUE.getOptional(propertyElement); + String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElement); if (!StringUtils.isBlank(defaultAttr)) { if (syntax.supportsStringMapping()) { defaultValue = syntax.fromString(defaultAttr); } else { - throw err.error(propertyElement.getAttributeNode(DEFAULT_VALUE.attributeName()), + throw err.error(propertyElement.getAttributeNode(PROPERTY_VALUE.attributeName()), "Type " + typeId + " cannot be parsed from a string, use a nested element, e.g. " + String.join("\nor\n", syntax.examples())); } } else { - NodeList children = propertyElement.getElementsByTagName(DEFAULT_VALUE.attributeName()); + NodeList children = propertyElement.getElementsByTagName(PROPERTY_VALUE.attributeName()); if (children.getLength() == 1) { defaultValue = syntax.fromXml((Element) children.item(0), err); } else { @@ -377,7 +377,7 @@ public class RuleFactory { /** Gets the string value from a property node. */ private static String valueFrom(Element propertyNode) { - String strValue = propertyNode.getAttribute(DEFAULT_VALUE.attributeName()); + String strValue = propertyNode.getAttribute(PROPERTY_VALUE.attributeName()); if (StringUtils.isNotBlank(strValue)) { return strValue; From ce9a73b09fb85a50482e6db8f949c8f8f295f74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 01:37:48 +0200 Subject: [PATCH 035/347] Rename XmlSyntax --- .../net/sourceforge/pmd/RuleSetWriter.java | 4 ++-- .../properties/GenericPropertyDescriptor.java | 8 +++---- .../pmd/properties/PropertyBuilder.java | 16 ++++++------- .../pmd/properties/PropertyDescriptor.java | 4 ++-- .../pmd/properties/PropertyTypeId.java | 12 +++++----- .../xml/{SyntaxSet.java => MapperSet.java} | 24 +++++++++---------- .../pmd/properties/xml/OptionalSyntax.java | 6 ++--- .../pmd/properties/xml/SeqSyntax.java | 8 ++++--- .../pmd/properties/xml/ValueSyntax.java | 4 +++- .../xml/{XmlSyntax.java => XmlMapper.java} | 10 ++++---- .../pmd/properties/xml/XmlSyntaxUtils.java | 24 +++++++++---------- .../pmd/properties/xml/XmlUtils.java | 4 ++-- .../sourceforge/pmd/rules/RuleFactory.java | 8 +++---- 13 files changed, 68 insertions(+), 64 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/{SyntaxSet.java => MapperSet.java} (81%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/{XmlSyntax.java => XmlMapper.java} (91%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 2e55ecb859..c7f5e99540 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -38,7 +38,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.xml.SchemaConstants; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.PropertyTypeId; -import net.sourceforge.pmd.properties.xml.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlMapper; /** * This class represents a way to serialize a RuleSet to an XML configuration @@ -307,7 +307,7 @@ public class RuleSetWriter { Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property"); SchemaConstants.NAME.setOn(element, propertyDescriptor.name()); - XmlSyntax xmlStrategy = propertyDescriptor.xmlStrategy(); + XmlMapper xmlStrategy = propertyDescriptor.xmlStrategy(); Element valueElt = createPropertyValueElement(xmlStrategy.getWriteElementName(value)); xmlStrategy.toXml(valueElt, value); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 4b55325d11..9f4b045f6e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -9,7 +9,7 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlMapper; /** @@ -21,7 +21,7 @@ import net.sourceforge.pmd.properties.xml.XmlSyntax; final class GenericPropertyDescriptor implements PropertyDescriptor { - private final XmlSyntax parser; + private final XmlMapper parser; private final PropertyTypeId typeId; private final String name; private final String description; @@ -33,7 +33,7 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { String description, T defaultValue, Set> constraints, - XmlSyntax parser, + XmlMapper parser, @Nullable PropertyTypeId typeId) { this.name = name; @@ -78,7 +78,7 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { } @Override - public XmlSyntax xmlStrategy() { + public XmlMapper xmlStrategy() { return parser; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 30478aa93c..2ceba161e9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -19,7 +19,7 @@ import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; // @formatter:off @@ -207,17 +207,17 @@ public abstract class PropertyBuilder, T> { // This would allow specifying eg lists of numbers as 1,2,3, for which the syntax would look clumsy abstract static class BaseSinglePropertyBuilder, T> extends PropertyBuilder { - private final XmlSyntax parser; + private final XmlMapper parser; // Class is not final but a package-private constructor restricts inheritance - BaseSinglePropertyBuilder(String name, XmlSyntax parser) { + BaseSinglePropertyBuilder(String name, XmlMapper parser) { super(name); this.parser = parser; } - protected XmlSyntax getParser() { + protected XmlMapper getParser() { return parser; } @@ -306,7 +306,7 @@ public abstract class PropertyBuilder, T> { // Note: This type is used to fix the first type parameter for classes that don't need more API. public static class GenericPropertyBuilder extends BaseSinglePropertyBuilder, T> { - GenericPropertyBuilder(String name, XmlSyntax parser) { + GenericPropertyBuilder(String name, XmlMapper parser) { super(name, parser); } } @@ -379,7 +379,7 @@ public abstract class PropertyBuilder, T> { */ public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { - private final XmlSyntax parser; + private final XmlMapper parser; private final Supplier emptyCollSupplier; private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; @@ -388,7 +388,7 @@ public abstract class PropertyBuilder, T> { * Builds a new builder for a collection type. Package-private. */ GenericCollectionPropertyBuilder(String name, - XmlSyntax parser, + XmlMapper parser, Supplier emptyCollSupplier) { super(name); this.parser = parser; @@ -489,7 +489,7 @@ public abstract class PropertyBuilder, T> { // and C will be the actual type parameter of the returned property // descriptor - XmlSyntax syntax = parser.supportsStringMapping() + XmlMapper syntax = parser.supportsStringMapping() ? XmlSyntaxUtils.seqAndDelimited(parser, emptyCollSupplier, false, multiValueDelimiter) : XmlSyntaxUtils.onlySeq(parser, emptyCollSupplier); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index d44e6bf0f5..3c6a9a6653 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.properties; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.properties.xml.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlMapper; /** @@ -53,7 +53,7 @@ public interface PropertyDescriptor { * Returns the strategy used to read and write this property to XML. * May support strings too. */ - XmlSyntax xmlStrategy(); + XmlMapper xmlStrategy(); /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index de909a2512..167c85e66f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -9,7 +9,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import net.sourceforge.pmd.properties.xml.XmlSyntax; +import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; @@ -52,7 +52,7 @@ public enum PropertyTypeId { private static final Map CONSTANTS_BY_MNEMONIC; private final String stringId; - private final XmlSyntax xmlSyntax; + private final XmlMapper xmlMapper; private final Function> factory; @@ -65,14 +65,14 @@ public enum PropertyTypeId { } - PropertyTypeId(String id, XmlSyntax syntax, Function> factory) { + PropertyTypeId(String id, XmlMapper syntax, Function> factory) { this.stringId = id; - this.xmlSyntax = syntax; + this.xmlMapper = syntax; this.factory = factory; } - public XmlSyntax getXmlSyntax() { - return xmlSyntax; + public XmlMapper getXmlMapper() { + return xmlMapper; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java similarity index 81% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 49605f0b58..5620ee9674 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SyntaxSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -21,16 +21,16 @@ import net.sourceforge.pmd.util.CollectionUtil; * A set of syntaxes for read and write. One special syntax is designated * as the one used to write elements, the others are used to read. */ -final class SyntaxSet extends XmlSyntax { +final class MapperSet extends XmlMapper { - private final XmlSyntax forWrite; - private final Map> readIndex; + private final XmlMapper forWrite; + private final Map> readIndex; /** * @param newSyntax Newer syntax (eg seq) * @param compat Value syntax (eg delimited string for sequence) */ - SyntaxSet(XmlSyntax newSyntax, ValueSyntax compat, boolean preferNew) { + MapperSet(XmlMapper newSyntax, ValueSyntax compat, boolean preferNew) { // the set here prunes duplicates this(preferNew ? newSyntax : compat, CollectionUtil.setOf(newSyntax, compat)); } @@ -44,7 +44,7 @@ final class SyntaxSet extends XmlSyntax { * @param forWrite Designated strategy for writing * @param forRead Set of supported syntaxes, must have pairwise different read names */ - private SyntaxSet(XmlSyntax forWrite, Collection> forRead) { + private MapperSet(XmlMapper forWrite, Collection> forRead) { super(); this.forWrite = forWrite; @@ -52,8 +52,8 @@ final class SyntaxSet extends XmlSyntax { throw new IllegalArgumentException("Empty set of reads strategies!"); } - Map> map = new LinkedHashMap<>(); - for (XmlSyntax syntax : forRead) { + Map> map = new LinkedHashMap<>(); + for (XmlMapper syntax : forRead) { for (String name : syntax.getReadElementNames()) { map.merge(name, syntax, (a, b) -> { @@ -80,7 +80,7 @@ final class SyntaxSet extends XmlSyntax { @Override public @Nullable T fromString(String string) { - for (XmlSyntax syntax : supportedReadStrategies()) { + for (XmlMapper syntax : supportedReadStrategies()) { if (syntax.supportsStringMapping()) { return syntax.fromString(string); } @@ -92,7 +92,7 @@ final class SyntaxSet extends XmlSyntax { @Override public String toString(T value) { - for (XmlSyntax syntax : supportedReadStrategies()) { + for (XmlMapper syntax : supportedReadStrategies()) { if (syntax.supportsStringMapping()) { return syntax.toString(value); } @@ -101,18 +101,18 @@ final class SyntaxSet extends XmlSyntax { throw new UnsupportedOperationException(); } - public Set> supportedReadStrategies() { + public Set> supportedReadStrategies() { return new LinkedHashSet<>(readIndex.values()); } @Override public boolean supportsStringMapping() { - return supportedReadStrategies().stream().anyMatch(XmlSyntax::supportsStringMapping); + return supportedReadStrategies().stream().anyMatch(XmlMapper::supportsStringMapping); } @Override public T fromXml(Element element, XmlErrorReporter err) { - XmlSyntax syntax = readIndex.get(element.getTagName()); + XmlMapper syntax = readIndex.get(element.getTagName()); if (syntax == null) { throw err.error(element, XmlUtils.UNEXPECTED_ELEMENT, element.getTagName(), XmlSyntaxUtils.formatPossibilities(readIndex.keySet())); } else { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index 1a5975af09..6d7c551aca 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -17,12 +17,12 @@ import org.w3c.dom.Element; * then mentioning {@code } will yield a toplevel empty optional. So * having a non-empty optional with an empty optional inside is disallowed. */ -final class OptionalSyntax extends XmlSyntax> { +final class OptionalSyntax extends XmlMapper> { private static final String EMPTY_NAME = "none"; - private final XmlSyntax itemSyntax; + private final XmlMapper itemSyntax; - OptionalSyntax(XmlSyntax itemSyntax) { + OptionalSyntax(XmlMapper itemSyntax) { this.itemSyntax = itemSyntax; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index 2c03934f0a..f8bd4079ba 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -11,6 +11,8 @@ import java.util.function.Supplier; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; + /** * Serialize to and from a simple string. Examples: * @@ -18,12 +20,12 @@ import org.w3c.dom.Element; * 1 * }

*/ -final class SeqSyntax> extends XmlSyntax.StableXmlSyntax { +final class SeqSyntax> extends StableXmlMapper { - private final XmlSyntax itemSyntax; + private final XmlMapper itemSyntax; private final Supplier emptyCollSupplier; - SeqSyntax(XmlSyntax itemSyntax, Supplier emptyCollSupplier) { + SeqSyntax(XmlMapper itemSyntax, Supplier emptyCollSupplier) { super("seq"); this.itemSyntax = itemSyntax; this.emptyCollSupplier = emptyCollSupplier; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index d933c70269..9e938f91dc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -11,6 +11,8 @@ import java.util.function.Function; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; + /** * Serialize to and from a simple string. Examples: * @@ -23,7 +25,7 @@ import org.w3c.dom.Element; *
This class is special because it enables compatibility with the
  * pre 7.0.0 XML syntax.
  */
-final class ValueSyntax extends XmlSyntax.StableXmlSyntax {
+final class ValueSyntax extends StableXmlMapper {
 
     private static final String VALUE_NAME = "value";
     private final Function toString;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
similarity index 91%
rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntax.java
rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
index e890c91e8d..806a44e9c2 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
@@ -16,9 +16,9 @@ import org.w3c.dom.Element;
  *
  * @author Clรฉment Fournier
  */
-public abstract class XmlSyntax {
+public abstract class XmlMapper {
 
-    /* package */ XmlSyntax() {
+    /* package */ XmlMapper() {
     }
 
     /** Extract the value from an XML element. */
@@ -75,16 +75,16 @@ public abstract class XmlSyntax {
         return examples().get(0);
     }
 
-    abstract static class StableXmlSyntax extends XmlSyntax {
+    abstract static class StableXmlMapper extends XmlMapper {
 
         private final String eltName;
         private final Set readNames;
 
-        /* package */ StableXmlSyntax(String eltName) {
+        /* package */ StableXmlMapper(String eltName) {
             this(eltName, Collections.singleton(eltName));
         }
 
-        /* package */ StableXmlSyntax(String eltName, Set readNames) {
+        /* package */ StableXmlMapper(String eltName, Set readNames) {
             this.eltName = eltName;
             this.readNames = readNames;
         }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
index 22c418a7f9..f365232add 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
@@ -38,14 +38,14 @@ public final class XmlSyntaxUtils {
     public static final ValueSyntax DOUBLE = new ValueSyntax<>(Double::valueOf);
 
 
-    public static final XmlSyntax> INTEGER_LIST = numberList(INTEGER);
-    public static final XmlSyntax> DOUBLE_LIST = numberList(DOUBLE);
-    public static final XmlSyntax> LONG_LIST = numberList(LONG);
+    public static final XmlMapper> INTEGER_LIST = numberList(INTEGER);
+    public static final XmlMapper> DOUBLE_LIST = numberList(DOUBLE);
+    public static final XmlMapper> LONG_LIST = numberList(LONG);
 
-    public static final XmlSyntax> CHAR_LIST = otherList(CHARACTER);
-    public static final XmlSyntax> STRING_LIST = otherList(STRING);
+    public static final XmlMapper> CHAR_LIST = otherList(CHARACTER);
+    public static final XmlMapper> STRING_LIST = otherList(STRING);
 
-    public static final XmlSyntax> REGEX_LIST = new SeqSyntax<>(REGEX, ArrayList::new);
+    public static final XmlMapper> REGEX_LIST = new SeqSyntax<>(REGEX, ArrayList::new);
 
 
     private XmlSyntaxUtils() {
@@ -53,15 +53,15 @@ public final class XmlSyntaxUtils {
     }
 
 
-    private static  XmlSyntax> numberList(ValueSyntax valueSyntax) {
+    private static  XmlMapper> numberList(ValueSyntax valueSyntax) {
         return seqAndDelimited(valueSyntax, ArrayList::new, true, ',');
     }
 
-    private static  XmlSyntax> otherList(ValueSyntax valueSyntax) {
+    private static  XmlMapper> otherList(ValueSyntax valueSyntax) {
         return seqAndDelimited(valueSyntax, ArrayList::new, true /* for now */, '|');
     }
 
-    public static  XmlSyntax> toOptional(XmlSyntax itemSyntax) {
+    public static  XmlMapper> toOptional(XmlMapper itemSyntax) {
         return new OptionalSyntax<>(itemSyntax);
     }
 
@@ -79,21 +79,21 @@ public final class XmlSyntaxUtils {
      *
      * @throws IllegalArgumentException If the item syntax doesn't support string mapping
      */
-    public static > XmlSyntax seqAndDelimited(XmlSyntax itemSyntax,
+    public static > XmlMapper seqAndDelimited(XmlMapper itemSyntax,
                                                                             Supplier emptyCollSupplier,
                                                                             boolean preferOldSyntax,
                                                                             char delimiter) {
         if (!itemSyntax.supportsStringMapping()) {
             throw new IllegalArgumentException("Item syntax does not support string mapping " + itemSyntax);
         }
-        return new SyntaxSet<>(
+        return new MapperSet<>(
             new SeqSyntax<>(itemSyntax, emptyCollSupplier),
             delimitedString(itemSyntax::toString, itemSyntax::fromString, delimiter, emptyCollSupplier),
             preferOldSyntax
         );
     }
 
-    public static > XmlSyntax onlySeq(XmlSyntax itemSyntax,
+    public static > XmlMapper onlySeq(XmlMapper itemSyntax,
                                                                     Supplier emptyCollSupplier) {
         return new SeqSyntax<>(itemSyntax, emptyCollSupplier);
     }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java
index 124f38ec88..088232d033 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java
@@ -16,7 +16,7 @@ public final class XmlUtils {
 
     static final String UNEXPECTED_ELEMENT = "Unexpected element '%s', expecting %s";
     static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '%s' is missing";
-    static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '%s' is missing";
+    static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type %s does not support the attribute syntax.\nUse a nested element, e.g. %s";
 
     private XmlUtils() {
 
@@ -36,7 +36,7 @@ public final class XmlUtils {
                                              .map(Element.class::cast);
     }
 
-    public static  T expectElement(XmlErrorReporter err, Element elt, XmlSyntax syntax) {
+    public static  T expectElement(XmlErrorReporter err, Element elt, XmlMapper syntax) {
 
         if (!syntax.getReadElementNames().contains(elt.getTagName())) {
             err.warn(elt, "Wrong name, expect " + XmlSyntaxUtils.formatPossibilities(syntax.getReadElementNames()));
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index 631546b63a..d1b431c9cc 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -31,7 +31,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.xml.SchemaConstants;
 import net.sourceforge.pmd.properties.PropertyTypeId;
 import net.sourceforge.pmd.properties.xml.XmlErrorReporter;
-import net.sourceforge.pmd.properties.xml.XmlSyntax;
+import net.sourceforge.pmd.properties.xml.XmlMapper;
 import net.sourceforge.pmd.util.ResourceLoader;
 
 
@@ -335,7 +335,7 @@ public class RuleFactory {
 
         builder.desc(SchemaConstants.DESCRIPTION.getAttributeOrThrow(propertyElement, err));
 
-        propertyValueCapture(propertyElement, typeId, factory.getXmlSyntax(), builder, err);
+        propertyValueCapture(propertyElement, typeId, factory.getXmlMapper(), builder, err);
 
         // TODO support constraints like numeric range
 
@@ -345,11 +345,11 @@ public class RuleFactory {
 
     private static  void propertyValueCapture(Element propertyElement,
                                                  String typeId,
-                                                 XmlSyntax baseSyntax,
+                                                 XmlMapper baseSyntax,
                                                  PropertyBuilder builder,
                                                  XmlErrorReporter err) {
         @SuppressWarnings("unchecked")
-        XmlSyntax syntax = (XmlSyntax) baseSyntax;
+        XmlMapper syntax = (XmlMapper) baseSyntax;
         T defaultValue;
 
 

From 74d915b3dbfbf4ab48745f584f70fd06d96e89bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Thu, 19 Sep 2019 01:51:24 +0200
Subject: [PATCH 036/347] Improve error messages

---
 .../net/sourceforge/pmd/RuleSetWriter.java    |   2 +-
 .../pmd/properties/PropertyBuilder.java       |  11 +-
 .../pmd/properties/xml/MapperSet.java         |   6 +-
 .../pmd/properties/xml/OptionalSyntax.java    |   6 +-
 .../pmd/properties/xml/SchemaConstants.java   |  71 ------------
 .../pmd/properties/xml/SeqSyntax.java         |  14 ++-
 .../pmd/properties/xml/ValueSyntax.java       |   5 +-
 .../pmd/properties/xml/XmlErrorMessages.java  |  22 ++++
 .../pmd/properties/xml/XmlErrorReporter.java  |   8 +-
 .../pmd/properties/xml/XmlMapper.java         |   6 +-
 .../pmd/properties/xml/XmlSyntaxUtils.java    |  19 ++--
 .../xml/internal/SchemaConstants.java         | 101 ++++++++++++++++++
 .../xml/{ => internal}/XmlUtils.java          |  12 +--
 .../sourceforge/pmd/rules/RuleFactory.java    |  37 ++++---
 14 files changed, 195 insertions(+), 125 deletions(-)
 delete mode 100755 pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SchemaConstants.java
 create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorMessages.java
 create mode 100755 pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java
 rename pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/{ => internal}/XmlUtils.java (72%)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
index c7f5e99540..5da990c5d9 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
@@ -35,7 +35,7 @@ import net.sourceforge.pmd.lang.LanguageVersion;
 import net.sourceforge.pmd.lang.rule.ImmutableLanguage;
 import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
-import net.sourceforge.pmd.properties.xml.SchemaConstants;
+import net.sourceforge.pmd.properties.xml.internal.SchemaConstants;
 import net.sourceforge.pmd.properties.PropertySource;
 import net.sourceforge.pmd.properties.PropertyTypeId;
 import net.sourceforge.pmd.properties.xml.XmlMapper;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index 2ceba161e9..bf3133dfcb 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -266,15 +266,16 @@ public abstract class PropertyBuilder, T> {
         public GenericPropertyBuilder> toOptional() {
             return new GenericPropertyBuilder>(this.getName(), XmlSyntaxUtils.toOptional(getParser())) {
                 {
-                    if (isDefaultValueSet()) {
-                        this.defaultValue(Optional.ofNullable(BaseSinglePropertyBuilder.this.getDefaultValue()));
+                    BaseSinglePropertyBuilder base = BaseSinglePropertyBuilder.this;
+                    if (base.isDefaultValueSet()) {
+                        this.defaultValue(Optional.ofNullable(base.getDefaultValue()));
                     }
 
-                    if (isDescriptionSet()) {
-                        this.desc(BaseSinglePropertyBuilder.this.getDescription());
+                    if (base.isDescriptionSet()) {
+                        this.desc(base.getDescription());
                     }
 
-                    for (PropertyConstraint validator : BaseSinglePropertyBuilder.this.getConstraints()) {
+                    for (PropertyConstraint validator : base.getConstraints()) {
                         this.require(validator.toOptionalConstraint());
                     }
                 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java
index 5620ee9674..8e1d8d99de 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java
@@ -114,7 +114,7 @@ final class MapperSet extends XmlMapper {
     public T fromXml(Element element, XmlErrorReporter err) {
         XmlMapper syntax = readIndex.get(element.getTagName());
         if (syntax == null) {
-            throw err.error(element, XmlUtils.UNEXPECTED_ELEMENT, element.getTagName(), XmlSyntaxUtils.formatPossibilities(readIndex.keySet()));
+            throw err.error(element, XmlErrorMessages.UNEXPECTED_ELEMENT, element.getTagName(), XmlSyntaxUtils.formatPossibilities(readIndex.keySet()));
         } else {
             return syntax.fromXml(element, err);
         }
@@ -127,7 +127,7 @@ final class MapperSet extends XmlMapper {
 
 
     @Override
-    public List examples() {
-        return readIndex.values().stream().flatMap(it -> it.examples().stream()).collect(Collectors.toList());
+    protected List examples(String curIndent, String baseIndent) {
+        return readIndex.values().stream().flatMap(it -> it.examples(curIndent, baseIndent).stream()).collect(Collectors.toList());
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java
index 6d7c551aca..8b4aa71587 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java
@@ -54,9 +54,9 @@ final class OptionalSyntax extends XmlMapper> {
     }
 
     @Override
-    public List examples() {
-        ArrayList list = new ArrayList<>(itemSyntax.examples());
-        list.add("");
+    protected List examples(String curIndent, String baseIndent) {
+        ArrayList list = new ArrayList<>(itemSyntax.examples(curIndent, baseIndent));
+        list.add(curIndent + "");
         return list;
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SchemaConstants.java
deleted file mode 100755
index 5e976995c7..0000000000
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SchemaConstants.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-
-package net.sourceforge.pmd.properties.xml;
-
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.checkerframework.checker.nullness.qual.Nullable;
-import org.w3c.dom.Element;
-
-
-/**
- * Constants of the ruleset schema.
- */
-public enum SchemaConstants {
-
-    /** The type of the property. */
-    TYPE("type"),
-    /** The name of the property. */
-    NAME("name"),
-    /** The description of the property. */
-    DESCRIPTION("description"),
-    /** The default value. */
-    PROPERTY_VALUE("value"),
-    /** For multi-valued properties, this defines the delimiter of the single values. */
-    DELIMITER("delimiter");
-
-    private final String attributeName;
-
-
-    SchemaConstants(String attributeName) {
-        this.attributeName = attributeName;
-    }
-
-    @NonNull
-    public String getAttributeOrThrow(Element element, XmlErrorReporter err) {
-        String attribute = element.getAttribute(attributeName);
-        if (attribute == null) {
-            throw err.error(element, XmlUtils.MISSING_REQUIRED_ATTRIBUTE, attributeName);
-        }
-
-        return attribute;
-    }
-
-    @Nullable
-    public String getAttributeOpt(Element element) {
-        return element.getAttribute(attributeName);
-    }
-
-    public void setOn(Element element, String value) {
-        element.setAttribute(attributeName, value);
-    }
-
-    /**
-     * Returns the String name of this attribute.
-     *
-     * @return The attribute's name
-     */
-    public String attributeName() {
-        return attributeName;
-    }
-
-
-    @Override
-    public String toString() {
-        return attributeName();
-    }
-
-
-}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java
index f8bd4079ba..b2f9d1fcb1 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java
@@ -12,6 +12,7 @@ import java.util.function.Supplier;
 import org.w3c.dom.Element;
 
 import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper;
+import net.sourceforge.pmd.properties.xml.internal.XmlUtils;
 
 /**
  * Serialize to and from a simple string. Examples:
@@ -54,10 +55,13 @@ final class SeqSyntax> extends StableXmlMapper {
     }
 
     @Override
-    public List examples() {
-        return Collections.singletonList("\n"
-            + "   " + String.join("\n    ", itemSyntax.examples()) + "\n"
-            + "   ..."
-            + "");
+    protected List examples(String curIndent, String baseIndent) {
+        String newIndent = curIndent + baseIndent;
+        return Collections.singletonList(
+            curIndent + "\n"
+                + newIndent + String.join("\n", itemSyntax.examples(newIndent, baseIndent)) + "\n"
+                + newIndent + "..."
+                + curIndent + ""
+        );
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java
index 9e938f91dc..56a320cb9d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java
@@ -75,8 +75,7 @@ final class ValueSyntax extends StableXmlMapper {
     }
 
     @Override
-    public List examples() {
-        return Collections.singletonList("data");
+    protected List examples(String curIndent, String baseIndent) {
+        return Collections.singletonList(curIndent + "data");
     }
-
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorMessages.java
new file mode 100644
index 0000000000..860c66665f
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorMessages.java
@@ -0,0 +1,22 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.properties.xml;
+
+/**
+ * @author Clรฉment Fournier
+ */
+public final class XmlErrorMessages {
+
+    public static final String UNEXPECTED_ELEMENT = "Unexpected element '%s', expecting %s";
+    public static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '%s' is missing";
+    public static final String MISSING_REQUIRED_ELEMENT = "Required child element '%s' is missing";
+    public static final String IGNORED_DUPLICATE_CHILD_ELEMENT = "Expecting a single '%s' child, this will be ignored";
+    public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type %s does not support the attribute syntax.\nUse a nested element, e.g. %s";
+    public static final String DEPRECATED_USE_OF_ATTRIBUTE = "The use of the '%s' attribute is deprecated. Use a nested element, e.g. %s";
+
+    private XmlErrorMessages() {
+        // utility class
+    }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java
index 1dbdf2a894..0b0af2ba0d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java
@@ -4,18 +4,22 @@
 
 package net.sourceforge.pmd.properties.xml;
 
+import java.util.logging.Logger;
+
 import org.w3c.dom.Node;
 
 /**
  * Reports errors in an XML document. Implementations have a way to
  * associate nodes with their location in the document.
  *
- * TODO this is a placeholder for now.
+ * TODO this is a placeholder for now, I need to publish the impl to maven
  */
 public interface XmlErrorReporter {
 
+    Logger LOGGER = Logger.getLogger(XmlErrorReporter.class.getName());
+
     default void warn(Node node, String message, Object... args) {
-        throw new UnsupportedOperationException("TODO");
+        LOGGER.warning(String.format(message, args));
     }
 
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
index 806a44e9c2..1d44803838 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
@@ -68,7 +68,11 @@ public abstract class XmlMapper {
      * Returns some examples for what XML output this strategy produces.
      * For example, {@code 1}.
      */
-    public abstract List examples();
+    public final List examples() {
+        return examples("", "    ");
+    }
+
+    protected abstract List examples(String curIndent, String baseIndent);
 
     @Override
     public String toString() {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
index f365232add..9eeeb9d1ff 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
@@ -17,9 +17,13 @@ import java.util.function.Supplier;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
+
 import net.sourceforge.pmd.annotation.InternalApi;
 
-
+/**
+ * This is internal API and shouldn't be used directly by clients.
+ */
 @InternalApi
 public final class XmlSyntaxUtils {
 
@@ -167,19 +171,14 @@ public final class XmlSyntaxUtils {
     }
 
 
-    static String enquote(String it) {
-        return "'" + it + "'";
-    }
-
-
-    // nullable
-    static String formatPossibilities(Set names) {
+    @Nullable
+    public static String formatPossibilities(Set names) {
         if (names.isEmpty()) {
             return null;
         } else if (names.size() == 1) {
-            return enquote(names.iterator().next());
+            return "'" + names.iterator().next() + "'";
         } else {
-            return "one of " + names.stream().map(XmlSyntaxUtils::enquote).collect(Collectors.joining(", "));
+            return "one of " + names.stream().map(it -> "'" + it + "'").collect(Collectors.joining(", "));
         }
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java
new file mode 100755
index 0000000000..8713a4d4f5
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java
@@ -0,0 +1,101 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.properties.xml.internal;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import net.sourceforge.pmd.properties.xml.XmlErrorMessages;
+import net.sourceforge.pmd.properties.xml.XmlErrorReporter;
+
+
+/**
+ * Constants of the ruleset schema.
+ */
+public enum SchemaConstants {
+
+    /** The type of the property. */
+    TYPE("type"),
+    /** The name of the property. */
+    NAME("name"),
+    /** The description of the property. */
+    DESCRIPTION("description"),
+    /** The default value. */
+    PROPERTY_VALUE("value");
+
+    private final String name;
+
+
+    SchemaConstants(String name) {
+        this.name = name;
+    }
+
+    @NonNull
+    public String getAttributeOrThrow(Element element, XmlErrorReporter err) {
+        String attribute = element.getAttribute(name);
+        if (attribute == null) {
+            throw err.error(element, XmlErrorMessages.MISSING_REQUIRED_ATTRIBUTE, name);
+        }
+
+        return attribute;
+    }
+
+    @Nullable
+    public String getAttributeOpt(Element element) {
+        String attr = element.getAttribute(name);
+        return attr.isEmpty() ? null : attr;
+    }
+
+    @Nullable
+    public Attr getAttributeNode(Element element) {
+        return element.getAttributeNode(name);
+    }
+
+    public List getChildrenIn(Element elt) {
+        return XmlUtils.getElementChildren(elt)
+                       .filter(it -> it.getTagName().equals(name))
+                       .collect(Collectors.toList());
+    }
+
+    public Element getSingleChildIn(Element elt, XmlErrorReporter err) {
+        List children = getChildrenIn(elt);
+        if (children.size() == 1) {
+            return children.get(0);
+        } else if (children.size() == 0) {
+            throw err.error(elt, XmlErrorMessages.MISSING_REQUIRED_ELEMENT, name);
+        } else {
+            for (int i = 1; i < children.size(); i++) {
+                err.warn(children.get(i), XmlErrorMessages.IGNORED_DUPLICATE_CHILD_ELEMENT, name);
+            }
+            return children.get(0);
+        }
+    }
+
+    public void setOn(Element element, String value) {
+        element.setAttribute(name, value);
+    }
+
+    /**
+     * Returns the String name of this attribute.
+     *
+     * @return The attribute's name
+     */
+    public String attributeName() {
+        return name;
+    }
+
+
+    @Override
+    public String toString() {
+        return attributeName();
+    }
+
+
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java
similarity index 72%
rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java
rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java
index 088232d033..83a59fa62d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java
@@ -2,7 +2,7 @@
  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
  */
 
-package net.sourceforge.pmd.properties.xml;
+package net.sourceforge.pmd.properties.xml.internal;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -12,11 +12,11 @@ import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-public final class XmlUtils {
+import net.sourceforge.pmd.properties.xml.XmlErrorReporter;
+import net.sourceforge.pmd.properties.xml.XmlMapper;
+import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils;
 
-    static final String UNEXPECTED_ELEMENT = "Unexpected element '%s', expecting %s";
-    static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '%s' is missing";
-    static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type %s does not support the attribute syntax.\nUse a nested element, e.g. %s";
+public final class XmlUtils {
 
     private XmlUtils() {
 
@@ -30,7 +30,7 @@ public final class XmlUtils {
         return nodes;
     }
 
-    static Stream getElementChildren(Element parent) {
+    public static Stream getElementChildren(Element parent) {
         return toList(parent.getChildNodes()).stream()
                                              .filter(it -> it.getNodeType() == Node.ELEMENT_NODE)
                                              .map(Element.class::cast);
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index d1b431c9cc..e0605f80b1 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -4,7 +4,7 @@
 
 package net.sourceforge.pmd.rules;
 
-import static net.sourceforge.pmd.properties.xml.SchemaConstants.PROPERTY_VALUE;
+import static net.sourceforge.pmd.properties.xml.internal.SchemaConstants.PROPERTY_VALUE;
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Arrays;
@@ -17,6 +17,8 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import org.apache.commons.lang3.StringUtils;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
@@ -28,10 +30,11 @@ import net.sourceforge.pmd.annotation.InternalApi;
 import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.properties.PropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
-import net.sourceforge.pmd.properties.xml.SchemaConstants;
 import net.sourceforge.pmd.properties.PropertyTypeId;
+import net.sourceforge.pmd.properties.xml.XmlErrorMessages;
 import net.sourceforge.pmd.properties.xml.XmlErrorReporter;
 import net.sourceforge.pmd.properties.xml.XmlMapper;
+import net.sourceforge.pmd.properties.xml.internal.SchemaConstants;
 import net.sourceforge.pmd.util.ResourceLoader;
 
 
@@ -353,22 +356,26 @@ public class RuleFactory {
         T defaultValue;
 
 
-        String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElement);
-        if (!StringUtils.isBlank(defaultAttr)) {
-            if (syntax.supportsStringMapping()) {
+        @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElement);
+        if (defaultAttr != null) {
+            Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElement);
+            try {
                 defaultValue = syntax.fromString(defaultAttr);
-            } else {
-                throw err.error(propertyElement.getAttributeNode(PROPERTY_VALUE.attributeName()),
-                                "Type " + typeId + " cannot be parsed from a string, use a nested element, e.g. "
-                                    + String.join("\nor\n", syntax.examples()));
+            } catch (IllegalArgumentException e) {
+                throw err.error(attrNode, e);
+            } catch (UnsupportedOperationException e) {
+                throw err.error(attrNode,
+                                XmlErrorMessages.PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE,
+                                typeId,
+                                String.join("\nor\n", syntax.examples()));
             }
+            err.warn(attrNode,
+                     XmlErrorMessages.DEPRECATED_USE_OF_ATTRIBUTE,
+                     PROPERTY_VALUE.attributeName(),
+                     String.join("\nor\n", syntax.examples()));
         } else {
-            NodeList children = propertyElement.getElementsByTagName(PROPERTY_VALUE.attributeName());
-            if (children.getLength() == 1) {
-                defaultValue = syntax.fromXml((Element) children.item(0), err);
-            } else {
-                throw new IllegalArgumentException("No value defined!");
-            }
+            Element child = PROPERTY_VALUE.getSingleChildIn(propertyElement, err);
+            defaultValue = syntax.fromXml(child, err);
         }
 
         builder.defaultValue(defaultValue);

From aa6da7fd181da8d082293d06bcd7fa3cdd2c05e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Thu, 19 Sep 2019 03:04:09 +0200
Subject: [PATCH 037/347] Fix tests

---
 .../sourceforge/pmd/lang/rule/MockRule.java   |  5 +++-
 .../pmd/properties/PropertyDescriptor.java    | 15 +++++++----
 .../pmd/properties/xml/OptionalSyntax.java    | 25 ++++++++++++++++++-
 3 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java
index b613a336fc..6b22ceae6b 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java
@@ -10,6 +10,7 @@ import net.sourceforge.pmd.RuleContext;
 import net.sourceforge.pmd.RulePriority;
 import net.sourceforge.pmd.lang.LanguageRegistry;
 import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.PropertyFactory;
 
 
@@ -26,10 +27,12 @@ import net.sourceforge.pmd.properties.PropertyFactory;
 @Deprecated
 public class MockRule extends AbstractRule {
 
+    private static final PropertyDescriptor PROPERTY_DESCRIPTOR = PropertyFactory.intProperty("testIntProperty").desc("testIntProperty").require(inRange(1, 100)).defaultValue(1).build();;
+
     public MockRule() {
         super();
         setLanguage(LanguageRegistry.getLanguage("Dummy"));
-        definePropertyDescriptor(PropertyFactory.intProperty("testIntProperty").desc("testIntProperty").require(inRange(1, 100)).defaultValue(1).build());
+        definePropertyDescriptor(PROPERTY_DESCRIPTOR);
     }
 
     public MockRule(String name, String description, String message, String ruleSetName, RulePriority priority) {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
index 3c6a9a6653..f9ff06969e 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
@@ -10,18 +10,23 @@ import net.sourceforge.pmd.properties.xml.XmlMapper;
 
 
 /**
- * Property value descriptor that defines the use & requirements for setting property values for use within PMD and
- * any associated GUIs. While concrete descriptor instances are static and immutable they provide validation,
- * serialization, and default values for any specific datatypes.
+ * Describes a property of a rule or a renderer. Provides validation,
+ * serialization, and default values for a datatype {@code }.
+ * Property descriptors are immutable and can be shared freely.
+ *
+ * 

Usage of this API is described on {@link PropertyFactory}. * *

Upcoming API changes to the properties framework

* see pmd/pmd#1432 * - * @param type of the property's value. This is a list type for multi-valued properties. + * @param Type of the property's value. + * + * @see PropertyFactory + * @see PropertyBuilder * * @author Brian Remedios * @author Clรฉment Fournier - * @version Refactored June 2017 (6.0.0) + * @version 7.0.0 */ public interface PropertyDescriptor { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index 8b4aa71587..b97f772794 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -24,12 +24,35 @@ final class OptionalSyntax extends XmlMapper> { OptionalSyntax(XmlMapper itemSyntax) { this.itemSyntax = itemSyntax; + } + // TODO this scheme for string mapping is lossy, and is there just + // for compatibility with CodeClimateRenderer + + @Override + public boolean supportsStringMapping() { + return itemSyntax.supportsStringMapping(); + } + + @Override + public String toString(Optional value) { + return value.map(itemSyntax::toString).orElse(""); + } + + @Override + public Optional fromString(String attributeData) { + return attributeData.isEmpty() ? Optional.empty() + : Optional.ofNullable(itemSyntax.fromString(attributeData)); } @Override public void toXml(Element container, Optional value) { - + if (value.isPresent()) { + itemSyntax.toXml(container, value.get()); + } else { + Element none = container.getOwnerDocument().createElement(EMPTY_NAME); + container.appendChild(none); + } } @Override From b3824b2a3e9a04df7815091b34bb9015762f018b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Sep 2019 03:16:27 +0200 Subject: [PATCH 038/347] Doc --- .../net/sourceforge/pmd/RuleSetWriter.java | 2 +- .../properties/GenericPropertyDescriptor.java | 2 +- .../pmd/properties/PropertyBuilder.java | 40 +++++---------- .../pmd/properties/PropertyDescriptor.java | 51 ++++++++++--------- 4 files changed, 43 insertions(+), 52 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 5da990c5d9..ae55c07c3c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -307,7 +307,7 @@ public class RuleSetWriter { Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property"); SchemaConstants.NAME.setOn(element, propertyDescriptor.name()); - XmlMapper xmlStrategy = propertyDescriptor.xmlStrategy(); + XmlMapper xmlStrategy = propertyDescriptor.xmlMapper(); Element valueElt = createPropertyValueElement(xmlStrategy.getWriteElementName(value)); xmlStrategy.toXml(valueElt, value); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 9f4b045f6e..5e6eb922e4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -78,7 +78,7 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { } @Override - public XmlMapper xmlStrategy() { + public XmlMapper xmlMapper() { return parser; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index bf3133dfcb..28e2e5ae77 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -259,27 +259,24 @@ public abstract class PropertyBuilder, T> { * are used on the validator property. If the default value was * previously set, it is converted to an optional with {@link Optional#ofNullable(Object)}. * - * @return A new list property builder - * - * @throws IllegalStateException if the default value has already been set + * @return A new property builder for an optional. */ public GenericPropertyBuilder> toOptional() { - return new GenericPropertyBuilder>(this.getName(), XmlSyntaxUtils.toOptional(getParser())) { - { - BaseSinglePropertyBuilder base = BaseSinglePropertyBuilder.this; - if (base.isDefaultValueSet()) { - this.defaultValue(Optional.ofNullable(base.getDefaultValue())); - } + GenericPropertyBuilder> result = new GenericPropertyBuilder<>(this.getName(), XmlSyntaxUtils.toOptional(getParser())); - if (base.isDescriptionSet()) { - this.desc(base.getDescription()); - } + if (isDefaultValueSet()) { + result.defaultValue(Optional.ofNullable(getDefaultValue())); + } - for (PropertyConstraint validator : base.getConstraints()) { - this.require(validator.toOptionalConstraint()); - } - } - }; + if (isDescriptionSet()) { + result.desc(getDescription()); + } + + for (PropertyConstraint validator : getConstraints()) { + result.require(validator.toOptionalConstraint()); + } + + return result; } @@ -479,17 +476,8 @@ public abstract class PropertyBuilder, T> { } - @SuppressWarnings("unchecked") @Override public PropertyDescriptor build() { - // Note: the unchecked cast is safe because pre-7.0.0, - // we only allow building property descriptors for lists. - // C is thus always List, and the cast doesn't fail - - // Post-7.0.0, the multi-value property classes will be removed - // and C will be the actual type parameter of the returned property - // descriptor - XmlMapper syntax = parser.supportsStringMapping() ? XmlSyntaxUtils.seqAndDelimited(parser, emptyCollSupplier, false, multiValueDelimiter) : XmlSyntaxUtils.onlySeq(parser, emptyCollSupplier); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index f9ff06969e..ab64894432 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -6,27 +6,31 @@ package net.sourceforge.pmd.properties; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.properties.xml.XmlMapper; /** - * Describes a property of a rule or a renderer. Provides validation, + * Describes a property of a rule or a renderer. + *

Usage of this API is described on {@link PropertyFactory}. + * + *

A property descriptor provides validation, * serialization, and default values for a datatype {@code }. * Property descriptors are immutable and can be shared freely. - * - *

Usage of this API is described on {@link PropertyFactory}. + * Property descriptors do not override {@link Object#equals(Object)} + * or {@link Object#hashCode()}. Pre 6.0.0 two descriptors were equal + * if they had the same name. * *

Upcoming API changes to the properties framework

* see pmd/pmd#1432 * * @param Type of the property's value. * - * @see PropertyFactory - * @see PropertyBuilder - * * @author Brian Remedios * @author Clรฉment Fournier * @version 7.0.0 + * @see PropertyFactory + * @see PropertyBuilder */ public interface PropertyDescriptor { @@ -58,16 +62,14 @@ public interface PropertyDescriptor { * Returns the strategy used to read and write this property to XML. * May support strings too. */ - XmlMapper xmlStrategy(); + XmlMapper xmlMapper(); /** - * Validation function that returns a diagnostic error message for a sample property value. Returns null if the - * value is acceptable. - * - * @param value The value to check. - * - * @return A diagnostic message. + * TODO + * this needs to go away. Property constraints should be checked + * at the time the ruleset is parsed, to report error messages + * targeted on each node. They could simply decorate the XmlMapper. * * @deprecated PMD 7.0.0 will change the return type to {@code Optional} */ @@ -80,43 +82,44 @@ public interface PropertyDescriptor { /** * Returns the type ID which was used to define this property. Returns * null if this property was defined in Java code and not in XML. + * + * TODO this replaces isDefinedExternally for the RulesetWriter. + * I still don't like it. */ + @InternalApi default @Nullable PropertyTypeId getTypeId() { return null; } /** - * Returns the value represented by this string. + * TODO port tests to use the mapper directly. * - * @param propertyString The string to parse - * - * @return The value represented by the string - * - * @throws IllegalArgumentException if the given string cannot be parsed + * @throws IllegalArgumentException if the given string cannot be parsed * @throws UnsupportedOperationException If operation is not supported * @deprecated PMD 7.0.0 will use a more powerful scheme to represent values than - * simple strings, this method won't be general enough + * simple strings, this method won't be general enough */ @Deprecated default T valueFrom(String propertyString) throws IllegalArgumentException { - return xmlStrategy().fromString(propertyString); + return xmlMapper().fromString(propertyString); } /** - * Formats the object onto a string suitable for storage within the property map. + * TODO port tests to use the mapper directly. * * @param value Object * * @return String * + * @throws UnsupportedOperationException If operation is not supported * @deprecated PMD 7.0.0 will use a more powerful scheme to represent values than - * simple strings, this method won't be general enough + * simple strings, this method won't be general enough */ @Deprecated default String asDelimitedString(T value) { - return xmlStrategy().toString(value); + return xmlMapper().toString(value); } From 5319a2272df61a29cb8e464e5b57e5ba4e0b9ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 26 Dec 2019 14:14:21 +0100 Subject: [PATCH 039/347] Fix rebase --- .../pmd/properties/PropertyBuilder.java | 13 ++--- .../constraints/PropertyConstraint.java | 50 ++++++++++++------- .../pmd/properties/xml/MapperSet.java | 1 + .../pmd/properties/xml/XmlMapper.java | 11 +++- .../xml/internal/SchemaConstants.java | 1 - .../xml/{ => internal}/XmlErrorMessages.java | 2 +- .../sourceforge/pmd/rules/RuleFactory.java | 2 +- 7 files changed, 47 insertions(+), 33 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/{ => internal}/XmlErrorMessages.java (94%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 28e2e5ae77..1f1a319cc9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -129,21 +129,16 @@ public abstract class PropertyBuilder, T> { return (B) this; } - // TODO 7.0.0 document the following: - // - // *

Constraints should be independent from each other, and should - // * perform no side effects. PMD doesn't specify how many times a - // * constraint predicate will be executed, or in what order. - // - // This is superfluous right now bc users may not create their own constraints - - /** * Add a constraint on the values that this property may take. * The validity of values will be checked when parsing the XML, * and invalid values will be reported. A rule will never be run * if some of its properties violate some constraints. * + *

Constraints should be independent from each other, and should + * perform no side effects. PMD doesn't specify how many times a + * constraint predicate will be executed, or in what order. + * * @param constraint The constraint * * @return The same builder diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java index 679e4325c5..6baee8e7ee 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java @@ -41,7 +41,7 @@ public interface PropertyConstraint { * @return An optional diagnostic message */ @Nullable - String validate(T value); // Future make default + String validate(T value); /** @@ -76,6 +76,36 @@ public interface PropertyConstraint { } + /** + * Returns a constraint that validates a collection of Ts + * by checking each component conforms to this validator. + * + * @return A collection validator + */ + default PropertyConstraint> toCollectionConstraint() { + final PropertyConstraint thisValidator = PropertyConstraint.this; + return new PropertyConstraint>() { + @Override + public @Nullable String validate(Iterable value) { + List errors = new ArrayList<>(); + for (T t : value) { + String compValidation = thisValidator.validate(t); + if (compValidation != null) { + errors.add(compValidation); + } + } + return errors.isEmpty() ? null + : String.join(", ", errors); + } + + @Override + public String getConstraintDescription() { + return "Components " + StringUtils.uncapitalize(thisValidator.getConstraintDescription()); + } + }; + } + + /** * Builds a new validator from a predicate, and description. * @@ -99,28 +129,10 @@ public interface PropertyConstraint { return pred.test(value) ? null : "Constraint violated on property value '" + value + "' (" + StringUtils.uncapitalize(constraintDescription) + ")"; } - @Override public String getConstraintDescription() { return StringUtils.capitalize(constraintDescription); } - - - @Override - public PropertyConstraint> toCollectionConstraint() { - final PropertyConstraint thisValidator = this; - return fromPredicate( - us -> { - for (U u : us) { - if (!pred.test(u)) { - return false; - } - } - return true; - }, - "Components " + StringUtils.uncapitalize(thisValidator.getConstraintDescription()) - ); - } }; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 8e1d8d99de..8e6a641944 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -15,6 +15,7 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.util.CollectionUtil; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index 1d44803838..cf1a42df38 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -37,9 +37,10 @@ public abstract class XmlMapper { } /** - * Read the value from a string. + * Read the value from a string, if it is supported. + * * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()} - * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) + * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) */ public T fromString(String attributeData) { throw new UnsupportedOperationException("Check #supportsStringMapping()"); @@ -72,6 +73,12 @@ public abstract class XmlMapper { return examples("", " "); } + /** + * Builds examples (impl). + * + * @param curIndent Indentation of the current level + * @param baseIndent Base indentation string, adding one indent level concats this with the [curIndent] + */ protected abstract List examples(String curIndent, String baseIndent); @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java index 8713a4d4f5..a45b759697 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java @@ -12,7 +12,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; import org.w3c.dom.Element; -import net.sourceforge.pmd.properties.xml.XmlErrorMessages; import net.sourceforge.pmd.properties.xml.XmlErrorReporter; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java similarity index 94% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorMessages.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java index 860c66665f..e5236dca30 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties.xml.internal; /** * @author Clรฉment Fournier diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index e0605f80b1..8c10005894 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -31,7 +31,7 @@ import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; -import net.sourceforge.pmd.properties.xml.XmlErrorMessages; +import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.properties.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.internal.SchemaConstants; From cd41cbeaf1fcb4229e47f54d97c4a67b9aca2676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 19 Mar 2020 01:20:10 +0100 Subject: [PATCH 040/347] Fix rebase --- .../sourceforge/pmd/ViolationSuppressor.java | 16 +++--- .../pmd/properties/PropertyFactory.java | 4 +- .../constraints/PropertyConstraint.java | 16 +++--- .../properties/PropertyDescriptorTest.java | 6 +-- .../constraints/NumericConstraintsTest.java | 50 +++++++++---------- 5 files changed, 43 insertions(+), 49 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java b/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java index a88f0568f2..d5ecf97192 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd; import java.util.Map; +import java.util.Optional; +import java.util.Optional; import java.util.regex.Pattern; import org.checkerframework.checker.nullness.qual.NonNull; @@ -37,10 +39,10 @@ public interface ViolationSuppressor { @Override public @Nullable SuppressedViolation suppressOrNull(RuleViolation rv, @NonNull Node node) { - String regex = rv.getRule().getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR); // Regex - if (regex != null && rv.getDescription() != null) { - if (Pattern.matches(regex, rv.getDescription())) { - return new SuppressedViolation(rv, this, regex); + Optional regex = rv.getRule().getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR); // Regex + if (regex.isPresent() && rv.getDescription() != null) { + if (Pattern.matches(regex.get(), rv.getDescription())) { + return new SuppressedViolation(rv, this, regex.get()); } } return null; @@ -63,12 +65,12 @@ public interface ViolationSuppressor { // this needs to be checked to be a valid xpath expression in the ruleset, // not at the time it is evaluated, and also parsed by the XPath parser only once Rule rule = rv.getRule(); - String xpath = rule.getProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR); - if (xpath == null) { + Optional xpath = rule.getProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR); + if (!xpath.isPresent()) { return null; } SaxonXPathRuleQuery rq = new SaxonXPathRuleQuery( - xpath, + xpath.get(), XPathVersion.DEFAULT, rule.getPropertiesByPropertyDescriptor(), // todo version should be carried around by the node diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index a526f774e5..238d20cc2d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -11,8 +11,6 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import org.apache.commons.lang3.EnumUtils; - import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; @@ -319,7 +317,7 @@ public final class PropertyFactory { } public static > GenericPropertyBuilder enumProperty(String name, Class enumClass) { - return new GenericPropertyBuilder<>(name, enumerationParser(EnumUtils.getEnumMap(enumClass))); + return enumProperty(name, enumClass, Enum::name); } public static > GenericPropertyBuilder enumProperty(String name, Class enumClass, Function labelMaker) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java index 6baee8e7ee..e191d0d6e4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java @@ -13,32 +13,28 @@ import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.properties.PropertyBuilder; /** * Validates the value of a property. * - *

This interface will change a lot with PMD 7.0.0, - * because of the switch to Java 8. Please use - * only the ready-made validators in {@link NumericConstraints} - * for now. - * * @param Type of value to handle * + * @see PropertyBuilder#require(PropertyConstraint) + * * @author Clรฉment Fournier * @since 6.10.0 */ -@Experimental public interface PropertyConstraint { /** * Returns a diagnostic message if the value - * has a problem. Otherwise returns an empty - * optional. + * has a problem. Otherwise returns null. * * @param value The value to validate * - * @return An optional diagnostic message + * @return A diagnostic message */ @Nullable String validate(T value); @@ -126,7 +122,7 @@ public interface PropertyConstraint { // TODO message could be better, eg include name of the property @Override public String validate(U value) { - return pred.test(value) ? null : "Constraint violated on property value '" + value + "' (" + StringUtils.uncapitalize(constraintDescription) + ")"; + return pred.test(value) ? null : value + " " + StringUtils.uncapitalize(constraintDescription); } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index 4e87d65f58..b9b20f6954 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -96,8 +96,7 @@ public class PropertyDescriptorTest { PropertyConstraint constraint = inRange(1, 10); thrown.expect(IllegalArgumentException.class); - thrown.expectMessage(allOf(containsIgnoreCase("Constraint violat"/*-ed or -ion*/), - containsIgnoreCase(constraint.getConstraintDescription()))); + thrown.expectMessage(containsIgnoreCase(constraint.getConstraintDescription())); PropertyFactory.intProperty("fooProp") .desc("hello") @@ -112,8 +111,7 @@ public class PropertyDescriptorTest { PropertyConstraint constraint = inRange(1d, 10d); thrown.expect(IllegalArgumentException.class); - thrown.expectMessage(allOf(containsIgnoreCase("Constraint violat"/*-ed or -ion*/), - containsIgnoreCase(constraint.getConstraintDescription()))); + thrown.expectMessage(containsIgnoreCase(constraint.getConstraintDescription())); PropertyFactory.doubleListProperty("fooProp") .desc("hello") diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java index a891a2c91a..7eb34d6520 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java @@ -12,40 +12,40 @@ public class NumericConstraintsTest { @Test public void testInRangeInteger() { PropertyConstraint constraint = NumericConstraints.inRange(1, 10); - Assert.assertTrue(constraint.test(1)); - Assert.assertTrue(constraint.test(5)); - Assert.assertTrue(constraint.test(10)); - Assert.assertFalse(constraint.test(0)); - Assert.assertFalse(constraint.test(-1)); - Assert.assertFalse(constraint.test(11)); - Assert.assertFalse(constraint.test(100)); + Assert.assertNull(constraint.validate(1)); + Assert.assertNull(constraint.validate(5)); + Assert.assertNull(constraint.validate(10)); + Assert.assertNotNull(constraint.validate(0)); + Assert.assertEquals("-1 should be between 1 and 10", constraint.validate(-1)); + Assert.assertNotNull(constraint.validate(11)); + Assert.assertNotNull(constraint.validate(100)); } @Test public void testInRangeDouble() { PropertyConstraint constraint = NumericConstraints.inRange(1.0, 10.0); - Assert.assertTrue(constraint.test(1.0)); - Assert.assertTrue(constraint.test(5.5)); - Assert.assertTrue(constraint.test(10.0)); - Assert.assertFalse(constraint.test(0.0)); - Assert.assertFalse(constraint.test(-1.0)); - Assert.assertFalse(constraint.test(11.1)); - Assert.assertFalse(constraint.test(100.0)); + Assert.assertNull(constraint.validate(1.0)); + Assert.assertNull(constraint.validate(5.5)); + Assert.assertNull(constraint.validate(10.0)); + Assert.assertNotNull(constraint.validate(0.0)); + Assert.assertNotNull(constraint.validate(-1.0)); + Assert.assertNotNull(constraint.validate(11.1)); + Assert.assertNotNull(constraint.validate(100.0)); } @Test public void testPositive() { PropertyConstraint constraint = NumericConstraints.positive(); - Assert.assertTrue(constraint.test(1)); - Assert.assertTrue(constraint.test(1.5f)); - Assert.assertTrue(constraint.test(1.5d)); - Assert.assertTrue(constraint.test(100)); - Assert.assertFalse(constraint.test(0)); - Assert.assertFalse(constraint.test(0.1f)); - Assert.assertFalse(constraint.test(0.9d)); - Assert.assertFalse(constraint.test(-1)); - Assert.assertFalse(constraint.test(-100)); - Assert.assertFalse(constraint.test(-0.1f)); - Assert.assertFalse(constraint.test(-0.1d)); + Assert.assertNull(constraint.validate(1)); + Assert.assertNull(constraint.validate(1.5f)); + Assert.assertNull(constraint.validate(1.5d)); + Assert.assertNull(constraint.validate(100)); + Assert.assertNotNull(constraint.validate(0)); + Assert.assertEquals("0.1 should be positive", constraint.validate(0.1f)); + Assert.assertNotNull(constraint.validate(0.9d)); + Assert.assertNotNull(constraint.validate(-1)); + Assert.assertNotNull(constraint.validate(-100)); + Assert.assertNotNull(constraint.validate(-0.1f)); + Assert.assertNotNull(constraint.validate(-0.1d)); } } From 3afd852203838447eb2cf9c26b7f0ca58f03b466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 9 Apr 2020 17:58:12 +0200 Subject: [PATCH 041/347] Remove some changes --- .../main/java/net/sourceforge/pmd/lang/rule/MockRule.java | 5 +---- .../net/sourceforge/pmd/renderers/AbstractRendererTest.java | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java index 6b22ceae6b..b613a336fc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java @@ -10,7 +10,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -27,12 +26,10 @@ import net.sourceforge.pmd.properties.PropertyFactory; @Deprecated public class MockRule extends AbstractRule { - private static final PropertyDescriptor PROPERTY_DESCRIPTOR = PropertyFactory.intProperty("testIntProperty").desc("testIntProperty").require(inRange(1, 100)).defaultValue(1).build();; - public MockRule() { super(); setLanguage(LanguageRegistry.getLanguage("Dummy")); - definePropertyDescriptor(PROPERTY_DESCRIPTOR); + definePropertyDescriptor(PropertyFactory.intProperty("testIntProperty").desc("testIntProperty").require(inRange(1, 100)).defaultValue(1).build()); } public MockRule(String name, String description, String message, String ruleSetName, RulePriority priority) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java index 8520293771..cc959120ea 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java @@ -94,7 +94,8 @@ public abstract class AbstractRendererTest { ctx.setSourceCodeFile(new File(getSourceCodeFilename())); Report report = new Report(); RuleWithProperties theRule = new RuleWithProperties(); - theRule.setProperty(RuleWithProperties.STRING_PROPERTY_DESCRIPTOR, "the string value\nsecond line with \"quotes\""); + theRule.setProperty(RuleWithProperties.STRING_PROPERTY_DESCRIPTOR, + "the string value\nsecond line with \"quotes\""); report.addRuleViolation(new ParametricRuleViolation(theRule, ctx, node, "blah")); String rendered = ReportTest.render(getRenderer(), report); assertEquals(filter(getExpectedWithProperties()), filter(rendered)); From 0e7865b5472fc6ac25deba157709cee4ea417343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 9 Apr 2020 19:43:49 +0200 Subject: [PATCH 042/347] Generalize collection buider to an arbitrary collector --- .../pmd/internal/util/IteratorUtil.java | 5 + .../pmd/properties/PropertyBuilder.java | 100 +++++++++++++----- .../pmd/properties/xml/MapperSet.java | 13 ++- .../pmd/properties/xml/SeqSyntax.java | 25 ++--- .../pmd/properties/xml/ValueSyntax.java | 27 +++-- .../pmd/properties/xml/XmlErrorReporter.java | 8 +- .../pmd/properties/xml/XmlMapper.java | 13 ++- .../pmd/properties/xml/XmlSyntaxUtils.java | 69 ++++++------ .../AvoidDuplicateLiteralsRule.java | 12 ++- 9 files changed, 167 insertions(+), 105 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java index 55385ac986..638a98c1d0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java @@ -14,6 +14,7 @@ import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; +import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; import java.util.function.Function; @@ -438,6 +439,10 @@ public final class IteratorUtil { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iter, 0), false); } + public static Stream stream(Iterable iter) { + return StreamSupport.stream(iter.spliterator(), false); + } + public abstract static class AbstractIterator implements Iterator { private State state = State.NOT_READY; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 1f1a319cc9..b38980bf3c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -4,20 +4,21 @@ package net.sourceforge.pmd.properties; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; + import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; import java.util.regex.Pattern; +import java.util.stream.Collector; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; +import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; @@ -228,13 +229,64 @@ public abstract class PropertyBuilder, T> { * @throws IllegalStateException if the default value has already been set */ /* package private */ GenericCollectionPropertyBuilder> toList() { + return to(Collectors.toList()); + } + + /** + * Returns a new builder that can be used to build a property + * with value type {@code }. The validators already added are + * converted to collection validators. The default value cannot + * have previously been set. The returned builder will support + * conversion to and from a delimited string if this property + * supports direct mapping to/from a string without delimiters. + * Otherwise it will only support the {@code } syntax. + * + *

Example usage: + *

{@code
+         *
+         * // this can be set both with
+         * // a,b,c
+         * // and
+         * // 
+         * //  a
+         * //  b
+         * // 
+         * PropertyDescriptor> whitelistSet =
+         *      PropertyFactory.stringProperty("whitelist")
+         *                     .desc(...)
+         *                     .to(Collectors.toSet())
+         *                     .emptyDefaultValue()
+         *                     .build();
+         *
+         * // this can be set only with the  syntax,
+         * // otherwise the delimiter would be ambiguous:
+         * // 
+         * //   
+         * //     a
+         * //   
+         * //   
+         * // 
+         * PropertyDescriptor>> whitelistSet =
+         *      PropertyFactory.stringProperty("whitelist")
+         *                     .desc(...)
+         *                     .to(Collectors.toSet())
+         *                     .to(Collectors.toList())
+         *                     .emptyDefaultValue()
+         *                     .build();
+         *
+         * }
+ * + * @return A new list property builder + * + * @throws IllegalStateException if the default value has already been set + */ + public > GenericCollectionPropertyBuilder to(Collector collector) { if (isDefaultValueSet()) { throw new IllegalStateException("The default value is already set!"); } - GenericCollectionPropertyBuilder> result = - new GenericCollectionPropertyBuilder<>(getName(), getParser(), ArrayList::new); + GenericCollectionPropertyBuilder result = new GenericCollectionPropertyBuilder<>(getName(), getParser(), collector); if (isDescriptionSet()) { result.desc(getDescription()); @@ -245,7 +297,6 @@ public abstract class PropertyBuilder, T> { } return result; - } /** @@ -357,7 +408,7 @@ public abstract class PropertyBuilder, T> { /** * Generic builder for a collection-valued property. - * This class adds methods related to {@link #defaultValue(Collection)} + * This class adds methods related to {@link #defaultValue(Iterable)} * to make its use more flexible. See e.g. {@link #defaultValues(Object, Object[])}. * *

Note: this is designed to support arbitrary collections. @@ -370,10 +421,10 @@ public abstract class PropertyBuilder, T> { * @author Clรฉment Fournier * @since 6.10.0 */ - public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { + public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { private final XmlMapper parser; - private final Supplier emptyCollSupplier; + private final Collector collector; private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; @@ -382,29 +433,27 @@ public abstract class PropertyBuilder, T> { */ GenericCollectionPropertyBuilder(String name, XmlMapper parser, - Supplier emptyCollSupplier) { + Collector collector) { super(name); this.parser = parser; - this.emptyCollSupplier = emptyCollSupplier; + this.collector = collector; } - private C getDefaultValue(Collection list) { - C coll = emptyCollSupplier.get(); - coll.addAll(list); - return coll; + private C getDefaultValue(Iterable list) { + return IteratorUtil.stream(list).collect(collector); } /** - * Specify a default value. + * Specify a default value. This will be converted to type + * {@code } with the supplied collector. * * @param val List of values * * @return The same builder */ - @SuppressWarnings("unchecked") - public GenericCollectionPropertyBuilder defaultValue(Collection val) { + public GenericCollectionPropertyBuilder defaultValue(Iterable val) { super.defaultValue(getDefaultValue(val)); return this; } @@ -421,10 +470,7 @@ public abstract class PropertyBuilder, T> { */ @SuppressWarnings("unchecked") public GenericCollectionPropertyBuilder defaultValues(V head, V... tail) { - List tmp = new ArrayList<>(tail.length + 1); - tmp.add(head); - tmp.addAll(Arrays.asList(tail)); - return super.defaultValue(getDefaultValue(tmp)); + return this.defaultValue(listOf(head, tail)); } @@ -434,7 +480,7 @@ public abstract class PropertyBuilder, T> { * @return The same builder */ public GenericCollectionPropertyBuilder emptyDefaultValue() { - return super.defaultValue(getDefaultValue(Collections.emptyList())); + return this.defaultValue(Collections.emptyList()); } @@ -473,9 +519,9 @@ public abstract class PropertyBuilder, T> { @Override public PropertyDescriptor build() { - XmlMapper syntax = parser.supportsStringMapping() - ? XmlSyntaxUtils.seqAndDelimited(parser, emptyCollSupplier, false, multiValueDelimiter) - : XmlSyntaxUtils.onlySeq(parser, emptyCollSupplier); + XmlMapper syntax = parser.supportsStringMapping() && !parser.isStringParserDelimited() + ? XmlSyntaxUtils.seqAndDelimited(parser, collector, false, multiValueDelimiter) + : XmlSyntaxUtils.onlySeq(parser, collector); return new GenericPropertyDescriptor<>( getName(), diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 8e6a641944..5f6f58f66f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.properties.xml; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -102,13 +101,19 @@ final class MapperSet extends XmlMapper { throw new UnsupportedOperationException(); } - public Set> supportedReadStrategies() { - return new LinkedHashSet<>(readIndex.values()); + private Collection> supportedReadStrategies() { + return readIndex.values(); } @Override public boolean supportsStringMapping() { - return supportedReadStrategies().stream().anyMatch(XmlMapper::supportsStringMapping); + return supportedReadStrategies().stream().anyMatch(XmlMapper::supportsStringMapping) + && forWrite.supportsStringMapping(); + } + + @Override + public boolean isStringParserDelimited() { + return supportedReadStrategies().stream().anyMatch(XmlMapper::isStringParserDelimited); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index b2f9d1fcb1..b5c16d43cf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.properties.xml; -import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; +import java.util.Objects; +import java.util.stream.Collector; import org.w3c.dom.Element; @@ -21,15 +21,15 @@ import net.sourceforge.pmd.properties.xml.internal.XmlUtils; * 1 * }

*/ -final class SeqSyntax> extends StableXmlMapper { +final class SeqSyntax> extends StableXmlMapper { private final XmlMapper itemSyntax; - private final Supplier emptyCollSupplier; + private final Collector collector; - SeqSyntax(XmlMapper itemSyntax, Supplier emptyCollSupplier) { + SeqSyntax(XmlMapper itemSyntax, Collector collector) { super("seq"); this.itemSyntax = itemSyntax; - this.emptyCollSupplier = emptyCollSupplier; + this.collector = collector; } @Override @@ -43,15 +43,10 @@ final class SeqSyntax> extends StableXmlMapper { @Override public C fromXml(Element element, XmlErrorReporter err) { - C result = emptyCollSupplier.get(); - - XmlUtils.getElementChildren(element).forEach(child -> { - T item = XmlUtils.expectElement(err, child, itemSyntax); - if (item != null) { - result.add(item); - } - }); - return result; + return XmlUtils.getElementChildren(element) + .map(child -> XmlUtils.expectElement(err, child, itemSyntax)) + .filter(Objects::nonNull) + .collect(collector); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 56a320cb9d..65cf8d13e8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -30,18 +30,15 @@ final class ValueSyntax extends StableXmlMapper { private static final String VALUE_NAME = "value"; private final Function toString; private final Function fromString; + private final boolean delimited; - ValueSyntax(Function toString, - Function fromString) { + private ValueSyntax(Function toString, + Function fromString, + boolean delimited) { super(VALUE_NAME); this.toString = toString; this.fromString = fromString; - } - - ValueSyntax(Function fromString) { - super(VALUE_NAME); - this.toString = Objects::toString; - this.fromString = fromString; + this.delimited = delimited; } @Override @@ -49,6 +46,11 @@ final class ValueSyntax extends StableXmlMapper { return true; } + @Override + public boolean isStringParserDelimited() { + return delimited; + } + @Override public T fromString(String attributeData) { return fromString.apply(attributeData); @@ -78,4 +80,13 @@ final class ValueSyntax extends StableXmlMapper { protected List examples(String curIndent, String baseIndent) { return Collections.singletonList(curIndent + "data"); } + + static ValueSyntax createNonDelimited(Function fromString) { + return new ValueSyntax<>(Objects::toString, fromString, false); + } + + static ValueSyntax createDelimited(Function toString, + Function fromString) { + return new ValueSyntax<>(toString, fromString, true); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java index 0b0af2ba0d..8a42215efe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.properties.xml; import java.util.logging.Logger; -import org.w3c.dom.Node; - /** * Reports errors in an XML document. Implementations have a way to * associate nodes with their location in the document. @@ -18,17 +16,17 @@ public interface XmlErrorReporter { Logger LOGGER = Logger.getLogger(XmlErrorReporter.class.getName()); - default void warn(Node node, String message, Object... args) { + default void warn(org.w3c.dom.Node node, String message, Object... args) { LOGGER.warning(String.format(message, args)); } - default RuntimeException error(Node node, String message, Object... args) { + default RuntimeException error(org.w3c.dom.Node node, String message, Object... args) { return new IllegalArgumentException(String.format(message, args)); } - default RuntimeException error(Node node, Throwable ex) { + default RuntimeException error(org.w3c.dom.Node node, Throwable ex) { return new IllegalArgumentException(ex); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index cf1a42df38..66bdc8416a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -10,11 +10,14 @@ import java.util.Set; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.PropertyFactory; + /** - * Strategy to serialize a value to and from XML. - * - * @author Clรฉment Fournier + * Strategy to serialize a value to and from XML. Some strategies support + * mapping to and from a string, without XML structure. They can be identified + * with {@link #supportsStringMapping()}. All the standard properties + * provided by {@link PropertyFactory} do. */ public abstract class XmlMapper { @@ -36,6 +39,10 @@ public abstract class XmlMapper { return false; } + public boolean isStringParserDelimited() { + return false; + } + /** * Read the value from a string, if it is supported. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 9eeeb9d1ff..5f88e64fc8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -6,20 +6,20 @@ package net.sourceforge.pmd.properties.xml; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.regex.Pattern; +import java.util.stream.Collector; import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.internal.util.IteratorUtil; /** * This is internal API and shouldn't be used directly by clients. @@ -27,19 +27,19 @@ import net.sourceforge.pmd.annotation.InternalApi; @InternalApi public final class XmlSyntaxUtils { - public static final ValueSyntax STRING = new ValueSyntax<>(Function.identity()); - public static final ValueSyntax CHARACTER = new ValueSyntax<>(value -> { + public static final ValueSyntax STRING = ValueSyntax.createNonDelimited(Function.identity()); + public static final ValueSyntax CHARACTER = ValueSyntax.createNonDelimited(value -> { if (value == null || value.length() != 1) { throw new IllegalArgumentException("missing/ambiguous character value for string \"" + value + "\""); } return value.charAt(0); }); - public static final ValueSyntax REGEX = new ValueSyntax<>(Pattern::compile); - public static final ValueSyntax INTEGER = new ValueSyntax<>(Integer::valueOf); - public static final ValueSyntax LONG = new ValueSyntax<>(Long::valueOf); - public static final ValueSyntax BOOLEAN = new ValueSyntax<>(Boolean::valueOf); - public static final ValueSyntax DOUBLE = new ValueSyntax<>(Double::valueOf); + public static final ValueSyntax REGEX = ValueSyntax.createNonDelimited(Pattern::compile); + public static final ValueSyntax INTEGER = ValueSyntax.createNonDelimited(Integer::valueOf); + public static final ValueSyntax LONG = ValueSyntax.createNonDelimited(Long::valueOf); + public static final ValueSyntax BOOLEAN = ValueSyntax.createNonDelimited(Boolean::valueOf); + public static final ValueSyntax DOUBLE = ValueSyntax.createNonDelimited(Double::valueOf); public static final XmlMapper> INTEGER_LIST = numberList(INTEGER); @@ -49,20 +49,17 @@ public final class XmlSyntaxUtils { public static final XmlMapper> CHAR_LIST = otherList(CHARACTER); public static final XmlMapper> STRING_LIST = otherList(STRING); - public static final XmlMapper> REGEX_LIST = new SeqSyntax<>(REGEX, ArrayList::new); - - private XmlSyntaxUtils() { } private static XmlMapper> numberList(ValueSyntax valueSyntax) { - return seqAndDelimited(valueSyntax, ArrayList::new, true, ','); + return seqAndDelimited(valueSyntax, Collectors.toList(), true, ','); } private static XmlMapper> otherList(ValueSyntax valueSyntax) { - return seqAndDelimited(valueSyntax, ArrayList::new, true /* for now */, '|'); + return seqAndDelimited(valueSyntax, Collectors.toList(), true /* for now */, '|'); } public static XmlMapper> toOptional(XmlMapper itemSyntax) { @@ -73,50 +70,46 @@ public final class XmlSyntaxUtils { * Builds an XML syntax that understands a {@code } syntax and * a delimited {@code } syntax. * - * @param itemSyntax Serializer for the items, must support string mapping - * @param emptyCollSupplier Supplier for the collection - * @param preferOldSyntax If true, the property will be written with {@code }, - * otherwise with {@code }. - * @param delimiter Delimiter for the {@code } syntax - * @param Type of items - * @param Type of collection to handle + * @param itemSyntax Serializer for the items, must support string mapping + * @param collector Collector to create the collection from strings + * @param preferOldSyntax If true, the property will be written with {@code }, + * otherwise with {@code }. + * @param delimiter Delimiter for the {@code } syntax + * @param Type of items + * @param Type of collection to handle * * @throws IllegalArgumentException If the item syntax doesn't support string mapping */ - public static > XmlMapper seqAndDelimited(XmlMapper itemSyntax, - Supplier emptyCollSupplier, + public static > XmlMapper seqAndDelimited(XmlMapper itemSyntax, + Collector collector, boolean preferOldSyntax, char delimiter) { if (!itemSyntax.supportsStringMapping()) { throw new IllegalArgumentException("Item syntax does not support string mapping " + itemSyntax); } return new MapperSet<>( - new SeqSyntax<>(itemSyntax, emptyCollSupplier), - delimitedString(itemSyntax::toString, itemSyntax::fromString, delimiter, emptyCollSupplier), + new SeqSyntax<>(itemSyntax, collector), + delimitedString(itemSyntax::toString, itemSyntax::fromString, delimiter, collector), preferOldSyntax ); } - public static > XmlMapper onlySeq(XmlMapper itemSyntax, - Supplier emptyCollSupplier) { - return new SeqSyntax<>(itemSyntax, emptyCollSupplier); + public static > XmlMapper onlySeq(XmlMapper itemSyntax, + Collector collector) { + return new SeqSyntax<>(itemSyntax, collector); } - private static > ValueSyntax delimitedString( + private static > ValueSyntax delimitedString( Function toString, Function fromString, char delimiter, - Supplier emptyCollSupplier + Collector collector ) { String delim = "" + delimiter; - return new ValueSyntax<>( - coll -> coll.stream().map(toString).collect(Collectors.joining(delim)), - string -> { - C coll = emptyCollSupplier.get(); - coll.addAll(parseListWithEscapes(string, delimiter, fromString)); - return coll; - } + return ValueSyntax.createDelimited( + coll -> IteratorUtil.stream(coll.iterator()).map(toString).collect(Collectors.joining(delim)), + string -> parseListWithEscapes(string, delimiter, fromString).stream().collect(collector) ); } @@ -188,7 +181,7 @@ public final class XmlSyntaxUtils { throw new IllegalArgumentException("Map may not contain entries with null values"); } - return new ValueSyntax<>(value -> { + return ValueSyntax.createNonDelimited(value -> { if (!mappings.containsKey(value)) { throw new IllegalArgumentException("Value was not in the set " + mappings.keySet()); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java index 3b4c824378..04d5bce06a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; import static net.sourceforge.pmd.properties.PropertyFactory.intProperty; -import static net.sourceforge.pmd.properties.PropertyFactory.stringListProperty; +import static net.sourceforge.pmd.properties.PropertyFactory.stringProperty; import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; import java.util.ArrayList; @@ -16,6 +16,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; @@ -37,12 +38,13 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule { booleanProperty("skipAnnotations") .desc("Skip literals within annotations").defaultValue(false).build(); - private static final PropertyDescriptor> EXCEPTION_LIST_DESCRIPTOR - = stringListProperty("exceptionList") + private static final PropertyDescriptor> EXCEPTION_LIST_DESCRIPTOR + = stringProperty("exceptionList") .desc("List of literals to ignore. " + "A literal is ignored if its image can be found in this list. " + "Components of this list should not be surrounded by double quotes.") - .defaultValue(Collections.emptyList()) + .to(Collectors.toSet()) + .defaultValue(Collections.emptySet()) .delim(',') .build(); @@ -62,7 +64,7 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule { literals.clear(); if (getProperty(EXCEPTION_LIST_DESCRIPTOR) != null) { - exceptions = new HashSet<>(getProperty(EXCEPTION_LIST_DESCRIPTOR)); + exceptions = getProperty(EXCEPTION_LIST_DESCRIPTOR); } minLength = 2 + getProperty(MINIMUM_LENGTH_DESCRIPTOR); From 72756c67d72fe8a83bc0ae382fef37c47a69fc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 9 Apr 2020 20:24:13 +0200 Subject: [PATCH 043/347] Improve usability of propertyTypeId --- .../pmd/properties/PropertyBuilder.java | 2 +- .../pmd/properties/PropertyTypeId.java | 28 ++++++++++---- .../pmd/properties/xml/XmlErrorReporter.java | 5 ++- .../pmd/properties/xml/XmlSyntaxUtils.java | 8 ++-- .../xml/internal/XmlErrorMessages.java | 15 +++----- .../sourceforge/pmd/rules/RuleFactory.java | 37 +++++++++---------- 6 files changed, 53 insertions(+), 42 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index b38980bf3c..dbbbc4ea7d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -55,7 +55,7 @@ public abstract class PropertyBuilder, T> { private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z][\\w-]*"); private final Set> validators = new LinkedHashSet<>(); - private String name; + private final String name; private String description; private T defaultValue; protected PropertyTypeId typeId; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 167c85e66f..b39ac6a1ca 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -71,8 +71,27 @@ public enum PropertyTypeId { this.factory = factory; } - public XmlMapper getXmlMapper() { - return xmlMapper; + // this is provided so that the mapper and the factory may be related + // through the same type parameter, so that capture works well + public interface BuilderAndMapper { + + XmlMapper getXmlMapper(); + + PropertyBuilder newBuilder(String name); + } + + public BuilderAndMapper getBuilderUtils() { + return new BuilderAndMapper() { + @Override + public XmlMapper getXmlMapper() { + return xmlMapper; + } + + @Override + public PropertyBuilder newBuilder(String name) { + return factory.apply(name); + } + }; } @@ -86,11 +105,6 @@ public enum PropertyTypeId { } - public PropertyBuilder newBuilder(String name) { - return factory.apply(name); - } - - /** * Returns the full mappings from type ids to enum constants. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java index 8a42215efe..821aa1a4c1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.properties.xml; +import java.text.MessageFormat; import java.util.logging.Logger; /** @@ -17,12 +18,12 @@ public interface XmlErrorReporter { Logger LOGGER = Logger.getLogger(XmlErrorReporter.class.getName()); default void warn(org.w3c.dom.Node node, String message, Object... args) { - LOGGER.warning(String.format(message, args)); + LOGGER.warning(MessageFormat.format(message, args)); } default RuntimeException error(org.w3c.dom.Node node, String message, Object... args) { - return new IllegalArgumentException(String.format(message, args)); + return new IllegalArgumentException(MessageFormat.format(message, args)); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 5f88e64fc8..a12ed59150 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -81,9 +81,9 @@ public final class XmlSyntaxUtils { * @throws IllegalArgumentException If the item syntax doesn't support string mapping */ public static > XmlMapper seqAndDelimited(XmlMapper itemSyntax, - Collector collector, - boolean preferOldSyntax, - char delimiter) { + Collector collector, + boolean preferOldSyntax, + char delimiter) { if (!itemSyntax.supportsStringMapping()) { throw new IllegalArgumentException("Item syntax does not support string mapping " + itemSyntax); } @@ -95,7 +95,7 @@ public final class XmlSyntaxUtils { } public static > XmlMapper onlySeq(XmlMapper itemSyntax, - Collector collector) { + Collector collector) { return new SeqSyntax<>(itemSyntax, collector); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java index e5236dca30..726fa9162d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java @@ -4,17 +4,14 @@ package net.sourceforge.pmd.properties.xml.internal; -/** - * @author Clรฉment Fournier - */ public final class XmlErrorMessages { - public static final String UNEXPECTED_ELEMENT = "Unexpected element '%s', expecting %s"; - public static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '%s' is missing"; - public static final String MISSING_REQUIRED_ELEMENT = "Required child element '%s' is missing"; - public static final String IGNORED_DUPLICATE_CHILD_ELEMENT = "Expecting a single '%s' child, this will be ignored"; - public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type %s does not support the attribute syntax.\nUse a nested element, e.g. %s"; - public static final String DEPRECATED_USE_OF_ATTRIBUTE = "The use of the '%s' attribute is deprecated. Use a nested element, e.g. %s"; + public static final String UNEXPECTED_ELEMENT = "Unexpected element '{0}', expecting {0}"; + public static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '{0}' is missing"; + public static final String MISSING_REQUIRED_ELEMENT = "Required child element '{0}' is missing"; + public static final String IGNORED_DUPLICATE_CHILD_ELEMENT = "Expecting a single '{0}' child, this will be ignored"; + public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type {0} does not support the attribute syntax.\nUse a nested element, e.g. {1}"; + public static final String DEPRECATED_USE_OF_ATTRIBUTE = "The use of the '{0}' attribute is deprecated. Use a nested element, e.g. {1}"; private XmlErrorMessages() { // utility class diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 8c10005894..5186258d0c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -31,6 +31,7 @@ import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; +import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.properties.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.xml.XmlMapper; @@ -324,7 +325,7 @@ public class RuleFactory { * @return The property descriptor */ private static PropertyDescriptor parsePropertyDefinition(Element propertyElement) { - XmlErrorReporter err = new XmlErrorReporter() { }; // TODO this is a fake instance, should be provided by context + XmlErrorReporter err = new XmlErrorReporter() {}; // TODO this is a fake instance, should be provided by context String typeId = SchemaConstants.TYPE.getAttributeOrThrow(propertyElement, err); @@ -333,28 +334,22 @@ public class RuleFactory { throw new IllegalArgumentException("No property descriptor factory for type: " + typeId); } - PropertyBuilder builder = - factory.newBuilder(SchemaConstants.NAME.getAttributeOrThrow(propertyElement, err)); - - builder.desc(SchemaConstants.DESCRIPTION.getAttributeOrThrow(propertyElement, err)); - - propertyValueCapture(propertyElement, typeId, factory.getXmlMapper(), builder, err); - - // TODO support constraints like numeric range - - return builder.build(); + return propertyDefCapture(propertyElement, err, typeId, factory.getBuilderUtils()); } + private static PropertyDescriptor propertyDefCapture(Element propertyElement, + XmlErrorReporter err, + String typeId, + BuilderAndMapper factory) { - private static void propertyValueCapture(Element propertyElement, - String typeId, - XmlMapper baseSyntax, - PropertyBuilder builder, - XmlErrorReporter err) { - @SuppressWarnings("unchecked") - XmlMapper syntax = (XmlMapper) baseSyntax; - T defaultValue; + String name = SchemaConstants.NAME.getAttributeOrThrow(propertyElement, err); + String description = SchemaConstants.DESCRIPTION.getAttributeOrThrow(propertyElement, err); + final PropertyBuilder builder = factory.newBuilder(name).desc(description); + + // parse the value + final XmlMapper syntax = factory.getXmlMapper(); + final T defaultValue; @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElement); if (defaultAttr != null) { @@ -379,6 +374,10 @@ public class RuleFactory { } builder.defaultValue(defaultValue); + + // TODO support constraints like numeric range + + return builder.build(); } From 7e525d9ad82e11bbf465f0aaf0b969ed96f655f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 9 Apr 2020 21:31:40 +0200 Subject: [PATCH 044/347] Make constraints go on the xml mapper --- .../properties/GenericPropertyDescriptor.java | 26 +--- .../pmd/properties/PropertyBuilder.java | 60 ++++----- .../pmd/properties/PropertyFactory.java | 25 ++-- .../properties/xml/ConstraintDecorator.java | 115 ++++++++++++++++++ .../pmd/properties/xml/MapperSet.java | 3 +- .../pmd/properties/xml/ValueSyntax.java | 8 +- .../pmd/properties/xml/XmlMapper.java | 36 +++++- .../pmd/properties/xml/XmlSyntaxUtils.java | 52 +++++--- .../xml/internal/SchemaConstants.java | 18 +-- .../xml/internal/XmlErrorMessages.java | 2 + .../pmd/properties/xml/internal/XmlUtils.java | 44 ++++++- .../sourceforge/pmd/rules/RuleFactory.java | 7 +- .../sourceforge/pmd/util/CollectionUtil.java | 15 +++ 13 files changed, 302 insertions(+), 109 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 5e6eb922e4..290f73809b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -4,11 +4,10 @@ package net.sourceforge.pmd.properties; -import java.util.Set; +import java.util.List; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper; @@ -26,42 +25,25 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { private final String name; private final String description; private final T defaultValue; - private final Set> constraints; - GenericPropertyDescriptor(String name, String description, T defaultValue, - Set> constraints, XmlMapper parser, @Nullable PropertyTypeId typeId) { this.name = name; this.description = description; this.defaultValue = defaultValue; - this.constraints = constraints; this.parser = parser; this.typeId = typeId; - String dftValueError = errorFor(defaultValue); - if (dftValueError != null) { - throw new IllegalArgumentException(dftValueError); + List strings = parser.checkConstraints(defaultValue); + if (!strings.isEmpty()) { + throw new IllegalArgumentException("Constraint violated " + strings); } } - - @Override - public String errorFor(T value) { - for (PropertyConstraint validator : constraints) { - String error = validator.validate(value); - if (error != null) { - return error; - } - - } - return null; - } - @Override public String name() { return name; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index dbbbc4ea7d..0932e65895 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -4,13 +4,12 @@ package net.sourceforge.pmd.properties; +import static java.util.Collections.emptyList; import static net.sourceforge.pmd.util.CollectionUtil.listOf; -import java.util.Collections; -import java.util.LinkedHashSet; +import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collector; import java.util.stream.Collectors; @@ -54,7 +53,6 @@ import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; public abstract class PropertyBuilder, T> { private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z][\\w-]*"); - private final Set> validators = new LinkedHashSet<>(); private final String name; private String description; private T defaultValue; @@ -71,12 +69,6 @@ public abstract class PropertyBuilder, T> { this.name = name; } - - Set> getConstraints() { - return validators; - } - - String getDescription() { if (!isDescriptionSet()) { throw new IllegalArgumentException("Description must be provided"); @@ -147,10 +139,7 @@ public abstract class PropertyBuilder, T> { * @see net.sourceforge.pmd.properties.constraints.NumericConstraints */ @SuppressWarnings("unchecked") - public B require(PropertyConstraint constraint) { - validators.add(constraint); - return (B) this; - } + public abstract B require(PropertyConstraint constraint); /** @@ -203,7 +192,7 @@ public abstract class PropertyBuilder, T> { // This would allow specifying eg lists of numbers as 1,2,3, for which the syntax would look clumsy abstract static class BaseSinglePropertyBuilder, T> extends PropertyBuilder { - private final XmlMapper parser; + private XmlMapper parser; // Class is not final but a package-private constructor restricts inheritance @@ -217,6 +206,12 @@ public abstract class PropertyBuilder, T> { return parser; } + @SuppressWarnings("unchecked") + @Override + public B require(PropertyConstraint constraint) { + parser = parser.withConstraint(constraint); + return (B) this; + } /** * Returns a new builder that can be used to build a property @@ -292,10 +287,6 @@ public abstract class PropertyBuilder, T> { result.desc(getDescription()); } - for (PropertyConstraint validator : getConstraints()) { - result.require(validator.toCollectionConstraint()); - } - return result; } @@ -318,10 +309,6 @@ public abstract class PropertyBuilder, T> { result.desc(getDescription()); } - for (PropertyConstraint validator : getConstraints()) { - result.require(validator.toOptionalConstraint()); - } - return result; } @@ -332,7 +319,6 @@ public abstract class PropertyBuilder, T> { getName(), getDescription(), getDefaultValue(), - getConstraints(), parser, typeId ); @@ -423,19 +409,20 @@ public abstract class PropertyBuilder, T> { */ public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { - private final XmlMapper parser; + private XmlMapper itemParser; private final Collector collector; private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; + private final List> collectionConstraints = new ArrayList<>(); /** * Builds a new builder for a collection type. Package-private. */ GenericCollectionPropertyBuilder(String name, - XmlMapper parser, + XmlMapper itemParser, Collector collector) { super(name); - this.parser = parser; + this.itemParser = itemParser; this.collector = collector; } @@ -444,6 +431,11 @@ public abstract class PropertyBuilder, T> { return IteratorUtil.stream(list).collect(collector); } + @Override + public GenericCollectionPropertyBuilder require(PropertyConstraint constraint) { + collectionConstraints.add(constraint); + return this; + } /** * Specify a default value. This will be converted to type @@ -480,7 +472,7 @@ public abstract class PropertyBuilder, T> { * @return The same builder */ public GenericCollectionPropertyBuilder emptyDefaultValue() { - return this.defaultValue(Collections.emptyList()); + return this.defaultValue(emptyList()); } @@ -494,7 +486,8 @@ public abstract class PropertyBuilder, T> { * @return The same builder */ public GenericCollectionPropertyBuilder requireEach(PropertyConstraint constraint) { - return super.require(constraint.toCollectionConstraint()); + this.itemParser = itemParser.withConstraint(constraint); + return this; } @@ -519,15 +512,16 @@ public abstract class PropertyBuilder, T> { @Override public PropertyDescriptor build() { - XmlMapper syntax = parser.supportsStringMapping() && !parser.isStringParserDelimited() - ? XmlSyntaxUtils.seqAndDelimited(parser, collector, false, multiValueDelimiter) - : XmlSyntaxUtils.onlySeq(parser, collector); + XmlMapper syntax = itemParser.supportsStringMapping() && !itemParser.isStringParserDelimited() + ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, multiValueDelimiter) + : XmlSyntaxUtils.onlySeq(itemParser, collector); + + syntax = XmlSyntaxUtils.withAllConstraints(syntax, collectionConstraints); return new GenericPropertyDescriptor<>( getName(), getDescription(), getDefaultValue(), - getConstraints(), syntax, typeId ); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 238d20cc2d..6023487ab1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -6,16 +6,19 @@ package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.properties.xml.XmlSyntaxUtils.enumerationParser; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.function.Function; +import java.util.stream.Collectors; import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.constraints.NumericConstraints; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; //@formatter:off @@ -310,10 +313,11 @@ public final class PropertyFactory { // the builder, even if it wasn't registered in the constants // This is fixed in the framework refactoring public static GenericPropertyBuilder enumProperty(String name, Map nameToValue) { - // TODO find solution to document the set of possible values - // At best, map that requirement to a constraint (eg make parser return null if not found, and - // add a non-null constraint with the right description.) - return new GenericPropertyBuilder<>(name, enumerationParser(nameToValue)); + XmlMapper parser = enumerationParser( + nameToValue, + t -> nameToValue.entrySet().stream().filter(it -> it.getValue().equals(t)).map(Entry::getKey).findFirst().get() + ); + return new GenericPropertyBuilder<>(name, parser); } public static > GenericPropertyBuilder enumProperty(String name, Class enumClass) { @@ -321,12 +325,13 @@ public final class PropertyFactory { } public static > GenericPropertyBuilder enumProperty(String name, Class enumClass, Function labelMaker) { - Map labels = new HashMap<>(); - for (T constant : enumClass.getEnumConstants()) { - labels.put(labelMaker.apply(constant), constant); - } - return new GenericPropertyBuilder<>(name, enumerationParser(labels)); + // don't use a merge function, so that it throws if multiple + // values have the same key + Map labelsToValues = Arrays.stream(enumClass.getEnumConstants()) + .collect(Collectors.toMap(labelMaker, t -> t)); + + return new GenericPropertyBuilder<>(name, enumerationParser(labelsToValues, labelMaker)); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java new file mode 100644 index 0000000000..cbcbe160b7 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -0,0 +1,115 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.xml; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.w3c.dom.Element; + +import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; +import net.sourceforge.pmd.util.CollectionUtil; + +/** + * Decorates an XmlMapper with a {@link PropertyConstraint}, + * that is checked when the value is parsed. This is used to + * report errors on the most specific failing element. + */ +class ConstraintDecorator extends XmlMapper { + + + private final XmlMapper xmlMapper; + private final List> constraints; + + ConstraintDecorator(XmlMapper mapper, List> constraints) { + this.xmlMapper = mapper; + this.constraints = constraints; + } + + @Override + public T fromXml(Element element, XmlErrorReporter err) { + T t = xmlMapper.fromXml(element, err); + List failures = checkConstraints(t); + if (!failures.isEmpty()) { + throw err.error(element, XmlErrorMessages.CONSTRAINT_NOT_SATISFIED, failures); + } + return t; + } + + @Override + public List> getConstraints() { + return constraints; + } + + @Override + public XmlMapper withConstraint(PropertyConstraint t) { + return new ConstraintDecorator<>(this.xmlMapper, CollectionUtil.plus(this.constraints, t)); + } + + @Override + public void toXml(Element container, T value) { + xmlMapper.toXml(container, value); + } + + + @Override + public String getWriteElementName(T value) { + return xmlMapper.getWriteElementName(value); + } + + + @Override + public Set getReadElementNames() { + return xmlMapper.getReadElementNames(); + } + + + @Override + protected List examples(String curIndent, String baseIndent) { + return xmlMapper.examples(curIndent, baseIndent); + } + + public XmlMapper getXmlMapper() { + return xmlMapper; + } + + @Override + public boolean supportsStringMapping() { + return xmlMapper.supportsStringMapping(); + } + + @Override + public boolean isStringParserDelimited() { + return xmlMapper.isStringParserDelimited(); + } + + @Override + public T fromString(String attributeData) { + return xmlMapper.fromString(attributeData); + } + + @Override + public String toString(T value) { + return xmlMapper.toString(value); + } + + @Override + public String toString() { + return xmlMapper.toString(); + } + + static ConstraintDecorator constrain(XmlMapper mapper, PropertyConstraint constraint) { + List> constraints; + if (mapper instanceof ConstraintDecorator) { + constraints = CollectionUtil.plus(((ConstraintDecorator) mapper).constraints, constraint); + } else { + constraints = Collections.singletonList(constraint); + } + + return new ConstraintDecorator<>(mapper, constraints); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 5f6f58f66f..0dab3d0fb0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -15,6 +15,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; +import net.sourceforge.pmd.properties.xml.internal.XmlUtils; import net.sourceforge.pmd.util.CollectionUtil; /** @@ -120,7 +121,7 @@ final class MapperSet extends XmlMapper { public T fromXml(Element element, XmlErrorReporter err) { XmlMapper syntax = readIndex.get(element.getTagName()); if (syntax == null) { - throw err.error(element, XmlErrorMessages.UNEXPECTED_ELEMENT, element.getTagName(), XmlSyntaxUtils.formatPossibilities(readIndex.keySet())); + throw err.error(element, XmlErrorMessages.UNEXPECTED_ELEMENT, element.getTagName(), XmlUtils.formatPossibleNames(readIndex.keySet())); } else { return syntax.fromXml(element, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 65cf8d13e8..11f25b6ff6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -25,16 +25,16 @@ import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; *
This class is special because it enables compatibility with the
  * pre 7.0.0 XML syntax.
  */
-final class ValueSyntax extends StableXmlMapper {
+class ValueSyntax extends StableXmlMapper {
 
     private static final String VALUE_NAME = "value";
     private final Function toString;
     private final Function fromString;
     private final boolean delimited;
 
-    private ValueSyntax(Function toString,
-                        Function fromString,
-                        boolean delimited) {
+    ValueSyntax(Function toString,
+                Function fromString,
+                boolean delimited) {
         super(VALUE_NAME);
         this.toString = toString;
         this.fromString = fromString;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
index 66bdc8416a..4cd1e3f9b3 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java
@@ -4,13 +4,16 @@
 
 package net.sourceforge.pmd.properties.xml;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 import net.sourceforge.pmd.properties.PropertyFactory;
+import net.sourceforge.pmd.properties.constraints.PropertyConstraint;
 
 
 /**
@@ -24,7 +27,12 @@ public abstract class XmlMapper {
     /* package */ XmlMapper() {
     }
 
-    /** Extract the value from an XML element. */
+    /**
+     * Extract the value from an XML element. If an error occurs, throws
+     * an exception with {@link XmlErrorReporter#error(Node, Throwable)}
+     * on the most specific node (the type of exception is unspecified).
+     * This will check property constraints if any.
+     */
     public abstract T fromXml(Element element, XmlErrorReporter err);
 
 
@@ -43,6 +51,32 @@ public abstract class XmlMapper {
         return false;
     }
 
+    public List> getConstraints() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns a new XML mapper with the given constraint.
+     */
+    public XmlMapper withConstraint(PropertyConstraint t) {
+        return new ConstraintDecorator<>(this, Collections.singletonList(t));
+    }
+
+    /**
+     * Checks the result of the constraints defined by this mapper on
+     * the given element.
+     */
+    public List checkConstraints(T t) {
+        List failures = new ArrayList<>();
+        for (PropertyConstraint constraint : getConstraints()) {
+            String validationResult = constraint.validate(t);
+            if (validationResult != null) {
+                failures.add(validationResult);
+            }
+        }
+        return failures;
+    }
+
     /**
      * Read the value from a string, if it is supported.
      *
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
index a12ed59150..26a76d10e9 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java
@@ -5,21 +5,22 @@
 package net.sourceforge.pmd.properties.xml;
 
 
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
 import java.util.function.Function;
 import java.util.regex.Pattern;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
-import org.checkerframework.checker.nullness.qual.Nullable;
-
 import net.sourceforge.pmd.annotation.InternalApi;
 import net.sourceforge.pmd.internal.util.IteratorUtil;
+import net.sourceforge.pmd.internal.util.PredicateUtil;
+import net.sourceforge.pmd.properties.constraints.PropertyConstraint;
 
 /**
  * This is internal API and shouldn't be used directly by clients.
@@ -66,6 +67,15 @@ public final class XmlSyntaxUtils {
         return new OptionalSyntax<>(itemSyntax);
     }
 
+    public static  XmlMapper withAllConstraints(XmlMapper mapper, List> constraints) {
+        XmlMapper result = mapper;
+        for (PropertyConstraint constraint : constraints) {
+            result = result.withConstraint(constraint);
+        }
+
+        return result;
+    }
+
     /**
      * Builds an XML syntax that understands a {@code } syntax and
      * a delimited {@code } syntax.
@@ -164,28 +174,30 @@ public final class XmlSyntaxUtils {
     }
 
 
-    @Nullable
-    public static String formatPossibilities(Set names) {
-        if (names.isEmpty()) {
-            return null;
-        } else if (names.size() == 1) {
-            return "'" + names.iterator().next() + "'";
-        } else {
-            return "one of " + names.stream().map(it -> "'" + it + "'").collect(Collectors.joining(", "));
-        }
-    }
-
-    public static  ValueSyntax enumerationParser(final Map mappings) {
+    public static  ValueSyntax enumerationParser(final Map mappings, Function reverseFun) {
 
         if (mappings.containsValue(null)) {
             throw new IllegalArgumentException("Map may not contain entries with null values");
         }
 
-        return ValueSyntax.createNonDelimited(value -> {
-            if (!mappings.containsKey(value)) {
-                throw new IllegalArgumentException("Value was not in the set " + mappings.keySet());
+        PropertyConstraint constraint =
+            PropertyConstraint.fromPredicate(PredicateUtil.always(), "Should be in set " + mappings.keySet());
+
+        return new ValueSyntax(
+            reverseFun,
+            value -> {
+                if (!mappings.containsKey(value)) {
+                    throw new IllegalArgumentException("Value is not in the set " + mappings.keySet());
+                }
+                return mappings.get(value);
+            },
+            false
+        ) {
+            @Override
+            public List> getConstraints() {
+                return listOf(constraint);
             }
-            return mappings.get(value);
-        });
+        };
+
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java
index a45b759697..c8dbab4646 100755
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java
@@ -4,6 +4,8 @@
 
 package net.sourceforge.pmd.properties.xml.internal;
 
+import static net.sourceforge.pmd.util.CollectionUtil.setOf;
+
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -58,23 +60,11 @@ public enum SchemaConstants {
     }
 
     public List getChildrenIn(Element elt) {
-        return XmlUtils.getElementChildren(elt)
-                       .filter(it -> it.getTagName().equals(name))
-                       .collect(Collectors.toList());
+        return XmlUtils.getElementChildrenNamed(elt, name).collect(Collectors.toList());
     }
 
     public Element getSingleChildIn(Element elt, XmlErrorReporter err) {
-        List children = getChildrenIn(elt);
-        if (children.size() == 1) {
-            return children.get(0);
-        } else if (children.size() == 0) {
-            throw err.error(elt, XmlErrorMessages.MISSING_REQUIRED_ELEMENT, name);
-        } else {
-            for (int i = 1; i < children.size(); i++) {
-                err.warn(children.get(i), XmlErrorMessages.IGNORED_DUPLICATE_CHILD_ELEMENT, name);
-            }
-            return children.get(0);
-        }
+        return XmlUtils.getSingleChildIn(elt, err, setOf(name));
     }
 
     public void setOn(Element element, String value) {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java
index 726fa9162d..f9052dcb7c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java
@@ -9,9 +9,11 @@ public final class XmlErrorMessages {
     public static final String UNEXPECTED_ELEMENT = "Unexpected element '{0}', expecting {0}";
     public static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '{0}' is missing";
     public static final String MISSING_REQUIRED_ELEMENT = "Required child element '{0}' is missing";
+    public static final String MISSING_REQUIRED_ELEMENT_EITHER = "Required child element named {0} is missing";
     public static final String IGNORED_DUPLICATE_CHILD_ELEMENT = "Expecting a single '{0}' child, this will be ignored";
     public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type {0} does not support the attribute syntax.\nUse a nested element, e.g. {1}";
     public static final String DEPRECATED_USE_OF_ATTRIBUTE = "The use of the '{0}' attribute is deprecated. Use a nested element, e.g. {1}";
+    public static final String CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied: {0}";
 
     private XmlErrorMessages() {
         // utility class
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java
index 83a59fa62d..2cf084ee32 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java
@@ -6,15 +6,17 @@ package net.sourceforge.pmd.properties.xml.internal;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 import net.sourceforge.pmd.properties.xml.XmlErrorReporter;
 import net.sourceforge.pmd.properties.xml.XmlMapper;
-import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils;
 
 public final class XmlUtils {
 
@@ -36,14 +38,52 @@ public final class XmlUtils {
                                              .map(Element.class::cast);
     }
 
+    public static Stream getElementChildrenNamed(Element parent, Set names) {
+        return getElementChildren(parent).filter(e -> names.contains(e.getTagName()));
+    }
+
+    public static Stream getElementChildrenNamed(Element parent, String name) {
+        return getElementChildren(parent).filter(e -> name.equals(e.getTagName()));
+    }
+
     public static  T expectElement(XmlErrorReporter err, Element elt, XmlMapper syntax) {
 
         if (!syntax.getReadElementNames().contains(elt.getTagName())) {
-            err.warn(elt, "Wrong name, expect " + XmlSyntaxUtils.formatPossibilities(syntax.getReadElementNames()));
+            err.warn(elt, "Wrong name, expected " + formatPossibleNames(syntax.getReadElementNames()));
         } else {
             return syntax.fromXml(elt, err);
         }
 
         return null;
     }
+
+    public static Element getSingleChildIn(Element elt, XmlErrorReporter err, Set names) {
+        List children = getElementChildrenNamed(elt, names).collect(Collectors.toList());
+        if (children.size() == 1) {
+            return children.get(0);
+        } else if (children.size() == 0) {
+            if (names.size() > 1) {
+                throw err.error(elt, XmlErrorMessages.MISSING_REQUIRED_ELEMENT_EITHER, formatPossibleNames(names));
+            } else {
+                throw err.error(elt, XmlErrorMessages.MISSING_REQUIRED_ELEMENT, names.iterator().next());
+            }
+        } else {
+            for (int i = 1; i < children.size(); i++) {
+                Element child = children.get(i);
+                err.warn(child, XmlErrorMessages.IGNORED_DUPLICATE_CHILD_ELEMENT, child.getTagName());
+            }
+            return children.get(0);
+        }
+    }
+
+    @Nullable
+    public static String formatPossibleNames(Set names) {
+        if (names.isEmpty()) {
+            return null;
+        } else if (names.size() == 1) {
+            return "'" + names.iterator().next() + "'";
+        } else {
+            return "one of " + names.stream().map(it -> "'" + it + "'").collect(Collectors.joining(", "));
+        }
+    }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index 5186258d0c..3bfe94ce2a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -32,10 +32,11 @@ import net.sourceforge.pmd.properties.PropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.PropertyTypeId;
 import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper;
-import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages;
 import net.sourceforge.pmd.properties.xml.XmlErrorReporter;
 import net.sourceforge.pmd.properties.xml.XmlMapper;
 import net.sourceforge.pmd.properties.xml.internal.SchemaConstants;
+import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages;
+import net.sourceforge.pmd.properties.xml.internal.XmlUtils;
 import net.sourceforge.pmd.util.ResourceLoader;
 
 
@@ -364,12 +365,14 @@ public class RuleFactory {
                                 typeId,
                                 String.join("\nor\n", syntax.examples()));
             }
+            // the attribute syntax is deprecated.
             err.warn(attrNode,
                      XmlErrorMessages.DEPRECATED_USE_OF_ATTRIBUTE,
                      PROPERTY_VALUE.attributeName(),
                      String.join("\nor\n", syntax.examples()));
         } else {
-            Element child = PROPERTY_VALUE.getSingleChildIn(propertyElement, err);
+            Element child = XmlUtils.getSingleChildIn(propertyElement, err, syntax.getReadElementNames());
+            // this will report the correct error if any
             defaultValue = syntax.fromXml(child, err);
         }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java
index 366e8fff84..785eb57353 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java
@@ -278,6 +278,21 @@ public final class CollectionUtil {
         return res;
     }
 
+    /**
+     * Produce a new list with the elements of the first, and one additional
+     * element. The returned list may be unmodifiable.
+     */
+    public static  List plus(List m, V v) {
+        if (m.isEmpty()) {
+            return Collections.singletonList(v);
+        }
+
+        List vs = new ArrayList<>(m.size() + 1);
+        vs.addAll(m);
+        vs.add(v);
+        return vs;
+    }
+
     /**
      * Produce a new map with the mappings of the first, and one additional
      * mapping. The returned map may be unmodifiable.

From 54c7708de59ebf923165ee91b7cb50054b6b745e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Thu, 9 Apr 2020 22:01:08 +0200
Subject: [PATCH 045/347] Cleanup error handling

---
 .../properties/GenericPropertyDescriptor.java | 12 +++---
 .../pmd/properties/PropertyBuilder.java       | 23 +----------
 .../pmd/properties/PropertyDescriptor.java    |  3 +-
 .../properties/xml/ConstraintDecorator.java   | 41 ++++++++-----------
 .../pmd/properties/xml/MapperSet.java         | 10 ++++-
 .../pmd/properties/xml/OptionalSyntax.java    | 18 +++++---
 .../pmd/properties/xml/SeqSyntax.java         | 37 ++++++++++++++---
 .../pmd/properties/xml/ValueSyntax.java       |  2 +-
 .../pmd/properties/xml/XmlErrorReporter.java  |  1 -
 .../pmd/properties/xml/XmlMapper.java         | 35 +++++++---------
 .../pmd/properties/xml/XmlSyntaxUtils.java    | 26 ++++++++++++
 .../xml/internal/XmlErrorMessages.java        |  1 +
 .../sourceforge/pmd/rules/RuleFactory.java    |  4 +-
 13 files changed, 123 insertions(+), 90 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java
index 290f73809b..56a48979bd 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java
@@ -4,11 +4,10 @@
 
 package net.sourceforge.pmd.properties;
 
-import java.util.List;
-
 import org.checkerframework.checker.nullness.qual.Nullable;
 
 import net.sourceforge.pmd.properties.xml.XmlMapper;
+import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils;
 
 
 /**
@@ -38,10 +37,11 @@ final class GenericPropertyDescriptor implements PropertyDescriptor {
         this.parser = parser;
         this.typeId = typeId;
 
-        List strings = parser.checkConstraints(defaultValue);
-        if (!strings.isEmpty()) {
-            throw new IllegalArgumentException("Constraint violated " + strings);
-        }
+        XmlSyntaxUtils.checkConstraintsThrow(
+            defaultValue,
+            parser.getConstraints(),
+            s -> new IllegalArgumentException("Constraint violated " + s)
+        );
     }
 
     @Override
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index 0932e65895..baa55ebee6 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -232,8 +232,7 @@ public abstract class PropertyBuilder, T> {
          * with value type {@code }. The validators already added are
          * converted to collection validators. The default value cannot
          * have previously been set. The returned builder will support
-         * conversion to and from a delimited string if this property
-         * supports direct mapping to/from a string without delimiters.
+         * conversion to and from a delimited string if this property does.
          * Otherwise it will only support the {@code } syntax.
          *
          * 

Example usage: @@ -253,24 +252,6 @@ public abstract class PropertyBuilder, T> { * .emptyDefaultValue() * .build(); * - * // this can be set only with the syntax, - * // otherwise the delimiter would be ambiguous: - * // - * // - * // a - * // - * // - * // - * PropertyDescriptor>> whitelistSet = - * PropertyFactory.stringProperty("whitelist") - * .desc(...) - * .to(Collectors.toSet()) - * .to(Collectors.toList()) - * .emptyDefaultValue() - * .build(); - * - * }

- * * @return A new list property builder * * @throws IllegalStateException if the default value has already been set @@ -512,7 +493,7 @@ public abstract class PropertyBuilder, T> { @Override public PropertyDescriptor build() { - XmlMapper syntax = itemParser.supportsStringMapping() && !itemParser.isStringParserDelimited() + XmlMapper syntax = itemParser.supportsStringMapping() ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, multiValueDelimiter) : XmlSyntaxUtils.onlySeq(itemParser, collector); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index ab64894432..0adcde02bf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -83,8 +83,7 @@ public interface PropertyDescriptor { * Returns the type ID which was used to define this property. Returns * null if this property was defined in Java code and not in XML. * - * TODO this replaces isDefinedExternally for the RulesetWriter. - * I still don't like it. + *

This replaces isDefinedExternally for the RulesetWriter. */ @InternalApi default @Nullable PropertyTypeId getTypeId() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index cbcbe160b7..f2d03890e7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.properties.xml; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -15,9 +14,16 @@ import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.util.CollectionUtil; /** - * Decorates an XmlMapper with a {@link PropertyConstraint}, - * that is checked when the value is parsed. This is used to + * Decorates an XmlMapper with some {@link PropertyConstraint}s. + * Those are checked when the value is parsed. This is used to * report errors on the most specific failing element. + * + *

Note that this is the only XmlMapper that *applies* constraints + * in {@link #fromXml(Element, XmlErrorReporter)}. A {@link SeqSyntax} + * or {@link OptionalSyntax} may return some constraints in {@link #getConstraints()} + * that are derived from the constraints of the item, yet not check them + * on elements (they will be applied on each element by the {@link XmlMapper} + * they wrap). */ class ConstraintDecorator extends XmlMapper { @@ -33,10 +39,13 @@ class ConstraintDecorator extends XmlMapper { @Override public T fromXml(Element element, XmlErrorReporter err) { T t = xmlMapper.fromXml(element, err); - List failures = checkConstraints(t); - if (!failures.isEmpty()) { - throw err.error(element, XmlErrorMessages.CONSTRAINT_NOT_SATISFIED, failures); - } + + XmlSyntaxUtils.checkConstraintsThrow( + t, + constraints, + s -> err.error(element, XmlErrorMessages.CONSTRAINT_NOT_SATISFIED, s) + ); + return t; } @@ -69,12 +78,8 @@ class ConstraintDecorator extends XmlMapper { @Override - protected List examples(String curIndent, String baseIndent) { - return xmlMapper.examples(curIndent, baseIndent); - } - - public XmlMapper getXmlMapper() { - return xmlMapper; + protected List examplesImpl(String curIndent, String baseIndent) { + return xmlMapper.examplesImpl(curIndent, baseIndent); } @Override @@ -102,14 +107,4 @@ class ConstraintDecorator extends XmlMapper { return xmlMapper.toString(); } - static ConstraintDecorator constrain(XmlMapper mapper, PropertyConstraint constraint) { - List> constraints; - if (mapper instanceof ConstraintDecorator) { - constraints = CollectionUtil.plus(((ConstraintDecorator) mapper).constraints, constraint); - } else { - constraints = Collections.singletonList(constraint); - } - - return new ConstraintDecorator<>(mapper, constraints); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 0dab3d0fb0..4a8f48c4cf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.properties.xml.internal.XmlUtils; import net.sourceforge.pmd.util.CollectionUtil; @@ -102,6 +103,11 @@ final class MapperSet extends XmlMapper { throw new UnsupportedOperationException(); } + @Override + public List> getConstraints() { + return readIndex.values().iterator().next().getConstraints(); + } + private Collection> supportedReadStrategies() { return readIndex.values(); } @@ -134,7 +140,7 @@ final class MapperSet extends XmlMapper { @Override - protected List examples(String curIndent, String baseIndent) { - return readIndex.values().stream().flatMap(it -> it.examples(curIndent, baseIndent).stream()).collect(Collectors.toList()); + protected List examplesImpl(String curIndent, String baseIndent) { + return readIndex.values().stream().flatMap(it -> it.examplesImpl(curIndent, baseIndent).stream()).collect(Collectors.toList()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index b97f772794..a9c1d20ea1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -4,14 +4,17 @@ package net.sourceforge.pmd.properties.xml; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.util.CollectionUtil; + /** * Serialize an optional value. If the value is itself an {@code Optional}, * then mentioning {@code } will yield a toplevel empty optional. So @@ -45,6 +48,13 @@ final class OptionalSyntax extends XmlMapper> { : Optional.ofNullable(itemSyntax.fromString(attributeData)); } + @Override + public List>> getConstraints() { + return itemSyntax.getConstraints().stream() + .map(PropertyConstraint::toOptionalConstraint) + .collect(Collectors.toList()); + } + @Override public void toXml(Element container, Optional value) { if (value.isPresent()) { @@ -77,9 +87,7 @@ final class OptionalSyntax extends XmlMapper> { } @Override - protected List examples(String curIndent, String baseIndent) { - ArrayList list = new ArrayList<>(itemSyntax.examples(curIndent, baseIndent)); - list.add(curIndent + ""); - return list; + protected List examplesImpl(String curIndent, String baseIndent) { + return CollectionUtil.plus(itemSyntax.examplesImpl(curIndent, baseIndent), curIndent + ""); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index b5c16d43cf..c70cff479a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -8,10 +8,13 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collector; +import java.util.stream.Collectors; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; +import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.properties.xml.internal.XmlUtils; /** @@ -43,18 +46,40 @@ final class SeqSyntax> extends StableXmlMapper { @Override public C fromXml(Element element, XmlErrorReporter err) { - return XmlUtils.getElementChildren(element) - .map(child -> XmlUtils.expectElement(err, child, itemSyntax)) - .filter(Objects::nonNull) - .collect(collector); + RuntimeException aggregateEx = err.error(element, XmlErrorMessages.LIST_CONSTRAINT_NOT_SATISFIED); + + C result = XmlUtils.getElementChildren(element) + .map(child -> { + try { + return XmlUtils.expectElement(err, child, itemSyntax); + } catch (Exception e) { + aggregateEx.addSuppressed(e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(collector); + + if (aggregateEx.getSuppressed().length > 0) { + throw aggregateEx; + } else { + return result; + } } @Override - protected List examples(String curIndent, String baseIndent) { + public List> getConstraints() { + return itemSyntax.getConstraints().stream() + .map(PropertyConstraint::toCollectionConstraint) + .collect(Collectors.toList()); + } + + @Override + protected List examplesImpl(String curIndent, String baseIndent) { String newIndent = curIndent + baseIndent; return Collections.singletonList( curIndent + "\n" - + newIndent + String.join("\n", itemSyntax.examples(newIndent, baseIndent)) + "\n" + + newIndent + String.join("\n", itemSyntax.examplesImpl(newIndent, baseIndent)) + "\n" + newIndent + "..." + curIndent + "" ); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 11f25b6ff6..e5fd42f9f4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -77,7 +77,7 @@ class ValueSyntax extends StableXmlMapper { } @Override - protected List examples(String curIndent, String baseIndent) { + protected List examplesImpl(String curIndent, String baseIndent) { return Collections.singletonList(curIndent + "data"); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java index 821aa1a4c1..8f45849cc0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java @@ -31,5 +31,4 @@ public interface XmlErrorReporter { return new IllegalArgumentException(ex); } - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index 4cd1e3f9b3..5e8b2f1ab8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.properties.xml; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -51,32 +50,26 @@ public abstract class XmlMapper { return false; } + /** + * Returns the constraints that this mapper applies to values + * after parsing them. This may be used for documentation, or + * to check a constraint on a value that was not parsed from + * XML. + * + * @implNote See {@link ConstraintDecorator} + */ public List> getConstraints() { return Collections.emptyList(); } /** - * Returns a new XML mapper with the given constraint. + * Returns a new XML mapper that will check parsed values with + * the given constraint. */ public XmlMapper withConstraint(PropertyConstraint t) { return new ConstraintDecorator<>(this, Collections.singletonList(t)); } - /** - * Checks the result of the constraints defined by this mapper on - * the given element. - */ - public List checkConstraints(T t) { - List failures = new ArrayList<>(); - for (PropertyConstraint constraint : getConstraints()) { - String validationResult = constraint.validate(t); - if (validationResult != null) { - failures.add(validationResult); - } - } - return failures; - } - /** * Read the value from a string, if it is supported. * @@ -110,8 +103,8 @@ public abstract class XmlMapper { * Returns some examples for what XML output this strategy produces. * For example, {@code 1}. */ - public final List examples() { - return examples("", " "); + public final List getExamples() { + return examplesImpl("", " "); } /** @@ -120,11 +113,11 @@ public abstract class XmlMapper { * @param curIndent Indentation of the current level * @param baseIndent Base indentation string, adding one indent level concats this with the [curIndent] */ - protected abstract List examples(String curIndent, String baseIndent); + protected abstract List examplesImpl(String curIndent, String baseIndent); @Override public String toString() { - return examples().get(0); + return getExamples().get(0); } abstract static class StableXmlMapper extends XmlMapper { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 26a76d10e9..2257373a27 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -67,6 +67,32 @@ public final class XmlSyntaxUtils { return new OptionalSyntax<>(itemSyntax); } + + /** + * Checks the result of the constraints defined by this mapper on + * the given element. Returns all failures as a list of strings. + */ + public static List checkConstraints(T t, List> constraints) { + List failures = new ArrayList<>(); + for (PropertyConstraint constraint : constraints) { + String validationResult = constraint.validate(t); + if (validationResult != null) { + failures.add(validationResult); + } + } + return failures; + } + + public static void checkConstraintsThrow(T t, + List> constraints, + Function exceptionMaker) { + List failures = checkConstraints(t, constraints); + + if (failures.isEmpty()) { + throw exceptionMaker.apply(String.join(", ", failures)); + } + } + public static XmlMapper withAllConstraints(XmlMapper mapper, List> constraints) { XmlMapper result = mapper; for (PropertyConstraint constraint : constraints) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java index f9052dcb7c..de5da1fec0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java @@ -14,6 +14,7 @@ public final class XmlErrorMessages { public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type {0} does not support the attribute syntax.\nUse a nested element, e.g. {1}"; public static final String DEPRECATED_USE_OF_ATTRIBUTE = "The use of the '{0}' attribute is deprecated. Use a nested element, e.g. {1}"; public static final String CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied: {0}"; + public static final String LIST_CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied on items"; private XmlErrorMessages() { // utility class diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 3bfe94ce2a..e8d7638b17 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -363,13 +363,13 @@ public class RuleFactory { throw err.error(attrNode, XmlErrorMessages.PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE, typeId, - String.join("\nor\n", syntax.examples())); + String.join("\nor\n", syntax.getExamples())); } // the attribute syntax is deprecated. err.warn(attrNode, XmlErrorMessages.DEPRECATED_USE_OF_ATTRIBUTE, PROPERTY_VALUE.attributeName(), - String.join("\nor\n", syntax.examples())); + String.join("\nor\n", syntax.getExamples())); } else { Element child = XmlUtils.getSingleChildIn(propertyElement, err, syntax.getReadElementNames()); // this will report the correct error if any From e2b4be51f2d7e981151f39bfaf49ca89ecd41ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 9 Apr 2020 22:49:18 +0200 Subject: [PATCH 046/347] Improve rule factory --- .../properties/xml/ConstraintDecorator.java | 5 - .../pmd/properties/xml/MapperSet.java | 5 - .../pmd/properties/xml/ValueSyntax.java | 5 +- .../pmd/properties/xml/XmlMapper.java | 21 +- .../xml/internal/SchemaConstants.java | 27 ++- .../xml/internal/XmlErrorMessages.java | 6 +- .../pmd/properties/xml/internal/XmlUtils.java | 46 +++++ .../sourceforge/pmd/rules/RuleFactory.java | 185 +++++++----------- 8 files changed, 150 insertions(+), 150 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index f2d03890e7..cbe210333d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -87,11 +87,6 @@ class ConstraintDecorator extends XmlMapper { return xmlMapper.supportsStringMapping(); } - @Override - public boolean isStringParserDelimited() { - return xmlMapper.isStringParserDelimited(); - } - @Override public T fromString(String attributeData) { return xmlMapper.fromString(attributeData); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 4a8f48c4cf..3134386370 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -118,11 +118,6 @@ final class MapperSet extends XmlMapper { && forWrite.supportsStringMapping(); } - @Override - public boolean isStringParserDelimited() { - return supportedReadStrategies().stream().anyMatch(XmlMapper::isStringParserDelimited); - } - @Override public T fromXml(Element element, XmlErrorReporter err) { XmlMapper syntax = readIndex.get(element.getTagName()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index e5fd42f9f4..305a8831a5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -11,6 +11,7 @@ import java.util.function.Function; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; /** @@ -47,8 +48,8 @@ class ValueSyntax extends StableXmlMapper { } @Override - public boolean isStringParserDelimited() { - return delimited; + public List> getConstraints() { + return Collections.emptyList(); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index 5e8b2f1ab8..9055d574cb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.properties.xml; +import static net.sourceforge.pmd.util.CollectionUtil.setOf; + import java.util.Collections; import java.util.List; import java.util.Set; @@ -46,10 +48,6 @@ public abstract class XmlMapper { return false; } - public boolean isStringParserDelimited() { - return false; - } - /** * Returns the constraints that this mapper applies to values * after parsing them. This may be used for documentation, or @@ -58,9 +56,7 @@ public abstract class XmlMapper { * * @implNote See {@link ConstraintDecorator} */ - public List> getConstraints() { - return Collections.emptyList(); - } + public abstract List> getConstraints(); /** * Returns a new XML mapper that will check parsed values with @@ -120,18 +116,15 @@ public abstract class XmlMapper { return getExamples().get(0); } + /** + * A mapper that has a single name for read and write. + */ abstract static class StableXmlMapper extends XmlMapper { private final String eltName; - private final Set readNames; /* package */ StableXmlMapper(String eltName) { - this(eltName, Collections.singleton(eltName)); - } - - /* package */ StableXmlMapper(String eltName, Set readNames) { this.eltName = eltName; - this.readNames = readNames; } @Override @@ -141,7 +134,7 @@ public abstract class XmlMapper { @Override public Set getReadElementNames() { - return readNames; + return setOf(eltName); } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java index c8dbab4646..b420ed291f 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java @@ -29,7 +29,14 @@ public enum SchemaConstants { /** The description of the property. */ DESCRIPTION("description"), /** The default value. */ - PROPERTY_VALUE("value"); + PROPERTY_VALUE("value"), + + PROPERTY_ELT("property"), + + PROPERTIES("properties"), + DEPRECATED("deprecated"), + + ; private final String name; @@ -38,6 +45,12 @@ public enum SchemaConstants { this.name = name; } + + public boolean getAsBooleanAttr(Element e, boolean defaultValue) { + String attr = e.getAttribute(name); + return attr != null ? Boolean.parseBoolean(attr) : defaultValue; + } + @NonNull public String getAttributeOrThrow(Element element, XmlErrorReporter err) { String attribute = element.getAttribute(name); @@ -60,7 +73,13 @@ public enum SchemaConstants { } public List getChildrenIn(Element elt) { - return XmlUtils.getElementChildrenNamed(elt, name).collect(Collectors.toList()); + return XmlUtils.getElementChildrenNamed(elt, name) + .collect(Collectors.toList()); + } + + public List getElementChildrenNamedReportOthers(Element elt, XmlErrorReporter err) { + return XmlUtils.getElementChildrenNamedReportOthers(elt, setOf(name), err) + .collect(Collectors.toList()); } public Element getSingleChildIn(Element elt, XmlErrorReporter err) { @@ -76,14 +95,14 @@ public enum SchemaConstants { * * @return The attribute's name */ - public String attributeName() { + public String xmlName() { return name; } @Override public String toString() { - return attributeName(); + return xmlName(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java index de5da1fec0..90377eec0e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java @@ -10,8 +10,12 @@ public final class XmlErrorMessages { public static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '{0}' is missing"; public static final String MISSING_REQUIRED_ELEMENT = "Required child element '{0}' is missing"; public static final String MISSING_REQUIRED_ELEMENT_EITHER = "Required child element named {0} is missing"; + public static final String IGNORED_UNEXPECTED_CHILD_ELEMENT = "Unexpected element '{0}', expecting only '{1}', this will be ignored"; public static final String IGNORED_DUPLICATE_CHILD_ELEMENT = "Expecting a single '{0}' child, this will be ignored"; - public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "The type {0} does not support the attribute syntax.\nUse a nested element, e.g. {1}"; + + public static final String DUPLICATE_PROPERTY_SETTER = "Duplicate property tag for name '{0}', this will be ignored"; + public static final String PROPERTY_DOES_NOT_EXIST = "Cannot set non-existent property '{0}' on rule '{1}', known properties are {2}"; + public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "This property does not support the attribute syntax.\nUse a nested element, e.g. {1}"; public static final String DEPRECATED_USE_OF_ATTRIBUTE = "The use of the '{0}' attribute is deprecated. Use a nested element, e.g. {1}"; public static final String CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied: {0}"; public static final String LIST_CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied on items"; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java index 2cf084ee32..e1d275a9ab 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.properties.xml.internal; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,6 +43,18 @@ public final class XmlUtils { return getElementChildren(parent).filter(e -> names.contains(e.getTagName())); } + public static Stream getElementChildrenNamedReportOthers(Element parent, Set names, XmlErrorReporter err) { + return getElementChildren(parent) + .map(it -> { + if (names.contains(it.getTagName())) { + return it; + } else { + err.warn(it, XmlErrorMessages.IGNORED_UNEXPECTED_CHILD_ELEMENT, it.getTagName(), formatPossibleNames(names)); + return null; + } + }).filter(Objects::nonNull); + } + public static Stream getElementChildrenNamed(Element parent, String name) { return getElementChildren(parent).filter(e -> name.equals(e.getTagName())); } @@ -57,6 +70,15 @@ public final class XmlUtils { return null; } + + public static List getChildrenExpectSingleName(Element elt, String name, XmlErrorReporter err) { + return XmlUtils.getElementChildren(elt).peek(it -> { + if (!it.getTagName().equals(name)) { + err.warn(it, XmlErrorMessages.IGNORED_UNEXPECTED_CHILD_ELEMENT, it.getTagName(), name); + } + }).collect(Collectors.toList()); + } + public static Element getSingleChildIn(Element elt, XmlErrorReporter err, Set names) { List children = getElementChildrenNamed(elt, names).collect(Collectors.toList()); if (children.size() == 1) { @@ -86,4 +108,28 @@ public final class XmlUtils { return "one of " + names.stream().map(it -> "'" + it + "'").collect(Collectors.joining(", ")); } } + + /** + * Parse a String from a textually type node. + * + * @param node The node. + * + * @return The String. + */ + public static String parseTextNode(Node node) { + final int nodeCount = node.getChildNodes().getLength(); + if (nodeCount == 0) { + return ""; + } + + StringBuilder buffer = new StringBuilder(); + + for (int i = 0; i < nodeCount; i++) { + Node childNode = node.getChildNodes().item(i); + if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) { + buffer.append(childNode.getNodeValue()); + } + } + return buffer.toString(); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index e8d7638b17..7b9d95f4ac 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -5,18 +5,20 @@ package net.sourceforge.pmd.rules; import static net.sourceforge.pmd.properties.xml.internal.SchemaConstants.PROPERTY_VALUE; +import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.formatPossibleNames; +import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.getSingleChildIn; +import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.parseTextNode; -import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; import org.w3c.dom.Element; @@ -36,7 +38,6 @@ import net.sourceforge.pmd.properties.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.internal.SchemaConstants; import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; -import net.sourceforge.pmd.properties.xml.internal.XmlUtils; import net.sourceforge.pmd.util.ResourceLoader; @@ -127,7 +128,7 @@ public class RuleFactory { ruleReference.setPriority(RulePriority.valueOf(Integer.parseInt(parseTextNode(node)))); break; case PROPERTIES: - setPropertyValues(ruleReference, (Element) node); + setPropertyValues(ruleReference, (Element) node, dummyErrorReporter()); break; default: throw new IllegalArgumentException("Unexpected element <" + node.getNodeName() @@ -175,7 +176,7 @@ public class RuleFactory { builder.message(ruleElement.getAttribute(MESSAGE)); builder.externalInfoUrl(ruleElement.getAttribute(EXTERNAL_INFO_URL)); - builder.setDeprecated(hasAttributeSetTrue(ruleElement, DEPRECATED)); + builder.setDeprecated(SchemaConstants.DEPRECATED.getAsBooleanAttr(ruleElement, false)); Element propertiesElement = null; @@ -216,7 +217,7 @@ public class RuleFactory { } if (propertiesElement != null) { - setPropertyValues(rule, propertiesElement); + setPropertyValues(rule, propertiesElement, dummyErrorReporter()); } return rule; @@ -232,27 +233,6 @@ public class RuleFactory { } } - /** - * Parses a properties element looking only for the values of the properties defined or overridden. - * - * @param propertiesNode Node to parse - * - * @return A map of property names to their value - */ - private Map getPropertyValuesFrom(Element propertiesNode) { - Map overriddenProperties = new HashMap<>(); - - for (int i = 0; i < propertiesNode.getChildNodes().getLength(); i++) { - Node node = propertiesNode.getChildNodes().item(i); - if (node.getNodeType() == Node.ELEMENT_NODE && PROPERTY.equals(node.getNodeName())) { - Entry overridden = getPropertyValue((Element) node); - overriddenProperties.put(overridden.getKey(), overridden.getValue()); - } - } - - return overriddenProperties; - } - /** * Parses the properties node and adds property definitions to the builder. Doesn't care for value overriding, that * will be handled after the rule instantiation. @@ -271,40 +251,42 @@ public class RuleFactory { } } - /** - * Gets a mapping of property name to its value from the given property element. - * - * @param propertyElement Property element - * - * @return An entry of property name to its value - */ - private Entry getPropertyValue(Element propertyElement) { - String name = propertyElement.getAttribute(SchemaConstants.NAME.attributeName()); - return new SimpleEntry<>(name, valueFrom(propertyElement)); - } - /** * Overrides the rule's properties with the values defined in the element. * * @param rule The rule * @param propertiesElt The {@literal } element */ - private void setPropertyValues(Rule rule, Element propertiesElt) { - Map overridden = getPropertyValuesFrom(propertiesElt); + private void setPropertyValues(Rule rule, Element propertiesElt, XmlErrorReporter err) { + Set overridden = new HashSet<>(); - for (Entry e : overridden.entrySet()) { - PropertyDescriptor descriptor = rule.getPropertyDescriptor(e.getKey()); - if (descriptor == null) { - throw new IllegalArgumentException( - "Cannot set non-existent property '" + e.getKey() + "' on Rule " + rule.getName()); + for (Element element : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesElt, err)) { + String name = SchemaConstants.NAME.getAttributeOrThrow(element, err); + if (!overridden.add(name)) { + err.warn(element, XmlErrorMessages.DUPLICATE_PROPERTY_SETTER, name); + continue; } - setRulePropertyCapture(rule, descriptor, e.getValue()); + PropertyDescriptor desc = rule.getPropertyDescriptor(name); + if (desc == null) { + err.warn(element, XmlErrorMessages.PROPERTY_DOES_NOT_EXIST, name, rule.getName(), knownPropertiesOf(rule)); + continue; + } + setRulePropertyCapture(rule, desc, element, err); } } - private void setRulePropertyCapture(Rule rule, PropertyDescriptor descriptor, String value) { - rule.setProperty(descriptor, descriptor.valueFrom(value)); + private void setRulePropertyCapture(Rule rule, PropertyDescriptor descriptor, Element propertyElt, XmlErrorReporter err) { + T value = parsePropertyValue(propertyElt, err, descriptor.xmlMapper()); + rule.setProperty(descriptor, value); + } + + @Nullable + private String knownPropertiesOf(Rule rule) { + Set set = rule.getPropertyDescriptors().stream() + .map(PropertyDescriptor::name) + .collect(Collectors.toSet()); + return formatPossibleNames(set); } /** @@ -315,7 +297,7 @@ public class RuleFactory { * @return True if this element defines a new property, false if this is just stating a value */ private static boolean isPropertyDefinition(Element node) { - return node.hasAttribute(SchemaConstants.TYPE.attributeName()); + return node.hasAttribute(SchemaConstants.TYPE.xmlName()); } /** @@ -326,7 +308,7 @@ public class RuleFactory { * @return The property descriptor */ private static PropertyDescriptor parsePropertyDefinition(Element propertyElement) { - XmlErrorReporter err = new XmlErrorReporter() {}; // TODO this is a fake instance, should be provided by context + XmlErrorReporter err = dummyErrorReporter(); String typeId = SchemaConstants.TYPE.getAttributeOrThrow(propertyElement, err); @@ -335,12 +317,11 @@ public class RuleFactory { throw new IllegalArgumentException("No property descriptor factory for type: " + typeId); } - return propertyDefCapture(propertyElement, err, typeId, factory.getBuilderUtils()); + return propertyDefCapture(propertyElement, err, factory.getBuilderUtils()); } private static PropertyDescriptor propertyDefCapture(Element propertyElement, XmlErrorReporter err, - String typeId, BuilderAndMapper factory) { String name = SchemaConstants.NAME.getAttributeOrThrow(propertyElement, err); @@ -350,31 +331,8 @@ public class RuleFactory { // parse the value final XmlMapper syntax = factory.getXmlMapper(); - final T defaultValue; - @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElement); - if (defaultAttr != null) { - Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElement); - try { - defaultValue = syntax.fromString(defaultAttr); - } catch (IllegalArgumentException e) { - throw err.error(attrNode, e); - } catch (UnsupportedOperationException e) { - throw err.error(attrNode, - XmlErrorMessages.PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE, - typeId, - String.join("\nor\n", syntax.getExamples())); - } - // the attribute syntax is deprecated. - err.warn(attrNode, - XmlErrorMessages.DEPRECATED_USE_OF_ATTRIBUTE, - PROPERTY_VALUE.attributeName(), - String.join("\nor\n", syntax.getExamples())); - } else { - Element child = XmlUtils.getSingleChildIn(propertyElement, err, syntax.getReadElementNames()); - // this will report the correct error if any - defaultValue = syntax.fromXml(child, err); - } + final T defaultValue = parsePropertyValue(propertyElement, err, syntax); builder.defaultValue(defaultValue); @@ -383,51 +341,40 @@ public class RuleFactory { return builder.build(); } + private static T parsePropertyValue(Element propertyElt, XmlErrorReporter err, XmlMapper syntax) { + @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElt); + if (defaultAttr != null) { + Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElt); - /** Gets the string value from a property node. */ - private static String valueFrom(Element propertyNode) { - String strValue = propertyNode.getAttribute(PROPERTY_VALUE.attributeName()); + // the attribute syntax is deprecated. + err.warn(attrNode, + XmlErrorMessages.DEPRECATED_USE_OF_ATTRIBUTE, + PROPERTY_VALUE.xmlName(), + String.join("\nor\n", syntax.getExamples())); - if (StringUtils.isNotBlank(strValue)) { - return strValue; - } - - final NodeList nodeList = propertyNode.getChildNodes(); - - for (int i = 0; i < nodeList.getLength(); i++) { - Node node = nodeList.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE && "value".equals(node.getNodeName())) { - return parseTextNode(node); + try { + return syntax.fromString(defaultAttr); + } catch (IllegalArgumentException e) { + throw err.error(attrNode, e); + } catch (UnsupportedOperationException e) { + throw err.error(attrNode, + XmlErrorMessages.PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE, + String.join("\nor\n", syntax.getExamples())); } + + } else { + Element child = getSingleChildIn(propertyElt, err, syntax.getReadElementNames()); + // this will report the correct error if any + return syntax.fromXml(child, err); } - return null; } - private static boolean hasAttributeSetTrue(Element element, String attributeId) { - return element.hasAttribute(attributeId) && "true".equalsIgnoreCase(element.getAttribute(attributeId)); - } - /** - * Parse a String from a textually type node. - * - * @param node The node. - * - * @return The String. - */ - private static String parseTextNode(Node node) { - final int nodeCount = node.getChildNodes().getLength(); - if (nodeCount == 0) { - return ""; - } - - StringBuilder buffer = new StringBuilder(); - - for (int i = 0; i < nodeCount; i++) { - Node childNode = node.getChildNodes().item(i); - if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) { - buffer.append(childNode.getNodeValue()); - } - } - return buffer.toString(); + @Deprecated + @NonNull + private static XmlErrorReporter dummyErrorReporter() { + // TODO this is a fake instance, should be provided by context + // I'm only doing this to not make the change too contagious for now + return new XmlErrorReporter() {}; } } From 7a57d9f404def4c81d0ddef78a4780ab7a253f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 10 Apr 2020 00:01:00 +0200 Subject: [PATCH 047/347] Simplify enum property handling --- .../properties/GenericPropertyDescriptor.java | 5 ++ .../pmd/properties/PropertyDescriptor.java | 13 ++-- .../pmd/properties/PropertyFactory.java | 39 +++++++++- .../constraints/PropertyConstraint.java | 3 +- .../properties/xml/ConstraintDecorator.java | 5 +- .../pmd/properties/xml/MapperSet.java | 5 +- .../pmd/properties/xml/OptionalSyntax.java | 5 +- .../pmd/properties/xml/ValueSyntax.java | 61 +++++++++++---- .../pmd/properties/xml/XmlMapper.java | 4 +- .../pmd/properties/xml/XmlSyntaxUtils.java | 74 +++++++++---------- .../sourceforge/pmd/util/CollectionUtil.java | 35 ++++++++- .../properties/PropertyDescriptorTest.java | 3 +- .../constraints/NumericConstraintsTest.java | 4 +- .../documentation/CommentRequiredRule.java | 38 +--------- 14 files changed, 181 insertions(+), 113 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 56a48979bd..1ec5a12597 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -59,6 +59,11 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { return defaultValue; } + @Override + public String errorFor(T value) { + return XmlSyntaxUtils.checkConstraintsJoin(value, parser.getConstraints()); + } + @Override public XmlMapper xmlMapper() { return parser; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 0adcde02bf..6150e20704 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -66,17 +66,16 @@ public interface PropertyDescriptor { /** - * TODO - * this needs to go away. Property constraints should be checked - * at the time the ruleset is parsed, to report error messages - * targeted on each node. They could simply decorate the XmlMapper. + * TODO this needs to go away. Property constraints are now checked at + * the time the ruleset is parsed, to report errors on the specific + * XML nodes. Other than that, constraints should be checked when + * calling {@link PropertySource#setProperty(PropertyDescriptor, Object)} + * for fail-fast behaviour. * * @deprecated PMD 7.0.0 will change the return type to {@code Optional} */ @Deprecated - default String errorFor(T value) { - return null; - } + String errorFor(T value); /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 6023487ab1..318aefb6c1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -9,10 +9,12 @@ import static net.sourceforge.pmd.properties.xml.XmlSyntaxUtils.enumerationParse import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Map.Entry; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; @@ -20,6 +22,7 @@ import net.sourceforge.pmd.properties.constraints.NumericConstraints; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; +import net.sourceforge.pmd.util.CollectionUtil; //@formatter:off /** @@ -307,6 +310,8 @@ public final class PropertyFactory { * @param nameToValue Map of labels to values. The null key is ignored. * @param Value type of the property * + * @throws IllegalArgumentException If the map contains a null value or key + * * @return A new builder */ // Note: there is a bug, whereby the default value can be set on @@ -315,17 +320,43 @@ public final class PropertyFactory { public static GenericPropertyBuilder enumProperty(String name, Map nameToValue) { XmlMapper parser = enumerationParser( nameToValue, - t -> nameToValue.entrySet().stream().filter(it -> it.getValue().equals(t)).map(Entry::getKey).findFirst().get() + t -> Objects.requireNonNull(CollectionUtil.getKeyOfValue(nameToValue, t)) ); return new GenericPropertyBuilder<>(name, parser); } + /** + * Returns a builder for an enumerated property for the given enum + * class, using the name of its enum constants as labels. + * + * @param name Property name + * @param enumClass Enum class + * @param Type of the enum class + * + * @return A new builder + */ public static > GenericPropertyBuilder enumProperty(String name, Class enumClass) { return enumProperty(name, enumClass, Enum::name); } - public static > GenericPropertyBuilder enumProperty(String name, Class enumClass, Function labelMaker) { - + /** + * Returns a builder for an enumerated property for the given enum + * class, using the given function to label its constants. + * + * @param name Property name + * @param enumClass Enum class + * @param labelMaker Function that labels each constant + * @param Type of the enum class + * + * @return A new builder + * + * @throws NullPointerException If any of the arguments is null + * @throws IllegalArgumentException If the label maker returns null on some constant + * @throws IllegalStateException If the label maker maps two constants to the same label + */ + public static > GenericPropertyBuilder enumProperty(String name, + Class enumClass, + Function labelMaker) { // don't use a merge function, so that it throws if multiple // values have the same key Map labelsToValues = Arrays.stream(enumClass.getEnumConstants()) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java index e191d0d6e4..a83b4e0e09 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java @@ -119,10 +119,9 @@ public interface PropertyConstraint { static PropertyConstraint fromPredicate(final Predicate pred, final String constraintDescription) { return new PropertyConstraint() { - // TODO message could be better, eg include name of the property @Override public String validate(U value) { - return pred.test(value) ? null : value + " " + StringUtils.uncapitalize(constraintDescription); + return pred.test(value) ? null : "'" + value + "' " + StringUtils.uncapitalize(constraintDescription); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index cbe210333d..585e04404b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.properties.xml; import java.util.List; import java.util.Set; +import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -88,12 +89,12 @@ class ConstraintDecorator extends XmlMapper { } @Override - public T fromString(String attributeData) { + public T fromString(@NonNull String attributeData) { return xmlMapper.fromString(attributeData); } @Override - public String toString(T value) { + public @NonNull String toString(T value) { return xmlMapper.toString(value); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 3134386370..3d1573f5ec 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; @@ -80,7 +81,7 @@ final class MapperSet extends XmlMapper { } @Override - public @Nullable T fromString(String string) { + public @Nullable T fromString(@NonNull String string) { for (XmlMapper syntax : supportedReadStrategies()) { if (syntax.supportsStringMapping()) { @@ -92,7 +93,7 @@ final class MapperSet extends XmlMapper { } @Override - public String toString(T value) { + public @NonNull String toString(T value) { for (XmlMapper syntax : supportedReadStrategies()) { if (syntax.supportsStringMapping()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index a9c1d20ea1..02b833a699 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -10,6 +10,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -38,12 +39,12 @@ final class OptionalSyntax extends XmlMapper> { } @Override - public String toString(Optional value) { + public @NonNull String toString(Optional value) { return value.map(itemSyntax::toString).orElse(""); } @Override - public Optional fromString(String attributeData) { + public Optional fromString(@NonNull String attributeData) { return attributeData.isEmpty() ? Optional.empty() : Optional.ofNullable(itemSyntax.fromString(attributeData)); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 305a8831a5..79529c4e3e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -4,13 +4,17 @@ package net.sourceforge.pmd.properties.xml; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; + import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Function; +import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.PredicateUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; @@ -29,17 +33,19 @@ import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; class ValueSyntax extends StableXmlMapper { private static final String VALUE_NAME = "value"; - private final Function toString; - private final Function fromString; - private final boolean delimited; + private final Function toString; + private final Function<@NonNull String, ? extends T> fromString; + + // these are not applied, just used to document the possible values + private final List> docConstraints; ValueSyntax(Function toString, - Function fromString, - boolean delimited) { + Function<@NonNull String, ? extends T> fromString, + List> docConstraints) { super(VALUE_NAME); this.toString = toString; this.fromString = fromString; - this.delimited = delimited; + this.docConstraints = docConstraints; } @Override @@ -49,16 +55,16 @@ class ValueSyntax extends StableXmlMapper { @Override public List> getConstraints() { - return Collections.emptyList(); + return docConstraints; } @Override - public T fromString(String attributeData) { + public T fromString(@NonNull String attributeData) { return fromString.apply(attributeData); } @Override - public String toString(T data) { + public @NonNull String toString(T data) { return toString.apply(data); } @@ -82,12 +88,39 @@ class ValueSyntax extends StableXmlMapper { return Collections.singletonList(curIndent + "data"); } - static ValueSyntax createNonDelimited(Function fromString) { - return new ValueSyntax<>(Objects::toString, fromString, false); + /** + * Creates a value syntax that cannot parse just any string, but + * which only applies the fromString parser if a precondition holds. + * The precondition is represented by a constraint on strings, and + * is documented as a constraint on the returned XML mapper. + */ + static ValueSyntax partialFunction(Function toString, + Function<@NonNull String, ? extends T> fromString, + PropertyConstraint checker) { + PropertyConstraint docConstraint = PropertyConstraint.fromPredicate( + PredicateUtil.always(), + checker.getConstraintDescription() + ); + + return new ValueSyntax<>( + toString, + s -> { + String error = checker.validate(s); + if (error != null) { + throw new IllegalArgumentException(error); + } + return fromString.apply(s); + }, + listOf(docConstraint) + ); } - static ValueSyntax createDelimited(Function toString, - Function fromString) { - return new ValueSyntax<>(toString, fromString, true); + static ValueSyntax withDefaultToString(Function fromString) { + return new ValueSyntax<>(Objects::toString, fromString, Collections.emptyList()); + } + + static ValueSyntax create(Function toString, + Function fromString) { + return new ValueSyntax<>(toString, fromString, Collections.emptyList()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index 9055d574cb..29d7746e93 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -10,6 +10,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -72,7 +73,7 @@ public abstract class XmlMapper { * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()} * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) */ - public T fromString(String attributeData) { + public T fromString(@NonNull String attributeData) { throw new UnsupportedOperationException("Check #supportsStringMapping()"); } @@ -82,6 +83,7 @@ public abstract class XmlMapper { * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()} * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) */ + @NonNull public String toString(T value) { throw new UnsupportedOperationException("Check #supportsStringMapping()"); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 2257373a27..68aebfdb02 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -5,8 +5,6 @@ package net.sourceforge.pmd.properties.xml; -import static net.sourceforge.pmd.util.CollectionUtil.listOf; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -17,10 +15,12 @@ import java.util.regex.Pattern; import java.util.stream.Collector; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; -import net.sourceforge.pmd.internal.util.PredicateUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.properties.xml.internal.XmlUtils; /** * This is internal API and shouldn't be used directly by clients. @@ -28,19 +28,21 @@ import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @InternalApi public final class XmlSyntaxUtils { - public static final ValueSyntax STRING = ValueSyntax.createNonDelimited(Function.identity()); - public static final ValueSyntax CHARACTER = ValueSyntax.createNonDelimited(value -> { - if (value == null || value.length() != 1) { - throw new IllegalArgumentException("missing/ambiguous character value for string \"" + value + "\""); - } - return value.charAt(0); - }); + public static final ValueSyntax STRING = ValueSyntax.withDefaultToString(Function.identity()); + public static final ValueSyntax CHARACTER = + ValueSyntax.partialFunction( + c -> Character.toString(c), + s -> s.charAt(0), + PropertyConstraint.fromPredicate( + s -> s.length() == 1, + "Should be exactly one character in length" + )); - public static final ValueSyntax REGEX = ValueSyntax.createNonDelimited(Pattern::compile); - public static final ValueSyntax INTEGER = ValueSyntax.createNonDelimited(Integer::valueOf); - public static final ValueSyntax LONG = ValueSyntax.createNonDelimited(Long::valueOf); - public static final ValueSyntax BOOLEAN = ValueSyntax.createNonDelimited(Boolean::valueOf); - public static final ValueSyntax DOUBLE = ValueSyntax.createNonDelimited(Double::valueOf); + public static final ValueSyntax REGEX = ValueSyntax.withDefaultToString(Pattern::compile); + public static final ValueSyntax INTEGER = ValueSyntax.withDefaultToString(Integer::valueOf); + public static final ValueSyntax LONG = ValueSyntax.withDefaultToString(Long::valueOf); + public static final ValueSyntax BOOLEAN = ValueSyntax.withDefaultToString(Boolean::valueOf); + public static final ValueSyntax DOUBLE = ValueSyntax.withDefaultToString(Double::valueOf); public static final XmlMapper> INTEGER_LIST = numberList(INTEGER); @@ -83,13 +85,22 @@ public final class XmlSyntaxUtils { return failures; } + @Nullable + public static String checkConstraintsJoin(T t, List> constraints) { + List failures = checkConstraints(t, constraints); + if (!failures.isEmpty()) { + return String.join(", ", failures); + } + return null; + } + public static void checkConstraintsThrow(T t, List> constraints, Function exceptionMaker) { - List failures = checkConstraints(t, constraints); - if (failures.isEmpty()) { - throw exceptionMaker.apply(String.join(", ", failures)); + String failures = checkConstraintsJoin(t, constraints); + if (failures != null) { + throw exceptionMaker.apply(failures); } } @@ -143,7 +154,7 @@ public final class XmlSyntaxUtils { Collector collector ) { String delim = "" + delimiter; - return ValueSyntax.createDelimited( + return ValueSyntax.create( coll -> IteratorUtil.stream(coll.iterator()).map(toString).collect(Collectors.joining(delim)), string -> parseListWithEscapes(string, delimiter, fromString).stream().collect(collector) ); @@ -206,24 +217,13 @@ public final class XmlSyntaxUtils { throw new IllegalArgumentException("Map may not contain entries with null values"); } - PropertyConstraint constraint = - PropertyConstraint.fromPredicate(PredicateUtil.always(), "Should be in set " + mappings.keySet()); - - return new ValueSyntax( + return ValueSyntax.partialFunction( reverseFun, - value -> { - if (!mappings.containsKey(value)) { - throw new IllegalArgumentException("Value is not in the set " + mappings.keySet()); - } - return mappings.get(value); - }, - false - ) { - @Override - public List> getConstraints() { - return listOf(constraint); - } - }; - + mappings::get, + PropertyConstraint.fromPredicate( + mappings::containsKey, + "Should be " + XmlUtils.formatPossibleNames(mappings.keySet()) + ) + ); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java index 785eb57353..db5831c34e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java @@ -19,6 +19,8 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; @@ -282,13 +284,14 @@ public final class CollectionUtil { * Produce a new list with the elements of the first, and one additional * element. The returned list may be unmodifiable. */ - public static List plus(List m, V v) { - if (m.isEmpty()) { + public static List plus(List list, V v) { + AssertionUtil.requireParamNotNull("list", list); + if (list.isEmpty()) { return Collections.singletonList(v); } - List vs = new ArrayList<>(m.size() + 1); - vs.addAll(m); + List vs = new ArrayList<>(list.size() + 1); + vs.addAll(list); vs.add(v); return vs; } @@ -298,6 +301,7 @@ public final class CollectionUtil { * mapping. The returned map may be unmodifiable. */ public static Map plus(Map m, K k, V v) { + AssertionUtil.requireParamNotNull("map", m); if (m instanceof PMap) { return ((PMap) m).plus(k, v); } @@ -309,6 +313,29 @@ public final class CollectionUtil { return newM; } + /** + * Returns the key that corresponds to the given value in the map, + * or null if it is not contained in the map. + * + * @param m Map + * @param v Value + * @param Type of keys + * @param Type of values + * + * @throws NullPointerException If the entry is found, but the key + * is null + * @throws NullPointerException If the map is null + */ + public static <@NonNull K, V> @Nullable K getKeyOfValue(Map m, V v) { + AssertionUtil.requireParamNotNull("map", m); + for (Entry it : m.entrySet()) { + if (it.getValue().equals(v)) { + return Objects.requireNonNull(it.getKey(), "This method uses null as a sentinel value"); + } + } + return null; + } + /** * Returns a map associating each key in the first list to its diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index b9b20f6954..64a991b3c3 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -8,7 +8,6 @@ import static java.util.Collections.emptyList; import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertEquals; @@ -298,7 +297,7 @@ public class PropertyDescriptorTest { .defaultValue(SampleEnum.B) .build(); thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Value was not in the set [TEST_A, TEST_B, TEST_C]"); + thrown.expectMessage("'InvalidEnumValue' should be one of 'TEST_A', 'TEST_B', 'TEST_C'"); descriptor.valueFrom("InvalidEnumValue"); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java index 7eb34d6520..40788ae22e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java @@ -16,7 +16,7 @@ public class NumericConstraintsTest { Assert.assertNull(constraint.validate(5)); Assert.assertNull(constraint.validate(10)); Assert.assertNotNull(constraint.validate(0)); - Assert.assertEquals("-1 should be between 1 and 10", constraint.validate(-1)); + Assert.assertEquals("'-1' should be between 1 and 10", constraint.validate(-1)); Assert.assertNotNull(constraint.validate(11)); Assert.assertNotNull(constraint.validate(100)); } @@ -41,7 +41,7 @@ public class NumericConstraintsTest { Assert.assertNull(constraint.validate(1.5d)); Assert.assertNull(constraint.validate(100)); Assert.assertNotNull(constraint.validate(0)); - Assert.assertEquals("0.1 should be positive", constraint.validate(0.1f)); + Assert.assertEquals("'0.1' should be positive", constraint.validate(0.1f)); Assert.assertNotNull(constraint.validate(0.9d)); Assert.assertNotNull(constraint.validate(-1)); Assert.assertNotNull(constraint.validate(-100)); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java index 83fa7d001a..f21cab9ddd 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java @@ -5,8 +5,6 @@ package net.sourceforge.pmd.lang.java.rule.documentation; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -28,6 +26,7 @@ import net.sourceforge.pmd.lang.java.multifile.signature.JavaOperationSignature; import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.util.CollectionUtil; /** @@ -262,48 +261,19 @@ public class CommentRequiredRule extends AbstractCommentRule { private enum CommentRequirement { Required("Required"), Ignored("Ignored"), Unwanted("Unwanted"); - private static final List LABELS = buildValueLabels(); - private static final Map MAPPINGS; private final String label; - static { - Map tmp = new HashMap<>(); - for (CommentRequirement r : values()) { - tmp.put(r.label, r); - } - MAPPINGS = Collections.unmodifiableMap(tmp); - } - CommentRequirement(String theLabel) { label = theLabel; } - - - private static List buildValueLabels() { - List labels = new ArrayList<>(values().length); - for (CommentRequirement r : values()) { - labels.add(r.label); - } - return Collections.unmodifiableList(labels); - } - - - public static List labels() { - return LABELS; - } - - - public static Map mappings() { - return MAPPINGS; - } } // pre-filled builder private static GenericPropertyBuilder requirementPropertyBuilder(String name, String commentType) { DESCRIPTOR_NAME_TO_COMMENT_TYPE.put(name, commentType); - return PropertyFactory.enumProperty(name, CommentRequirement.mappings()) - .desc(commentType + ". Possible values: " + CommentRequirement.labels()) - .defaultValue(CommentRequirement.Required); + return PropertyFactory.enumProperty(name, CommentRequirement.class, cr -> cr.label) + .desc(commentType + ". Possible values: " + CollectionUtil.map(CommentRequirement.values(), cr -> cr.label)) + .defaultValue(CommentRequirement.Required); } } From d0056bd4f1a274b52401a09d5902830dcf837a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 10 Apr 2020 00:22:32 +0200 Subject: [PATCH 048/347] Make error message naming scheme systematic --- .../properties/xml/ConstraintDecorator.java | 2 +- .../pmd/properties/xml/MapperSet.java | 2 +- .../pmd/properties/xml/SeqSyntax.java | 2 +- .../xml/internal/SchemaConstants.java | 2 +- .../xml/internal/XmlErrorMessages.java | 27 ++++++++++--------- .../pmd/properties/xml/internal/XmlUtils.java | 16 +++++------ .../sourceforge/pmd/rules/RuleFactory.java | 25 +++++++---------- .../sourceforge/pmd/RuleSetFactoryTest.java | 2 +- 8 files changed, 37 insertions(+), 41 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index 585e04404b..3e980db7e3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -44,7 +44,7 @@ class ConstraintDecorator extends XmlMapper { XmlSyntaxUtils.checkConstraintsThrow( t, constraints, - s -> err.error(element, XmlErrorMessages.CONSTRAINT_NOT_SATISFIED, s) + s -> err.error(element, XmlErrorMessages.ERR__CONSTRAINT_NOT_SATISFIED, s) ); return t; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 3d1573f5ec..6e3474de22 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -123,7 +123,7 @@ final class MapperSet extends XmlMapper { public T fromXml(Element element, XmlErrorReporter err) { XmlMapper syntax = readIndex.get(element.getTagName()); if (syntax == null) { - throw err.error(element, XmlErrorMessages.UNEXPECTED_ELEMENT, element.getTagName(), XmlUtils.formatPossibleNames(readIndex.keySet())); + throw err.error(element, XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, element.getTagName(), XmlUtils.formatPossibleNames(readIndex.keySet())); } else { return syntax.fromXml(element, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index c70cff479a..5dca9a5c73 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -46,7 +46,7 @@ final class SeqSyntax> extends StableXmlMapper { @Override public C fromXml(Element element, XmlErrorReporter err) { - RuntimeException aggregateEx = err.error(element, XmlErrorMessages.LIST_CONSTRAINT_NOT_SATISFIED); + RuntimeException aggregateEx = err.error(element, XmlErrorMessages.ERR__LIST_CONSTRAINT_NOT_SATISFIED); C result = XmlUtils.getElementChildren(element) .map(child -> { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java index b420ed291f..4c2be71a1b 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java @@ -55,7 +55,7 @@ public enum SchemaConstants { public String getAttributeOrThrow(Element element, XmlErrorReporter err) { String attribute = element.getAttribute(name); if (attribute == null) { - throw err.error(element, XmlErrorMessages.MISSING_REQUIRED_ATTRIBUTE, name); + throw err.error(element, XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); } return attribute; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java index 90377eec0e..f9ce6bb800 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java @@ -6,19 +6,22 @@ package net.sourceforge.pmd.properties.xml.internal; public final class XmlErrorMessages { - public static final String UNEXPECTED_ELEMENT = "Unexpected element '{0}', expecting {0}"; - public static final String MISSING_REQUIRED_ATTRIBUTE = "Required attribute '{0}' is missing"; - public static final String MISSING_REQUIRED_ELEMENT = "Required child element '{0}' is missing"; - public static final String MISSING_REQUIRED_ELEMENT_EITHER = "Required child element named {0} is missing"; - public static final String IGNORED_UNEXPECTED_CHILD_ELEMENT = "Unexpected element '{0}', expecting only '{1}', this will be ignored"; - public static final String IGNORED_DUPLICATE_CHILD_ELEMENT = "Expecting a single '{0}' child, this will be ignored"; + private static final String THIS_WILL_BE_IGNORED = ", this will be ignored"; - public static final String DUPLICATE_PROPERTY_SETTER = "Duplicate property tag for name '{0}', this will be ignored"; - public static final String PROPERTY_DOES_NOT_EXIST = "Cannot set non-existent property '{0}' on rule '{1}', known properties are {2}"; - public static final String PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE = "This property does not support the attribute syntax.\nUse a nested element, e.g. {1}"; - public static final String DEPRECATED_USE_OF_ATTRIBUTE = "The use of the '{0}' attribute is deprecated. Use a nested element, e.g. {1}"; - public static final String CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied: {0}"; - public static final String LIST_CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied on items"; + public static final String ERR__UNEXPECTED_ELEMENT = "Unexpected element ''{0}'', expecting {1}"; + public static final String ERR__MISSING_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is missing"; + public static final String ERR__MISSING_REQUIRED_ELEMENT = "Required child element named {0} is missing"; + + public static final String IGNORED__UNEXPECTED_ELEMENT = ERR__UNEXPECTED_ELEMENT + THIS_WILL_BE_IGNORED; + public static final String IGNORED__DUPLICATE_CHILD_ELEMENT = "Duplicated child with name ''{0}''" + THIS_WILL_BE_IGNORED; + public static final String IGNORED__DUPLICATE_PROPERTY_SETTER = "Duplicate property tag with name ''{0}''" + THIS_WILL_BE_IGNORED; + + public static final String ERR__UNSUPPORTED_VALUE_ATTRIBUTE = "This property does not support the attribute syntax.\nUse a nested element, e.g. {1}"; + public static final String ERR__PROPERTY_DOES_NOT_EXIST = "Cannot set non-existent property ''{0}'' on rule {1}"; + public static final String ERR__CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied: {0}"; + public static final String ERR__LIST_CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied on items"; + + public static final String WARN__DEPRECATED_USE_OF_ATTRIBUTE = "The use of the ''{0}'' attribute is deprecated. Use a nested element, e.g. {1}"; private XmlErrorMessages() { // utility class diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java index e1d275a9ab..e1128252e6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java @@ -4,6 +4,10 @@ package net.sourceforge.pmd.properties.xml.internal; +import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.ERR__MISSING_REQUIRED_ELEMENT; +import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.IGNORED__DUPLICATE_CHILD_ELEMENT; +import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.IGNORED__UNEXPECTED_ELEMENT; + import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -49,7 +53,7 @@ public final class XmlUtils { if (names.contains(it.getTagName())) { return it; } else { - err.warn(it, XmlErrorMessages.IGNORED_UNEXPECTED_CHILD_ELEMENT, it.getTagName(), formatPossibleNames(names)); + err.warn(it, IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), formatPossibleNames(names)); return null; } }).filter(Objects::nonNull); @@ -74,7 +78,7 @@ public final class XmlUtils { public static List getChildrenExpectSingleName(Element elt, String name, XmlErrorReporter err) { return XmlUtils.getElementChildren(elt).peek(it -> { if (!it.getTagName().equals(name)) { - err.warn(it, XmlErrorMessages.IGNORED_UNEXPECTED_CHILD_ELEMENT, it.getTagName(), name); + err.warn(it, IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), name); } }).collect(Collectors.toList()); } @@ -84,15 +88,11 @@ public final class XmlUtils { if (children.size() == 1) { return children.get(0); } else if (children.size() == 0) { - if (names.size() > 1) { - throw err.error(elt, XmlErrorMessages.MISSING_REQUIRED_ELEMENT_EITHER, formatPossibleNames(names)); - } else { - throw err.error(elt, XmlErrorMessages.MISSING_REQUIRED_ELEMENT, names.iterator().next()); - } + throw err.error(elt, ERR__MISSING_REQUIRED_ELEMENT, formatPossibleNames(names)); } else { for (int i = 1; i < children.size(); i++) { Element child = children.get(i); - err.warn(child, XmlErrorMessages.IGNORED_DUPLICATE_CHILD_ELEMENT, child.getTagName()); + err.warn(child, IGNORED__DUPLICATE_CHILD_ELEMENT, child.getTagName()); } return children.get(0); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 7b9d95f4ac..636f8016f3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -5,7 +5,10 @@ package net.sourceforge.pmd.rules; import static net.sourceforge.pmd.properties.xml.internal.SchemaConstants.PROPERTY_VALUE; -import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.formatPossibleNames; +import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST; +import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE; +import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER; +import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.WARN__DEPRECATED_USE_OF_ATTRIBUTE; import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.getSingleChildIn; import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.parseTextNode; @@ -16,7 +19,6 @@ import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -37,7 +39,6 @@ import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; import net.sourceforge.pmd.properties.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.internal.SchemaConstants; -import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.util.ResourceLoader; @@ -263,14 +264,14 @@ public class RuleFactory { for (Element element : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesElt, err)) { String name = SchemaConstants.NAME.getAttributeOrThrow(element, err); if (!overridden.add(name)) { - err.warn(element, XmlErrorMessages.DUPLICATE_PROPERTY_SETTER, name); + err.warn(element, IGNORED__DUPLICATE_PROPERTY_SETTER, name); continue; } PropertyDescriptor desc = rule.getPropertyDescriptor(name); if (desc == null) { - err.warn(element, XmlErrorMessages.PROPERTY_DOES_NOT_EXIST, name, rule.getName(), knownPropertiesOf(rule)); - continue; + // todo just warn and ignore + throw err.error(element, ERR__PROPERTY_DOES_NOT_EXIST, name, rule.getName()); } setRulePropertyCapture(rule, desc, element, err); } @@ -281,14 +282,6 @@ public class RuleFactory { rule.setProperty(descriptor, value); } - @Nullable - private String knownPropertiesOf(Rule rule) { - Set set = rule.getPropertyDescriptors().stream() - .map(PropertyDescriptor::name) - .collect(Collectors.toSet()); - return formatPossibleNames(set); - } - /** * Finds out if the property element defines a property. * @@ -348,7 +341,7 @@ public class RuleFactory { // the attribute syntax is deprecated. err.warn(attrNode, - XmlErrorMessages.DEPRECATED_USE_OF_ATTRIBUTE, + WARN__DEPRECATED_USE_OF_ATTRIBUTE, PROPERTY_VALUE.xmlName(), String.join("\nor\n", syntax.getExamples())); @@ -358,7 +351,7 @@ public class RuleFactory { throw err.error(attrNode, e); } catch (UnsupportedOperationException e) { throw err.error(attrNode, - XmlErrorMessages.PROPERTY_DOESNT_SUPPORT_VALUE_ATTRIBUTE, + ERR__UNSUPPORTED_VALUE_ATTRIBUTE, String.join("\nor\n", syntax.getExamples())); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index d2d5332458..d819dfa7bd 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -399,7 +399,7 @@ public class RuleSetFactoryTest { @Test public void testExternalReferenceOverrideNonExistent() throws RuleSetNotFoundException { ex.expect(IllegalArgumentException.class); - ex.expectMessage("Cannot set non-existent property 'test4' on Rule TestNameOverride"); + ex.expectMessage("Cannot set non-existent property 'test4' on rule TestNameOverride"); loadFirstRule(REF_OVERRIDE_NONEXISTENT); } From 3212845856f402580b347d140b7b6a9cb6b6214d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 10 Apr 2020 00:55:40 +0200 Subject: [PATCH 049/347] Fix tests --- .../properties/GenericPropertyDescriptor.java | 21 +++++++++++++++++++ .../pmd/properties/PropertyDescriptor.java | 9 ++++++++ .../pmd/renderers/CodeClimateRenderer.java | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index 1ec5a12597..b02f9cdb97 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.properties; +import java.util.Objects; + import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.properties.xml.XmlMapper; @@ -74,4 +76,23 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { public PropertyTypeId getTypeId() { return typeId; } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof PropertyDescriptor)) { + return false; + } + PropertyDescriptor that = (PropertyDescriptor) o; + return Objects.equals(name, that.name()) + && defaultValue.equals(that.defaultValue()); + } + + @Override + public int hashCode() { + return Objects.hash(name, defaultValue); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 6150e20704..493e7af9aa 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -121,4 +121,13 @@ public interface PropertyDescriptor { } + /** + * Property descriptors are equal if they have the same name and + * default value. In general, {@link XmlMapper}s are not equatable, + * so they are not taken into account in the comparison; descriptions + * are ignored. + */ + @Override + boolean equals(Object o); + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java index 302e10d6ea..86c0f7abf4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java @@ -161,7 +161,7 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer { if (propertyValue == null) { propertyValue = ""; } - propertyValue = propertyValue.replaceAll("(\n|\r\n|\r)", "\\\\n"); + propertyValue = propertyValue.replaceAll("\\R", "\\\\n"); result.append(propertyName).append(" | ").append(propertyValue).append(" | ").append(property.description()).append("\\n"); } From 84f96caeb54e25cbc7c62ebfa94cba81428ae36e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 10 Apr 2020 01:05:37 +0200 Subject: [PATCH 050/347] PMD + CStyle violations --- pmd-core/pmd-core-checkstyle-suppressions.xml | 1 + .../net/sourceforge/pmd/RuleSetWriter.java | 6 +---- .../util/xml}/SchemaConstants.java | 16 +++++------ .../util/xml}/XmlErrorMessages.java | 2 +- .../util}/xml/XmlErrorReporter.java | 6 ++++- .../util/xml/XmlUtil.java} | 17 ++++++------ .../pmd/properties/PropertyTypeId.java | 2 +- .../constraints/PropertyConstraint.java | 5 ++-- .../properties/xml/ConstraintDecorator.java | 3 ++- .../pmd/properties/xml/MapperSet.java | 7 ++--- .../pmd/properties/xml/OptionalSyntax.java | 1 + .../pmd/properties/xml/SeqSyntax.java | 27 ++++++++++--------- .../pmd/properties/xml/ValueSyntax.java | 1 + .../pmd/properties/xml/XmlMapper.java | 5 +++- .../pmd/properties/xml/XmlSyntaxUtils.java | 4 +-- .../sourceforge/pmd/rules/RuleFactory.java | 20 +++++++------- 16 files changed, 64 insertions(+), 59 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/{properties/xml/internal => internal/util/xml}/SchemaConstants.java (84%) rename pmd-core/src/main/java/net/sourceforge/pmd/{properties/xml/internal => internal/util/xml}/XmlErrorMessages.java (96%) rename pmd-core/src/main/java/net/sourceforge/pmd/{properties => internal/util}/xml/XmlErrorReporter.java (87%) rename pmd-core/src/main/java/net/sourceforge/pmd/{properties/xml/internal/XmlUtils.java => internal/util/xml/XmlUtil.java} (87%) diff --git a/pmd-core/pmd-core-checkstyle-suppressions.xml b/pmd-core/pmd-core-checkstyle-suppressions.xml index c1647b6530..eb502dc7ba 100644 --- a/pmd-core/pmd-core-checkstyle-suppressions.xml +++ b/pmd-core/pmd-core-checkstyle-suppressions.xml @@ -8,4 +8,5 @@ + \ No newline at end of file diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index ae55c07c3c..94079dbe3a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -30,12 +30,12 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; +import net.sourceforge.pmd.internal.util.xml.SchemaConstants; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.ImmutableLanguage; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.xml.internal.SchemaConstants; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.xml.XmlMapper; @@ -133,10 +133,6 @@ public class RuleSetWriter { return document.createElementNS(RULESET_2_0_0_NS_URI, name); } - private Element createPropertyDefaultElement() { - return document.createElementNS(RULESET_2_0_0_NS_URI, "default"); - } - private Element createExcludePatternElement(String excludePattern) { return createTextElement("exclude-pattern", excludePattern); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java similarity index 84% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java rename to pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java index 4c2be71a1b..b6a5fff3a8 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml.internal; +package net.sourceforge.pmd.internal.util.xml; import static net.sourceforge.pmd.util.CollectionUtil.setOf; @@ -14,8 +14,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; import org.w3c.dom.Element; -import net.sourceforge.pmd.properties.xml.XmlErrorReporter; - /** * Constants of the ruleset schema. @@ -36,7 +34,7 @@ public enum SchemaConstants { PROPERTIES("properties"), DEPRECATED("deprecated"), - ; + ; // SUPPRESS CHECKSTYLE enum trailing semi is awesome private final String name; @@ -73,17 +71,17 @@ public enum SchemaConstants { } public List getChildrenIn(Element elt) { - return XmlUtils.getElementChildrenNamed(elt, name) - .collect(Collectors.toList()); + return XmlUtil.getElementChildrenNamed(elt, name) + .collect(Collectors.toList()); } public List getElementChildrenNamedReportOthers(Element elt, XmlErrorReporter err) { - return XmlUtils.getElementChildrenNamedReportOthers(elt, setOf(name), err) - .collect(Collectors.toList()); + return XmlUtil.getElementChildrenNamedReportOthers(elt, setOf(name), err) + .collect(Collectors.toList()); } public Element getSingleChildIn(Element elt, XmlErrorReporter err) { - return XmlUtils.getSingleChildIn(elt, err, setOf(name)); + return XmlUtil.getSingleChildIn(elt, err, setOf(name)); } public void setOn(Element element, String value) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java similarity index 96% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java rename to pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java index f9ce6bb800..9016c49537 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml.internal; +package net.sourceforge.pmd.internal.util.xml; public final class XmlErrorMessages { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorReporter.java similarity index 87% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java rename to pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorReporter.java index 8f45849cc0..9a6f3bd106 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlErrorReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorReporter.java @@ -2,7 +2,11 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal.util.xml; import java.text.MessageFormat; import java.util.logging.Logger; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java similarity index 87% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java rename to pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java index e1128252e6..fbaa018956 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/internal/XmlUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java @@ -2,11 +2,11 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml.internal; +package net.sourceforge.pmd.internal.util.xml; -import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.ERR__MISSING_REQUIRED_ELEMENT; -import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.IGNORED__DUPLICATE_CHILD_ELEMENT; -import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.IGNORED__UNEXPECTED_ELEMENT; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__MISSING_REQUIRED_ELEMENT; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__DUPLICATE_CHILD_ELEMENT; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__UNEXPECTED_ELEMENT; import java.util.ArrayList; import java.util.List; @@ -20,12 +20,11 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import net.sourceforge.pmd.properties.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.xml.XmlMapper; -public final class XmlUtils { +public final class XmlUtil { - private XmlUtils() { + private XmlUtil() { } @@ -76,7 +75,7 @@ public final class XmlUtils { public static List getChildrenExpectSingleName(Element elt, String name, XmlErrorReporter err) { - return XmlUtils.getElementChildren(elt).peek(it -> { + return XmlUtil.getElementChildren(elt).peek(it -> { if (!it.getTagName().equals(name)) { err.warn(it, IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), name); } @@ -87,7 +86,7 @@ public final class XmlUtils { List children = getElementChildrenNamed(elt, names).collect(Collectors.toList()); if (children.size() == 1) { return children.get(0); - } else if (children.size() == 0) { + } else if (children.isEmpty()) { throw err.error(elt, ERR__MISSING_REQUIRED_ELEMENT, formatPossibleNames(names)); } else { for (int i = 1; i < children.size(); i++) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index b39ac6a1ca..9bc80251e6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -47,7 +47,7 @@ public enum PropertyTypeId { LONG_LIST("List[Long]", XmlSyntaxUtils.LONG_LIST, PropertyFactory::longIntListProperty), DOUBLE("Double", XmlSyntaxUtils.DOUBLE, PropertyFactory::doubleProperty), DOUBLE_LIST("List[Double]", XmlSyntaxUtils.DOUBLE_LIST, PropertyFactory::doubleListProperty), - ; + ; // SUPPRESS CHECKSTYLE enum trailing semi is awesome private static final Map CONSTANTS_BY_MNEMONIC; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java index a83b4e0e09..13a8ae59ce 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java @@ -79,13 +79,12 @@ public interface PropertyConstraint { * @return A collection validator */ default PropertyConstraint> toCollectionConstraint() { - final PropertyConstraint thisValidator = PropertyConstraint.this; return new PropertyConstraint>() { @Override public @Nullable String validate(Iterable value) { List errors = new ArrayList<>(); for (T t : value) { - String compValidation = thisValidator.validate(t); + String compValidation = PropertyConstraint.this.validate(t); if (compValidation != null) { errors.add(compValidation); } @@ -96,7 +95,7 @@ public interface PropertyConstraint { @Override public String getConstraintDescription() { - return "Components " + StringUtils.uncapitalize(thisValidator.getConstraintDescription()); + return "Components " + StringUtils.uncapitalize(PropertyConstraint.this.getConstraintDescription()); } }; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index 3e980db7e3..806940fcd0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -10,8 +10,9 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; +import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; import net.sourceforge.pmd.util.CollectionUtil; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 6e3474de22..202163c685 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -15,9 +15,10 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; +import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; +import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; -import net.sourceforge.pmd.properties.xml.internal.XmlUtils; import net.sourceforge.pmd.util.CollectionUtil; /** @@ -123,7 +124,7 @@ final class MapperSet extends XmlMapper { public T fromXml(Element element, XmlErrorReporter err) { XmlMapper syntax = readIndex.get(element.getTagName()); if (syntax == null) { - throw err.error(element, XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, element.getTagName(), XmlUtils.formatPossibleNames(readIndex.keySet())); + throw err.error(element, XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, element.getTagName(), XmlUtil.formatPossibleNames(readIndex.keySet())); } else { return syntax.fromXml(element, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index 02b833a699..d84e114439 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -13,6 +13,7 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index 5dca9a5c73..6aa96bc350 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -12,10 +12,11 @@ import java.util.stream.Collectors; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; +import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; +import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; -import net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages; -import net.sourceforge.pmd.properties.xml.internal.XmlUtils; /** * Serialize to and from a simple string. Examples: @@ -48,17 +49,17 @@ final class SeqSyntax> extends StableXmlMapper { public C fromXml(Element element, XmlErrorReporter err) { RuntimeException aggregateEx = err.error(element, XmlErrorMessages.ERR__LIST_CONSTRAINT_NOT_SATISFIED); - C result = XmlUtils.getElementChildren(element) - .map(child -> { - try { - return XmlUtils.expectElement(err, child, itemSyntax); - } catch (Exception e) { - aggregateEx.addSuppressed(e); - return null; - } - }) - .filter(Objects::nonNull) - .collect(collector); + C result = XmlUtil.getElementChildren(element) + .map(child -> { + try { + return XmlUtil.expectElement(err, child, itemSyntax); + } catch (Exception e) { + aggregateEx.addSuppressed(e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(collector); if (aggregateEx.getSuppressed().length > 0) { throw aggregateEx; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 79529c4e3e..0e56e36bf8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -15,6 +15,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.PredicateUtil; +import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index 29d7746e93..3f875402b0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -14,6 +14,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import org.w3c.dom.Node; +import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -26,7 +27,9 @@ import net.sourceforge.pmd.properties.constraints.PropertyConstraint; */ public abstract class XmlMapper { - /* package */ XmlMapper() { + XmlMapper() { + // package private, we want to control available mappers to + // put them into the ruleset schema } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 68aebfdb02..5a88f6ab63 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -19,8 +19,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; +import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.internal.XmlUtils; /** * This is internal API and shouldn't be used directly by clients. @@ -222,7 +222,7 @@ public final class XmlSyntaxUtils { mappings::get, PropertyConstraint.fromPredicate( mappings::containsKey, - "Should be " + XmlUtils.formatPossibleNames(mappings.keySet()) + "Should be " + XmlUtil.formatPossibleNames(mappings.keySet()) ) ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 636f8016f3..93cf67acf1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -4,13 +4,13 @@ package net.sourceforge.pmd.rules; -import static net.sourceforge.pmd.properties.xml.internal.SchemaConstants.PROPERTY_VALUE; -import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST; -import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE; -import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER; -import static net.sourceforge.pmd.properties.xml.internal.XmlErrorMessages.WARN__DEPRECATED_USE_OF_ATTRIBUTE; -import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.getSingleChildIn; -import static net.sourceforge.pmd.properties.xml.internal.XmlUtils.parseTextNode; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PROPERTY_VALUE; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.WARN__DEPRECATED_USE_OF_ATTRIBUTE; +import static net.sourceforge.pmd.internal.util.xml.XmlUtil.getSingleChildIn; +import static net.sourceforge.pmd.internal.util.xml.XmlUtil.parseTextNode; import java.util.Arrays; import java.util.Collections; @@ -31,14 +31,14 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSetReference; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.internal.util.xml.SchemaConstants; +import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; -import net.sourceforge.pmd.properties.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.xml.XmlMapper; -import net.sourceforge.pmd.properties.xml.internal.SchemaConstants; import net.sourceforge.pmd.util.ResourceLoader; @@ -368,6 +368,6 @@ public class RuleFactory { private static XmlErrorReporter dummyErrorReporter() { // TODO this is a fake instance, should be provided by context // I'm only doing this to not make the change too contagious for now - return new XmlErrorReporter() {}; + return new XmlErrorReporter() { }; } } From d4ac25deb2626bf97af6e6b2b3afd024782a4607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Apr 2020 02:35:29 +0200 Subject: [PATCH 051/347] Refactor the ruleset factory to use the message handler --- pmd-core/pom.xml | 5 + .../net/sourceforge/pmd/RuleSetFactory.java | 252 ++++++++++-------- .../internal/util/xml/SchemaConstants.java | 2 + .../internal/util/xml/XmlErrorMessages.java | 1 + .../internal/util/xml/XmlErrorReporter.java | 38 --- .../pmd/internal/util/xml/XmlUtil.java | 2 + .../properties/xml/ConstraintDecorator.java | 3 +- .../pmd/properties/xml/MapperSet.java | 3 +- .../pmd/properties/xml/OptionalSyntax.java | 3 +- .../pmd/properties/xml/SeqSyntax.java | 3 +- .../pmd/properties/xml/ValueSyntax.java | 3 +- .../pmd/properties/xml/XmlMapper.java | 3 +- .../sourceforge/pmd/rules/RuleBuilder.java | 2 +- .../sourceforge/pmd/rules/RuleFactory.java | 149 ++++------- 14 files changed, 224 insertions(+), 245 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorReporter.java diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 1cdde6d0c6..ba783942f3 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -114,6 +114,11 @@ org.pcollections pcollections + + com.github.oowekyala.ooxml + nice-xml-messages + 1.0-SNAPSHOT + com.github.tomakehurst diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index e0d5fd9dbb..428704cfac 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd; import java.io.IOException; import java.io.InputStream; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -26,12 +27,12 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; -import org.xml.sax.SAXException; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; import net.sourceforge.pmd.lang.Language; @@ -42,6 +43,15 @@ import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.rules.RuleFactory; import net.sourceforge.pmd.util.ResourceLoader; +import com.github.oowekyala.ooxml.DomUtils; +import com.github.oowekyala.ooxml.messages.AccumulatingErrorReporter; +import com.github.oowekyala.ooxml.messages.LoggerMessageHandler; +import com.github.oowekyala.ooxml.messages.PositionedXmlDoc; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; +import com.github.oowekyala.ooxml.messages.XmlException; +import com.github.oowekyala.ooxml.messages.XmlException.Severity; +import com.github.oowekyala.ooxml.messages.XmlMessageUtils; + /** * RuleSetFactory is responsible for creating RuleSet instances from XML * content. By default Rules will be loaded using the {@link RulePriority#LOW} priority, @@ -230,7 +240,7 @@ public class RuleSetFactory { private RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException { - return parseRuleSetNode(ruleSetReferenceId, withDeprecatedRuleReferences); + return readDocument(ruleSetReferenceId, withDeprecatedRuleReferences); } /** @@ -358,21 +368,21 @@ public class RuleSetFactory { /** * Parse a ruleset node to construct a RuleSet. * - * @param ruleSetReferenceId - * The RuleSetReferenceId of the RuleSet being parsed. - * @param withDeprecatedRuleReferences - * whether rule references that are deprecated should be ignored - * or not + * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed. + * @param withDeprecatedRuleReferences whether rule references that are deprecated should be ignored + * or not + * * @return The new RuleSet. */ - private RuleSet parseRuleSetNode(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) - throws RuleSetNotFoundException { - try (CheckedInputStream inputStream = new CheckedInputStream( - ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { + private RuleSet readDocument(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) + throws RuleSetNotFoundException { + try (CheckedInputStream inputStream = new CheckedInputStream(ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { if (!ruleSetReferenceId.isExternal()) { throw new IllegalArgumentException( - "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); + "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); } + + LoggerMessageHandler handler = new LoggerMessageHandler(LOG, false); DocumentBuilder builder = createDocumentBuilder(); InputSource inputSource; if (compatibilityFilter != null) { @@ -380,73 +390,102 @@ public class RuleSetFactory { } else { inputSource = new InputSource(inputStream); } - Document document = builder.parse(inputSource); - Element ruleSetElement = document.getDocumentElement(); + inputSource.setSystemId(ruleSetReferenceId.getRuleSetFileName()); - RuleSetBuilder ruleSetBuilder = new RuleSetBuilder(inputStream.getChecksum().getValue()) - .withFileName(ruleSetReferenceId.getRuleSetFileName()); + PositionedXmlDoc parsed = XmlMessageUtils.getInstance().parse(builder, inputSource, handler); - if (ruleSetElement.hasAttribute("name")) { - ruleSetBuilder.withName(ruleSetElement.getAttribute("name")); - } else { - LOG.warning("RuleSet name is missing. Future versions of PMD will require it."); - ruleSetBuilder.withName("Missing RuleSet Name"); + AccumulatingErrorReporter err = makeReporter(handler, parsed); + try { + RuleSetBuilder ruleSetBuilder = new RuleSetBuilder(inputStream.getChecksum().getValue()).withFileName(ruleSetReferenceId.getRuleSetFileName()); + + RuleSet ruleSet = parseRulesetNode(ruleSetReferenceId, withDeprecatedRuleReferences, parsed, ruleSetBuilder, err); + err.close(Severity.WARNING, Severity.ERROR); + return ruleSet; + } catch (XmlException e) { + err.close(e.getSeverity(), Severity.ERROR); + throw e; } - - Set rulesetReferences = new HashSet<>(); - - NodeList nodeList = ruleSetElement.getChildNodes(); - for (int i = 0; i < nodeList.getLength(); i++) { - Node node = nodeList.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - String nodeName = node.getNodeName(); - String text = parseTextNode(node); - if (DESCRIPTION.equals(nodeName)) { - ruleSetBuilder.withDescription(text); - } else if ("include-pattern".equals(nodeName)) { - final Pattern pattern = parseRegex(text); - if (pattern == null) { - continue; - } - ruleSetBuilder.withFileInclusions(pattern); - } else if ("exclude-pattern".equals(nodeName)) { - final Pattern pattern = parseRegex(text); - if (pattern == null) { - continue; - } - ruleSetBuilder.withFileExclusions(pattern); - } else if ("rule".equals(nodeName)) { - parseRuleNode(ruleSetReferenceId, ruleSetBuilder, node, withDeprecatedRuleReferences, rulesetReferences); - } else { - throw new IllegalArgumentException(UNEXPECTED_ELEMENT + node.getNodeName() - + "> encountered as child of element."); - } - } + } catch (ParserConfigurationException | IOException | XmlException ex) { + if (!(ex instanceof XmlException)) { // would already have been reported + ex.printStackTrace(); } - - if (!ruleSetBuilder.hasDescription()) { - LOG.warning("RuleSet description is missing. Future versions of PMD will require it."); - ruleSetBuilder.withDescription("Missing description"); - } - - ruleSetBuilder.filterRulesByPriority(minimumPriority); - - return ruleSetBuilder.build(); - } catch (ReflectiveOperationException ex) { - ex.printStackTrace(); - throw new RuntimeException("Couldn't find the class " + ex.getMessage(), ex); - } catch (ParserConfigurationException | IOException | SAXException ex) { - ex.printStackTrace(); - throw new RuntimeException("Couldn't read the ruleset " + ruleSetReferenceId + ": " + ex.getMessage(), ex); + throw new RuntimeException("Couldn't read the ruleset " + ruleSetReferenceId, ex); } } - private Pattern parseRegex(String text) { + @NonNull + private AccumulatingErrorReporter makeReporter(LoggerMessageHandler handler, PositionedXmlDoc parsed) { + return new AccumulatingErrorReporter(handler, parsed.getPositioner(), Severity.WARNING) { + @Override + protected String template(String message, Object... args) { + return MessageFormat.format(message, args); + } + }; + } + + private RuleSet parseRulesetNode(RuleSetReferenceId ruleSetReferenceId, + boolean withDeprecatedRuleReferences, + PositionedXmlDoc parsed, + RuleSetBuilder builder, + XmlErrorReporter err) throws RuleSetNotFoundException { + Element ruleSetElement = parsed.getDocument().getDocumentElement(); + + if (ruleSetElement.hasAttribute("name")) { + builder.withName(ruleSetElement.getAttribute("name")); + } else { + err.warn(ruleSetElement, "RuleSet name is missing. Future versions of PMD will require it."); + builder.withName("Missing RuleSet Name"); + } + + Set rulesetReferences = new HashSet<>(); + + for (Element node : DomUtils.elementsIn(ruleSetElement)) { + String nodeName = node.getNodeName(); + String text = parseTextNode(node); + switch (nodeName) { + case DESCRIPTION: + builder.withDescription(text); + break; + case "include-pattern": { + final Pattern pattern = parseRegex(node, text, err); + if (pattern == null) { + continue; + } + builder.withFileInclusions(pattern); + break; + } + case "exclude-pattern": { + final Pattern pattern = parseRegex(node, text, err); + if (pattern == null) { + continue; + } + builder.withFileExclusions(pattern); + break; + } + case "rule": + parseRuleNode(ruleSetReferenceId, builder, node, withDeprecatedRuleReferences, rulesetReferences, err); + break; + default: + throw err.error(node, "Unexpected element as child of "); + } + } + + if (!builder.hasDescription()) { + err.warn(ruleSetElement, "RuleSet description is missing. Future versions of PMD will require it."); + builder.withDescription("Missing description"); + } + + builder.filterRulesByPriority(minimumPriority); + + return builder.build(); + } + + private Pattern parseRegex(Element node, String text, XmlErrorReporter err) { final Pattern pattern; try { pattern = Pattern.compile(text); } catch (PatternSyntaxException pse) { - LOG.warning(pse.getMessage()); + err.error(node, pse); return null; } return pattern; @@ -493,28 +532,28 @@ public class RuleSetFactory { /** * Parse a rule node. * - * @param ruleSetReferenceId - * The RuleSetReferenceId of the RuleSet being parsed. - * @param ruleSetBuilder - * The RuleSet being constructed. - * @param ruleNode - * Must be a rule element node. - * @param withDeprecatedRuleReferences - * whether rule references that are deprecated should be ignored - * or not - * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning + * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed. + * @param ruleSetBuilder The RuleSet being constructed. + * @param ruleNode Must be a rule element node. + * @param withDeprecatedRuleReferences whether rule references that are deprecated should be ignored + * or not + * @param rulesetReferences keeps track of already processed complete ruleset references in order to log + * a warning */ - private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode, - boolean withDeprecatedRuleReferences, Set rulesetReferences) - throws ClassNotFoundException, InstantiationException, IllegalAccessException, RuleSetNotFoundException { + private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, + RuleSetBuilder ruleSetBuilder, + Node ruleNode, + boolean withDeprecatedRuleReferences, + Set rulesetReferences, + XmlErrorReporter err) throws RuleSetNotFoundException { Element ruleElement = (Element) ruleNode; String ref = ruleElement.getAttribute("ref"); if (ref.endsWith("xml")) { parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref, rulesetReferences); } else if (StringUtils.isBlank(ref)) { - parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode); + parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, err); } else { - parseRuleReferenceNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, ref, withDeprecatedRuleReferences); + parseRuleReferenceNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, ref, withDeprecatedRuleReferences, err); } } @@ -606,15 +645,15 @@ public class RuleSetFactory { * Parse a rule node as a single Rule. The Rule has been fully defined * within the context of the current RuleSet. * - * @param ruleSetReferenceId - * The RuleSetReferenceId of the RuleSet being parsed. - * @param ruleSetBuilder - * The RuleSet being constructed. - * @param ruleNode - * Must be a rule element node. + * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed. + * @param ruleSetBuilder The RuleSet being constructed. + * @param ruleNode Must be a rule element node. + * @param err Error reporter */ - private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, + RuleSetBuilder ruleSetBuilder, + Node ruleNode, + XmlErrorReporter err) { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not @@ -623,7 +662,7 @@ public class RuleSetFactory { && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { return; } - Rule rule = new RuleFactory(resourceLoader).buildRule(ruleElement); + Rule rule = new RuleFactory(resourceLoader).buildRule(ruleElement, err); rule.setRuleSetName(ruleSetBuilder.getName()); if (warnDeprecated && StringUtils.isBlank(ruleElement.getAttribute("language"))) { @@ -641,26 +680,25 @@ public class RuleSetFactory { * which comes from another RuleSet with some of it's attributes potentially * overridden. * - * @param ruleSetReferenceId - * The RuleSetReferenceId of the RuleSet being parsed. - * @param ruleSetBuilder - * The RuleSet being constructed. - * @param ruleNode - * Must be a rule element node. - * @param ref - * A reference to a Rule. - * @param withDeprecatedRuleReferences - * whether rule references that are deprecated should be ignored - * or not + * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed. + * @param ruleSetBuilder The RuleSet being constructed. + * @param ruleNode Must be a rule element node. + * @param ref A reference to a Rule. + * @param withDeprecatedRuleReferences whether rule references that are deprecated should be ignored + * @param err Error reporter */ - private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode, String ref, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException { + private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, + RuleSetBuilder ruleSetBuilder, + Node ruleNode, + String ref, + boolean withDeprecatedRuleReferences, + XmlErrorReporter err) throws RuleSetNotFoundException { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not // it. if (StringUtils.isNotBlank(ruleSetReferenceId.getRuleName()) - && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { + && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { return; } @@ -716,7 +754,7 @@ public class RuleSetFactory { RuleSetReference ruleSetReference = new RuleSetReference(otherRuleSetReferenceId.getRuleSetFileName(), false); - RuleReference ruleReference = new RuleFactory(resourceLoader).decorateRule(referencedRule, ruleSetReference, ruleElement); + RuleReference ruleReference = new RuleFactory(resourceLoader).decorateRule(referencedRule, ruleSetReference, ruleElement, err); if (warnDeprecated && ruleReference.isDeprecated() && !isSameRuleSet) { if (LOG.isLoggable(Level.WARNING)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java index b6a5fff3a8..0eed1c8f02 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java @@ -14,6 +14,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; import org.w3c.dom.Element; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * Constants of the ruleset schema. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java index 9016c49537..5227dbdf8f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java @@ -9,6 +9,7 @@ public final class XmlErrorMessages { private static final String THIS_WILL_BE_IGNORED = ", this will be ignored"; public static final String ERR__UNEXPECTED_ELEMENT = "Unexpected element ''{0}'', expecting {1}"; + public static final String ERR__UNEXPECTED_ELEMENT_IN = "Unexpected element ''{0}'' in {1}, expecting {1}"; public static final String ERR__MISSING_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is missing"; public static final String ERR__MISSING_REQUIRED_ELEMENT = "Required child element named {0} is missing"; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorReporter.java deleted file mode 100644 index 9a6f3bd106..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorReporter.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal.util.xml; - -import java.text.MessageFormat; -import java.util.logging.Logger; - -/** - * Reports errors in an XML document. Implementations have a way to - * associate nodes with their location in the document. - * - * TODO this is a placeholder for now, I need to publish the impl to maven - */ -public interface XmlErrorReporter { - - Logger LOGGER = Logger.getLogger(XmlErrorReporter.class.getName()); - - default void warn(org.w3c.dom.Node node, String message, Object... args) { - LOGGER.warning(MessageFormat.format(message, args)); - } - - - default RuntimeException error(org.w3c.dom.Node node, String message, Object... args) { - return new IllegalArgumentException(MessageFormat.format(message, args)); - } - - - default RuntimeException error(org.w3c.dom.Node node, Throwable ex) { - return new IllegalArgumentException(ex); - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java index fbaa018956..a928c4c948 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java @@ -22,6 +22,8 @@ import org.w3c.dom.NodeList; import net.sourceforge.pmd.properties.xml.XmlMapper; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + public final class XmlUtil { private XmlUtil() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index 806940fcd0..3a32e9eafd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -11,10 +11,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; -import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * Decorates an XmlMapper with some {@link PropertyConstraint}s. * Those are checked when the value is parsed. This is used to diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 202163c685..977fd80e34 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -16,11 +16,12 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; -import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * A set of syntaxes for read and write. One special syntax is designated * as the one used to write elements, the others are used to read. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index d84e114439..6059fb257c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -13,10 +13,11 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * Serialize an optional value. If the value is itself an {@code Optional}, * then mentioning {@code } will yield a toplevel empty optional. So diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index 6aa96bc350..5047372187 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -13,11 +13,12 @@ import java.util.stream.Collectors; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; -import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * Serialize to and from a simple string. Examples: * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 0e56e36bf8..cc01bb7cf0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -15,10 +15,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.PredicateUtil; -import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * Serialize to and from a simple string. Examples: * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index 3f875402b0..90eba14748 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -14,10 +14,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import org.w3c.dom.Node; -import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * Strategy to serialize a value to and from XML. Some strategies support diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java index 47313a12f7..7febb8723d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java @@ -24,7 +24,7 @@ import net.sourceforge.pmd.util.ResourceLoader; /** * Builds a rule, validating its parameters throughout. The builder can define property descriptors, but not override - * them. For that, use {@link RuleFactory#decorateRule(Rule, RuleSetReference, Element)}. + * them. For that, use {@link RuleFactory#decorateRule(Rule, RuleSetReference, Element, com.github.oowekyala.ooxml.messages.XmlErrorReporter)}. * * @author Clรฉment Fournier * @since 6.0.0 diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 93cf67acf1..f44ff8a543 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -17,22 +17,18 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSetReference; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.xml.SchemaConstants; -import net.sourceforge.pmd.internal.util.xml.XmlErrorReporter; +import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -41,6 +37,9 @@ import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.util.ResourceLoader; +import com.github.oowekyala.ooxml.DomUtils; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + /** * Builds rules from rule XML nodes. @@ -52,8 +51,6 @@ import net.sourceforge.pmd.util.ResourceLoader; @Deprecated public class RuleFactory { - private static final Logger LOG = Logger.getLogger(RuleFactory.class.getName()); - private static final String DEPRECATED = "deprecated"; private static final String NAME = "name"; private static final String MESSAGE = "message"; @@ -93,49 +90,39 @@ public class RuleFactory { *

Declaring a property in the overriding element throws an exception (the property must exist in the referenced * rule). * - * @param referencedRule Referenced rule + * @param referencedRule Referenced rule * @param ruleSetReference the ruleset, where the referenced rule is defined - * @param ruleElement Element overriding some metadata about the rule + * @param ruleElement Element overriding some metadata about the rule + * @param err Error reporter * * @return A rule reference to the referenced rule */ - public RuleReference decorateRule(Rule referencedRule, RuleSetReference ruleSetReference, Element ruleElement) { + public RuleReference decorateRule(Rule referencedRule, RuleSetReference ruleSetReference, Element ruleElement, XmlErrorReporter err) { RuleReference ruleReference = new RuleReference(referencedRule, ruleSetReference); - if (ruleElement.hasAttribute(DEPRECATED)) { - ruleReference.setDeprecated(Boolean.parseBoolean(ruleElement.getAttribute(DEPRECATED))); - } - if (ruleElement.hasAttribute(NAME)) { - ruleReference.setName(ruleElement.getAttribute(NAME)); - } - if (ruleElement.hasAttribute(MESSAGE)) { - ruleReference.setMessage(ruleElement.getAttribute(MESSAGE)); - } - if (ruleElement.hasAttribute(EXTERNAL_INFO_URL)) { - ruleReference.setExternalInfoUrl(ruleElement.getAttribute(EXTERNAL_INFO_URL)); - } + DomUtils.getAttributeOpt(ruleElement, DEPRECATED).map(Boolean::parseBoolean).ifPresent(ruleReference::setDeprecated); + DomUtils.getAttributeOpt(ruleElement, NAME).ifPresent(ruleReference::setName); + DomUtils.getAttributeOpt(ruleElement, MESSAGE).ifPresent(ruleReference::setMessage); + DomUtils.getAttributeOpt(ruleElement, EXTERNAL_INFO_URL).ifPresent(ruleReference::setExternalInfoUrl); - for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) { - Node node = ruleElement.getChildNodes().item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - switch (node.getNodeName()) { - case DESCRIPTION: - ruleReference.setDescription(parseTextNode(node)); - break; - case EXAMPLE: - ruleReference.addExample(parseTextNode(node)); - break; - case PRIORITY: - ruleReference.setPriority(RulePriority.valueOf(Integer.parseInt(parseTextNode(node)))); - break; - case PROPERTIES: - setPropertyValues(ruleReference, (Element) node, dummyErrorReporter()); - break; - default: - throw new IllegalArgumentException("Unexpected element <" + node.getNodeName() - + "> encountered as child of element for Rule " - + ruleReference.getName()); - } + for (Element node : DomUtils.elementsIn(ruleElement)) { + switch (node.getNodeName()) { + case DESCRIPTION: + ruleReference.setDescription(parseTextNode(node)); + break; + case EXAMPLE: + ruleReference.addExample(parseTextNode(node)); + break; + case PRIORITY: + ruleReference.setPriority(RulePriority.valueOf(Integer.parseInt(parseTextNode(node)))); + break; + case PROPERTIES: + setPropertyValues(ruleReference, node, err); + break; + default: + throw err.error(node, + XmlErrorMessages.ERR__UNEXPECTED_ELEMENT_IN, + "rule " + ruleReference.getName()); } } @@ -151,29 +138,22 @@ public class RuleFactory { * @param ruleElement The rule element to parse * * @return A new instance of the rule described by this element + * * @throws IllegalArgumentException if the element doesn't describe a valid rule. */ - public Rule buildRule(Element ruleElement) { + public Rule buildRule(Element ruleElement, XmlErrorReporter err) { checkRequiredAttributesArePresent(ruleElement); - String name = ruleElement.getAttribute(NAME); + RuleBuilder builder = new RuleBuilder( + ruleElement.getAttribute(NAME), + resourceLoader, + ruleElement.getAttribute(CLASS), + ruleElement.getAttribute("language") + ); - RuleBuilder builder = new RuleBuilder(name, - resourceLoader, - ruleElement.getAttribute(CLASS), - ruleElement.getAttribute("language")); - - if (ruleElement.hasAttribute(MINIMUM_LANGUAGE_VERSION)) { - builder.minimumLanguageVersion(ruleElement.getAttribute(MINIMUM_LANGUAGE_VERSION)); - } - - if (ruleElement.hasAttribute(MAXIMUM_LANGUAGE_VERSION)) { - builder.maximumLanguageVersion(ruleElement.getAttribute(MAXIMUM_LANGUAGE_VERSION)); - } - - if (ruleElement.hasAttribute(SINCE)) { - builder.since(ruleElement.getAttribute(SINCE)); - } + DomUtils.getAttributeOpt(ruleElement, MINIMUM_LANGUAGE_VERSION).ifPresent(builder::minimumLanguageVersion); + DomUtils.getAttributeOpt(ruleElement, MAXIMUM_LANGUAGE_VERSION).ifPresent(builder::maximumLanguageVersion); + DomUtils.getAttributeOpt(ruleElement, SINCE).ifPresent(builder::since); builder.message(ruleElement.getAttribute(MESSAGE)); builder.externalInfoUrl(ruleElement.getAttribute(EXTERNAL_INFO_URL)); @@ -181,13 +161,8 @@ public class RuleFactory { Element propertiesElement = null; - final NodeList nodeList = ruleElement.getChildNodes(); - for (int i = 0; i < nodeList.getLength(); i++) { - Node node = nodeList.item(i); - if (node.getNodeType() != Node.ELEMENT_NODE) { - continue; - } + for (Element node : DomUtils.elementsIn(ruleElement)) { switch (node.getNodeName()) { case DESCRIPTION: builder.description(parseTextNode(node)); @@ -199,13 +174,13 @@ public class RuleFactory { builder.priority(Integer.parseInt(parseTextNode(node).trim())); break; case PROPERTIES: - parsePropertiesForDefinitions(builder, node); - propertiesElement = (Element) node; + parsePropertiesForDefinitions(builder, node, err); + propertiesElement = node; break; default: - throw new IllegalArgumentException("Unexpected element <" + node.getNodeName() - + "> encountered as child of element for Rule " - + name); + throw err.error(node, + XmlErrorMessages.ERR__UNEXPECTED_ELEMENT_IN, + "rule " + ruleElement.getAttribute(NAME)); } } @@ -213,12 +188,11 @@ public class RuleFactory { try { rule = builder.build(); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - LOG.log(Level.SEVERE, "Error instantiating a rule", e); - throw new RuntimeException(e); + throw err.fatal(ruleElement, e); } if (propertiesElement != null) { - setPropertyValues(rule, propertiesElement, dummyErrorReporter()); + setPropertyValues(rule, propertiesElement, err); } return rule; @@ -240,14 +214,12 @@ public class RuleFactory { * * @param builder Rule builder * @param propertiesNode Node to parse + * @param err Error reporter */ - private void parsePropertiesForDefinitions(RuleBuilder builder, Node propertiesNode) { - for (int i = 0; i < propertiesNode.getChildNodes().getLength(); i++) { - Node node = propertiesNode.getChildNodes().item(i); - if (node.getNodeType() == Node.ELEMENT_NODE && PROPERTY.equals(node.getNodeName()) - && isPropertyDefinition((Element) node)) { - PropertyDescriptor descriptor = parsePropertyDefinition((Element) node); - builder.defineProperty(descriptor); + private void parsePropertiesForDefinitions(RuleBuilder builder, Element propertiesNode, @NonNull XmlErrorReporter err) { + for (Element child : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesNode, err)) { + if (isPropertyDefinition(child)) { + builder.defineProperty(parsePropertyDefinition(child, err)); } } } @@ -297,11 +269,11 @@ public class RuleFactory { * Parses a property definition node and returns the defined property descriptor. * * @param propertyElement Property node to parse + * @param err Error reporter * * @return The property descriptor */ - private static PropertyDescriptor parsePropertyDefinition(Element propertyElement) { - XmlErrorReporter err = dummyErrorReporter(); + private static PropertyDescriptor parsePropertyDefinition(Element propertyElement, XmlErrorReporter err) { String typeId = SchemaConstants.TYPE.getAttributeOrThrow(propertyElement, err); @@ -361,13 +333,4 @@ public class RuleFactory { return syntax.fromXml(child, err); } } - - - @Deprecated - @NonNull - private static XmlErrorReporter dummyErrorReporter() { - // TODO this is a fake instance, should be provided by context - // I'm only doing this to not make the change too contagious for now - return new XmlErrorReporter() { }; - } } From 64a90fe3512fcc2a088d3227bc7eef103766acb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 14 Jun 2020 19:14:22 +0200 Subject: [PATCH 052/347] Fix rebase --- .../net/sourceforge/pmd/RuleSetFactory.java | 15 ++++++++++++++- .../pmd/properties/PropertyDescriptor.java | 6 ++---- .../pmd/renderers/HTMLRenderer.java | 5 ++--- .../net/sourceforge/pmd/rules/RuleFactory.java | 11 +++++------ .../sourceforge/pmd/RuleSetFactoryTest.java | 18 +++++++++++++----- .../pmd/renderers/HTMLRendererTest.java | 5 +++-- .../pmd/renderers/SummaryHTMLRendererTest.java | 3 ++- 7 files changed, 41 insertions(+), 22 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 428704cfac..a509d3251a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -373,6 +373,8 @@ public class RuleSetFactory { * or not * * @return The new RuleSet. + * + * @throws RulesetParseException If the ruleset cannot be parsed (eg IO exception, malformed XML, validation errors) */ private RuleSet readDocument(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException { @@ -409,7 +411,18 @@ public class RuleSetFactory { if (!(ex instanceof XmlException)) { // would already have been reported ex.printStackTrace(); } - throw new RuntimeException("Couldn't read the ruleset " + ruleSetReferenceId, ex); + throw new RulesetParseException("Couldn't read the ruleset " + ruleSetReferenceId, ex); + } + } + + static class RulesetParseException extends RuntimeException { + + public RulesetParseException(String message, Throwable cause) { + super(message, cause); + } + + public RulesetParseException(Throwable cause) { + super(cause); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 493e7af9aa..563255d1b8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -122,10 +122,8 @@ public interface PropertyDescriptor { /** - * Property descriptors are equal if they have the same name and - * default value. In general, {@link XmlMapper}s are not equatable, - * so they are not taken into account in the comparison; descriptions - * are ignored. + * Property descriptors are equal if they have the same name, to be + * used in a map. Other attributes are ignored. */ @Override boolean equals(Object o); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java index d50f25d76a..8eca23164d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java @@ -20,7 +20,6 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.properties.StringProperty; /** * Renderer to basic HTML format. @@ -32,9 +31,9 @@ public class HTMLRenderer extends AbstractIncrementingRenderer { public static final String NAME = "html"; - // TODO use PropertyDescriptor> : we need a "blank" default value public static final PropertyDescriptor> LINE_PREFIX = - PropertyFactory.stringProperty("linePrefix").desc("Prefix for line number anchor in the source file.") + PropertyFactory.stringProperty("linePrefix") + .desc("Prefix for line number anchor in the source file.") .toOptional() .defaultValue(Optional.empty()) .build(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index f44ff8a543..8795d52a01 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -8,7 +8,6 @@ import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PROPERTY_VAL import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST; import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE; import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.WARN__DEPRECATED_USE_OF_ATTRIBUTE; import static net.sourceforge.pmd.internal.util.xml.XmlUtil.getSingleChildIn; import static net.sourceforge.pmd.internal.util.xml.XmlUtil.parseTextNode; @@ -311,11 +310,11 @@ public class RuleFactory { if (defaultAttr != null) { Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElt); - // the attribute syntax is deprecated. - err.warn(attrNode, - WARN__DEPRECATED_USE_OF_ATTRIBUTE, - PROPERTY_VALUE.xmlName(), - String.join("\nor\n", syntax.getExamples())); + // the attribute syntax could be deprecated. + // err.warn(attrNode, + // WARN__DEPRECATED_USE_OF_ATTRIBUTE, + // PROPERTY_VALUE.xmlName(), + // String.join("\nor\n", syntax.getExamples())); try { return syntax.fromString(defaultAttr); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index d819dfa7bd..c41e9bfe07 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -4,6 +4,11 @@ package net.sourceforge.pmd; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -20,10 +25,10 @@ import java.util.List; import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.hamcrest.Matchers; import org.junit.Test; import org.junit.rules.ExpectedException; +import net.sourceforge.pmd.RuleSetFactory.RulesetParseException; import net.sourceforge.pmd.junit.JavaUtilLoggingRule; import net.sourceforge.pmd.junit.LocaleRule; import net.sourceforge.pmd.lang.DummyLanguageModule; @@ -33,6 +38,8 @@ import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.util.ResourceLoader; +import com.github.oowekyala.ooxml.messages.XmlException; + public class RuleSetFactoryTest { @org.junit.Rule @@ -398,8 +405,9 @@ public class RuleSetFactoryTest { @Test public void testExternalReferenceOverrideNonExistent() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage("Cannot set non-existent property 'test4' on rule TestNameOverride"); + ex.expect(RulesetParseException.class); + ex.expectCause(allOf(isA(XmlException.class), + hasProperty("simpleMessage", is("Cannot set non-existent property 'test4' on rule TestNameOverride")))); loadFirstRule(REF_OVERRIDE_NONEXISTENT); } @@ -580,7 +588,7 @@ public class RuleSetFactoryTest { @Test public void testIncorrectMinimumLanguageVersion() throws RuleSetNotFoundException { ex.expect(IllegalArgumentException.class); - ex.expectMessage(Matchers.containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." + ex.expectMessage(containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." loadFirstRule(INCORRECT_MINIMUM_LANGUAGE_VERSION); } @@ -611,7 +619,7 @@ public class RuleSetFactoryTest { @Test public void testIncorrectMaximumLanguageVersion() throws RuleSetNotFoundException { ex.expect(IllegalArgumentException.class); - ex.expectMessage(Matchers.containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." + ex.expectMessage(containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." loadFirstRule(INCORRECT_MAXIMUM_LANGUAGE_VERSION); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/HTMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/HTMLRendererTest.java index 9daba62e06..a405a0d9df 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/HTMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/HTMLRendererTest.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.renderers; import static org.junit.Assert.assertEquals; import java.io.IOException; +import java.util.Optional; import org.junit.Test; @@ -96,7 +97,7 @@ public class HTMLRendererTest extends AbstractRendererTest { final String linkPrefix = "https://github.com/pmd/pmd/blob/master/"; final String linePrefix = "L"; renderer.setProperty(HTMLRenderer.LINK_PREFIX, linkPrefix); - renderer.setProperty(HTMLRenderer.LINE_PREFIX, linePrefix); + renderer.setProperty(HTMLRenderer.LINE_PREFIX, Optional.of(linePrefix)); renderer.setProperty(HTMLRenderer.HTML_EXTENSION, false); Report rep = reportOneViolation(); @@ -122,7 +123,7 @@ public class HTMLRendererTest extends AbstractRendererTest { final HTMLRenderer renderer = new HTMLRenderer(); final String linkPrefix = "https://github.com/pmd/pmd/blob/master/"; renderer.setProperty(HTMLRenderer.LINK_PREFIX, linkPrefix); - renderer.setProperty(HTMLRenderer.LINE_PREFIX, ""); + renderer.setProperty(HTMLRenderer.LINE_PREFIX, Optional.of("")); renderer.setProperty(HTMLRenderer.HTML_EXTENSION, false); Report rep = reportOneViolation(); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java index 867868b007..76e62a4db8 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.junit.Test; @@ -27,7 +28,7 @@ public class SummaryHTMLRendererTest extends AbstractRendererTest { public Renderer getRenderer() { Renderer result = new SummaryHTMLRenderer(); result.setProperty(HTMLRenderer.LINK_PREFIX, "link_prefix"); - result.setProperty(HTMLRenderer.LINE_PREFIX, "line_prefix"); + result.setProperty(HTMLRenderer.LINE_PREFIX, Optional.of("line_prefix")); result.setProperty(HTMLRenderer.HTML_EXTENSION, true); return result; } From 8f3d52ab1cba4fac72c84dbf6c64bcbbb688e54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 14 Jun 2020 19:56:19 +0200 Subject: [PATCH 053/347] Remove some useless methods --- .../sourceforge/pmd/lang/rule/MockRule.java | 5 ++++- .../properties/GenericPropertyDescriptor.java | 20 ------------------- .../pmd/properties/PropertyBuilder.java | 11 ++++++---- .../pmd/properties/PropertyDescriptor.java | 8 -------- .../pmd/properties/PropertyTypeId.java | 2 +- .../AvoidDuplicateLiteralsRule.java | 2 +- 6 files changed, 13 insertions(+), 35 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java index b613a336fc..e609d7d862 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/MockRule.java @@ -10,6 +10,7 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -26,10 +27,12 @@ import net.sourceforge.pmd.properties.PropertyFactory; @Deprecated public class MockRule extends AbstractRule { + private static final PropertyDescriptor PROP = PropertyFactory.intProperty("testIntProperty").desc("testIntProperty").require(inRange(1, 100)).defaultValue(1).build(); + public MockRule() { super(); setLanguage(LanguageRegistry.getLanguage("Dummy")); - definePropertyDescriptor(PropertyFactory.intProperty("testIntProperty").desc("testIntProperty").require(inRange(1, 100)).defaultValue(1).build()); + definePropertyDescriptor(PROP); } public MockRule(String name, String description, String message, String ruleSetName, RulePriority priority) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java index b02f9cdb97..4f7dcb170d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.properties; -import java.util.Objects; - import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.properties.xml.XmlMapper; @@ -77,22 +75,4 @@ final class GenericPropertyDescriptor implements PropertyDescriptor { return typeId; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof PropertyDescriptor)) { - return false; - } - PropertyDescriptor that = (PropertyDescriptor) o; - return Objects.equals(name, that.name()) - && defaultValue.equals(that.defaultValue()); - } - - @Override - public int hashCode() { - return Objects.hash(name, defaultValue); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index baa55ebee6..f9ebd2c896 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -171,7 +171,8 @@ public abstract class PropertyBuilder, T> { * * @return The built descriptor * - * @throws IllegalArgumentException if the description or default value were not provided, or if the default value doesn't satisfy the given constraints + * @throws IllegalArgumentException if the description or default value were not provided + * @throws IllegalArgumentException if the default value does not satisfy the given constraints */ public abstract PropertyDescriptor build(); @@ -222,9 +223,11 @@ public abstract class PropertyBuilder, T> { * @return A new list property builder * * @throws IllegalStateException if the default value has already been set + * + * @see #map(Collector) */ - /* package private */ GenericCollectionPropertyBuilder> toList() { - return to(Collectors.toList()); + public GenericCollectionPropertyBuilder> toList() { + return map(Collectors.toList()); } /** @@ -256,7 +259,7 @@ public abstract class PropertyBuilder, T> { * * @throws IllegalStateException if the default value has already been set */ - public > GenericCollectionPropertyBuilder to(Collector collector) { + public > GenericCollectionPropertyBuilder map(Collector collector) { if (isDefaultValueSet()) { throw new IllegalStateException("The default value is already set!"); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 563255d1b8..088ea1f279 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -120,12 +120,4 @@ public interface PropertyDescriptor { return xmlMapper().toString(value); } - - /** - * Property descriptors are equal if they have the same name, to be - * used in a map. Other attributes are ignored. - */ - @Override - boolean equals(Object o); - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 9bc80251e6..7c8ab90ec0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -89,7 +89,7 @@ public enum PropertyTypeId { @Override public PropertyBuilder newBuilder(String name) { - return factory.apply(name); + return factory.apply(name).typeId(PropertyTypeId.this); } }; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java index 04d5bce06a..4175814ccf 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java @@ -43,7 +43,7 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule { .desc("List of literals to ignore. " + "A literal is ignored if its image can be found in this list. " + "Components of this list should not be surrounded by double quotes.") - .to(Collectors.toSet()) + .map(Collectors.toSet()) .defaultValue(Collections.emptySet()) .delim(',') .build(); From 8a8ea6860f2c87650feb1d83e20473614541e430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 14 Jun 2020 20:17:25 +0200 Subject: [PATCH 054/347] Fix pmd violations --- .../java/net/sourceforge/pmd/RuleSetFactory.java | 13 +++++-------- .../pmd/properties/PropertyDescriptor.java | 4 +++- .../java/net/sourceforge/pmd/rules/RuleFactory.java | 7 ++++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index a509d3251a..f98a58f49f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -63,10 +63,6 @@ public class RuleSetFactory { private static final Logger LOG = Logger.getLogger(RuleSetFactory.class.getName()); - private static final String DESCRIPTION = "description"; - private static final String UNEXPECTED_ELEMENT = "Unexpected element <"; - private static final String PRIORITY = "priority"; - private final ResourceLoader resourceLoader; private final RulePriority minimumPriority; private final boolean warnDeprecated; @@ -396,6 +392,7 @@ public class RuleSetFactory { PositionedXmlDoc parsed = XmlMessageUtils.getInstance().parse(builder, inputSource, handler); + @SuppressWarnings("PMD.CloseResource") AccumulatingErrorReporter err = makeReporter(handler, parsed); try { RuleSetBuilder ruleSetBuilder = new RuleSetBuilder(inputStream.getChecksum().getValue()).withFileName(ruleSetReferenceId.getRuleSetFileName()); @@ -408,7 +405,7 @@ public class RuleSetFactory { throw e; } } catch (ParserConfigurationException | IOException | XmlException ex) { - if (!(ex instanceof XmlException)) { // would already have been reported + if (!(ex instanceof XmlException)) { // NOPMD would already have been reported ex.printStackTrace(); } throw new RulesetParseException("Couldn't read the ruleset " + ruleSetReferenceId, ex); @@ -456,7 +453,7 @@ public class RuleSetFactory { String nodeName = node.getNodeName(); String text = parseTextNode(node); switch (nodeName) { - case DESCRIPTION: + case RuleFactory.DESCRIPTION: builder.withDescription(text); break; case "include-pattern": { @@ -475,7 +472,7 @@ public class RuleSetFactory { builder.withFileExclusions(pattern); break; } - case "rule": + case RuleFactory.RULE: parseRuleNode(ruleSetReferenceId, builder, node, withDeprecatedRuleReferences, rulesetReferences, err); break; default: @@ -595,7 +592,7 @@ public class RuleSetFactory { Element excludeElement = (Element) child; String excludedRuleName = excludeElement.getAttribute("name"); excludedRulesCheck.add(excludedRuleName); - } else if (isElementNode(child, PRIORITY)) { + } else if (isElementNode(child, RuleFactory.PRIORITY)) { priority = parseTextNode(child).trim(); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 088ea1f279..02e1876c31 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -24,7 +24,9 @@ import net.sourceforge.pmd.properties.xml.XmlMapper; *

Upcoming API changes to the properties framework

* see pmd/pmd#1432 * - * @param Type of the property's value. + * TODO this could be turned into a class, there's a single, very simple implementation + * + * @param Type of the property's value * * @author Brian Remedios * @author Clรฉment Fournier diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 8795d52a01..f9dac1df90 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -58,10 +58,11 @@ public class RuleFactory { private static final String MAXIMUM_LANGUAGE_VERSION = "maximumLanguageVersion"; private static final String SINCE = "since"; private static final String PROPERTIES = "properties"; - private static final String PRIORITY = "priority"; + public static final String PRIORITY = "priority"; + + public static final String RULE = "rule"; private static final String EXAMPLE = "example"; - private static final String DESCRIPTION = "description"; - private static final String PROPERTY = "property"; + public static final String DESCRIPTION = "description"; private static final String CLASS = "class"; private static final List REQUIRED_ATTRIBUTES = Collections.unmodifiableList(Arrays.asList(NAME, CLASS)); From 29d535f0a08ff0a65d863cb1c4a178e568d70afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 18:25:02 +0200 Subject: [PATCH 055/347] Checkstyle --- .../src/main/java/net/sourceforge/pmd/RuleSetFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index f98a58f49f..121e950bed 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -414,11 +414,11 @@ public class RuleSetFactory { static class RulesetParseException extends RuntimeException { - public RulesetParseException(String message, Throwable cause) { + RulesetParseException(String message, Throwable cause) { super(message, cause); } - public RulesetParseException(Throwable cause) { + RulesetParseException(Throwable cause) { super(cause); } } From 3fb4b7d8475dbb1aff0212d20833e388e5fb8f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 18:34:38 +0200 Subject: [PATCH 056/347] Make PropertyDescriptor a class --- .../properties/GenericPropertyDescriptor.java | 78 ------------- .../pmd/properties/PropertyBuilder.java | 4 +- .../pmd/properties/PropertyDescriptor.java | 107 ++++++++++++++---- 3 files changed, 90 insertions(+), 99 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java deleted file mode 100644 index 4f7dcb170d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/GenericPropertyDescriptor.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import org.checkerframework.checker.nullness.qual.Nullable; - -import net.sourceforge.pmd.properties.xml.XmlMapper; -import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; - - -/** - * Bound to be the single implementation for PropertyDescriptor in 7.0.0. - * - * @author Clรฉment Fournier - * @since 6.10.0 - */ -final class GenericPropertyDescriptor implements PropertyDescriptor { - - - private final XmlMapper parser; - private final PropertyTypeId typeId; - private final String name; - private final String description; - private final T defaultValue; - - GenericPropertyDescriptor(String name, - String description, - T defaultValue, - XmlMapper parser, - @Nullable PropertyTypeId typeId) { - - this.name = name; - this.description = description; - this.defaultValue = defaultValue; - this.parser = parser; - this.typeId = typeId; - - XmlSyntaxUtils.checkConstraintsThrow( - defaultValue, - parser.getConstraints(), - s -> new IllegalArgumentException("Constraint violated " + s) - ); - } - - @Override - public String name() { - return name; - } - - @Override - public String description() { - return description; - } - - @Override - public T defaultValue() { - return defaultValue; - } - - @Override - public String errorFor(T value) { - return XmlSyntaxUtils.checkConstraintsJoin(value, parser.getConstraints()); - } - - @Override - public XmlMapper xmlMapper() { - return parser; - } - - @Nullable - @Override - public PropertyTypeId getTypeId() { - return typeId; - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index f9ebd2c896..02983aa35c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -299,7 +299,7 @@ public abstract class PropertyBuilder, T> { @Override public PropertyDescriptor build() { - return new GenericPropertyDescriptor<>( + return new PropertyDescriptor<>( getName(), getDescription(), getDefaultValue(), @@ -502,7 +502,7 @@ public abstract class PropertyBuilder, T> { syntax = XmlSyntaxUtils.withAllConstraints(syntax, collectionConstraints); - return new GenericPropertyDescriptor<>( + return new PropertyDescriptor<>( getName(), getDescription(), getDefaultValue(), diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 02e1876c31..85451695c7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -4,10 +4,13 @@ package net.sourceforge.pmd.properties; +import java.util.Objects; + import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.properties.xml.XmlMapper; +import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; /** @@ -24,8 +27,6 @@ import net.sourceforge.pmd.properties.xml.XmlMapper; *

Upcoming API changes to the properties framework

* see pmd/pmd#1432 * - * TODO this could be turned into a class, there's a single, very simple implementation - * * @param Type of the property's value * * @author Brian Remedios @@ -34,50 +35,90 @@ import net.sourceforge.pmd.properties.xml.XmlMapper; * @see PropertyFactory * @see PropertyBuilder */ -public interface PropertyDescriptor { +public final class PropertyDescriptor { + + + private final XmlMapper parser; + private final PropertyTypeId typeId; + private final String name; + private final String description; + private final T defaultValue; + + PropertyDescriptor(String name, + String description, + T defaultValue, + XmlMapper parser, + @Nullable PropertyTypeId typeId) { + + this.name = name; + this.description = description; + this.defaultValue = defaultValue; + this.parser = parser; + this.typeId = typeId; + + XmlSyntaxUtils.checkConstraintsThrow( + defaultValue, + parser.getConstraints(), + s -> new IllegalArgumentException("Constraint violated " + s) + ); + } + /** - * The name of the property without spaces as it serves as the key into the property map. + * The name of the property without spaces as it serves as the key + * into the property map. * * @return String */ - String name(); + public String name() { + return name; + } /** - * Describes the property and the role it plays within the rule it is specified for. Could be used in a tooltip. + * Describes the property and the role it plays within the rule it + * is specified for. Could be used in a tooltip. * * @return String */ - String description(); + public String description() { + return description; + } /** - * Default value to use when the user hasn't specified one or when they wish to revert to a known-good state. + * Default value to use when the user hasn't specified one or when + * they wish to revert to a known-good state. * * @return Object */ - T defaultValue(); + public T defaultValue() { + return defaultValue; + } /** * Returns the strategy used to read and write this property to XML. * May support strings too. */ - XmlMapper xmlMapper(); + public XmlMapper xmlMapper() { + return parser; + } /** * TODO this needs to go away. Property constraints are now checked at - * the time the ruleset is parsed, to report errors on the specific - * XML nodes. Other than that, constraints should be checked when - * calling {@link PropertySource#setProperty(PropertyDescriptor, Object)} - * for fail-fast behaviour. + * the time the ruleset is parsed, to report errors on the specific + * XML nodes. Other than that, constraints should be checked when + * calling {@link PropertySource#setProperty(PropertyDescriptor, Object)} + * for fail-fast behaviour. * * @deprecated PMD 7.0.0 will change the return type to {@code Optional} */ @Deprecated - String errorFor(T value); + public String errorFor(T value) { + return XmlSyntaxUtils.checkConstraintsJoin(value, parser.getConstraints()); + } /** @@ -87,8 +128,8 @@ public interface PropertyDescriptor { *

This replaces isDefinedExternally for the RulesetWriter. */ @InternalApi - default @Nullable PropertyTypeId getTypeId() { - return null; + public @Nullable PropertyTypeId getTypeId() { + return typeId; } @@ -101,7 +142,7 @@ public interface PropertyDescriptor { * simple strings, this method won't be general enough */ @Deprecated - default T valueFrom(String propertyString) throws IllegalArgumentException { + public T valueFrom(String propertyString) throws IllegalArgumentException { return xmlMapper().fromString(propertyString); } @@ -118,8 +159,36 @@ public interface PropertyDescriptor { * simple strings, this method won't be general enough */ @Deprecated - default String asDelimitedString(T value) { + public String asDelimitedString(T value) { return xmlMapper().toString(value); } + @Override + public String toString() { + return "GenericPropertyDescriptor{ " + + "name='" + name + '\'' + + ", parser=" + parser + + ", typeId=" + typeId + + ", description='" + description + '\'' + + ", defaultValue=" + defaultValue + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PropertyDescriptor that = (PropertyDescriptor) o; + return name.equals(that.name) + && description.equals(that.description) + && Objects.equals(defaultValue, that.defaultValue); + } + + @Override + public int hashCode() { + return Objects.hash(name, description, defaultValue); + } } From 5e323127f2df9146326c9d471b0b72df9e9641a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 18:45:08 +0200 Subject: [PATCH 057/347] Fix xml parser options --- .../net/sourceforge/pmd/properties/PropertyDescriptor.java | 2 +- .../java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java | 2 +- .../net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 85451695c7..779236f292 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -165,7 +165,7 @@ public final class PropertyDescriptor { @Override public String toString() { - return "GenericPropertyDescriptor{ " + return "PropertyDescriptor{ " + "name='" + name + '\'' + ", parser=" + parser + ", typeId=" + typeId diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java index 3f0a0dd349..2bfc19ba51 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java @@ -60,7 +60,7 @@ public class XmlParserOptions extends ParserOptions { .defaultValue(false) .build(); public static final PropertyDescriptor LOOKUP_DESCRIPTOR_DTD = - PropertyFactory.booleanProperty("xincludeAware") + PropertyFactory.booleanProperty("lookupDescriptorDtd") .desc("deprecated!Specifies whether XML parser will attempt to lookup the DTD.") .defaultValue(false) .build(); diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java index 7aac852637..8ae2b07a0a 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.xml.rule; +import static net.sourceforge.pmd.lang.xml.XmlParserOptions.LOOKUP_DESCRIPTOR_DTD; + import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -56,6 +58,7 @@ public class AbstractXmlRule extends AbstractRule implements ImmutableLanguage { definePropertyDescriptor(NAMESPACE_AWARE_DESCRIPTOR); definePropertyDescriptor(VALIDATING_DESCRIPTOR); definePropertyDescriptor(XINCLUDE_AWARE_DESCRIPTOR); + definePropertyDescriptor(LOOKUP_DESCRIPTOR_DTD); } @Override From fe006d407d416c580f9b4dfe43e245ebe663441a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 19:16:29 +0200 Subject: [PATCH 058/347] Change default delimiter to comma --- .../pmd/properties/PropertyBuilder.java | 22 +------------------ .../pmd/properties/PropertyFactory.java | 11 ++++------ .../sourceforge/pmd/rules/RuleFactory.java | 5 +---- .../sourceforge/pmd/RuleSetFactoryTest.java | 2 +- .../sourceforge/pmd/RuleWithProperties.java | 1 - .../properties/PropertyDescriptorTest.java | 4 ++-- .../resources/rulesets/ruledoctest/sample.xml | 2 +- .../bestpractices/GuardLogStatementRule.java | 3 +-- .../rule/design/LoosePackageCouplingRule.java | 4 ++-- .../AvoidDuplicateLiteralsRule.java | 1 - .../rule/errorprone/CloseResourceRule.java | 4 ++-- .../resources/category/java/bestpractices.xml | 2 +- .../resources/category/java/errorprone.xml | 2 +- .../pmd/lang/java/rule/XPathRuleTest.java | 1 - .../resources/category/pom/errorprone.xml | 1 - 15 files changed, 17 insertions(+), 48 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 02983aa35c..5f90f79a50 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -395,7 +395,6 @@ public abstract class PropertyBuilder, T> { private XmlMapper itemParser; private final Collector collector; - private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; private final List> collectionConstraints = new ArrayList<>(); @@ -475,29 +474,10 @@ public abstract class PropertyBuilder, T> { } - /** - * Specify a delimiter character. By default it's {@value PropertyFactory#DEFAULT_DELIMITER}, - * or {@value PropertyFactory#DEFAULT_NUMERIC_DELIMITER} for numeric properties. - * - * @param delim Delimiter - * - * @return The same builder - * - * @deprecated PMD 7.0.0 will introduce a new XML syntax for multi-valued properties which will not rely on delimiters. - * This method is kept until this is implemented for compatibility reasons with the pre-7.0.0 framework, but - * it will be scrapped come 7.0.0. - */ - @Deprecated - public GenericCollectionPropertyBuilder delim(char delim) { - this.multiValueDelimiter = delim; - return this; - } - - @Override public PropertyDescriptor build() { XmlMapper syntax = itemParser.supportsStringMapping() - ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, multiValueDelimiter) + ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, PropertyFactory.DEFAULT_DELIMITER) : XmlSyntaxUtils.onlySeq(itemParser, collector); syntax = XmlSyntaxUtils.withAllConstraints(syntax, collectionConstraints); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 318aefb6c1..2b5ef6e709 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -87,10 +87,7 @@ public final class PropertyFactory { /** Default delimiter for multi-valued properties other than numeric ones. */ - static final char DEFAULT_DELIMITER = '|'; - - /** Default delimiter for numeric multi-valued properties. */ - static final char DEFAULT_NUMERIC_DELIMITER = ','; + static final char DEFAULT_DELIMITER = ','; private PropertyFactory() { @@ -129,7 +126,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> intListProperty(String name) { - return intProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); + return intProperty(name).toList(); } @@ -166,7 +163,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> longIntListProperty(String name) { - return longIntProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); + return longIntProperty(name).toList(); } @@ -198,7 +195,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> doubleListProperty(String name) { - return doubleProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); + return doubleProperty(name).toList(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index f9dac1df90..afd0b2c9de 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -295,11 +295,8 @@ public class RuleFactory { final PropertyBuilder builder = factory.newBuilder(name).desc(description); // parse the value - final XmlMapper syntax = factory.getXmlMapper(); - final T defaultValue = parsePropertyValue(propertyElement, err, syntax); - - builder.defaultValue(defaultValue); + builder.defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper())); // TODO support constraints like numeric range diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index c41e9bfe07..081d642444 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -192,7 +192,7 @@ public class RuleSetFactoryTest { + "class=\"net.sourceforge.pmd.lang.rule.XPathRule\" language=\"dummy\">\n" + " Please move your class to the right folder(rest \nfolder)\n" + " 2\n \n \n "); PropertyDescriptor> prop = (PropertyDescriptor>) r.getPropertyDescriptor("packageRegEx"); List values = r.getProperty(prop); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleWithProperties.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleWithProperties.java index 618af5898a..37defeccc3 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleWithProperties.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleWithProperties.java @@ -26,7 +26,6 @@ public class RuleWithProperties extends FooRule { PropertyFactory.stringListProperty("multiString") .desc("multi string property") .defaultValues("default1", "default2") - .delim(',') .build(); public RuleWithProperties() { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index 64a991b3c3..acc793bbab 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -231,7 +231,7 @@ public class PropertyDescriptorTest { assertEquals("stringListProp", listDescriptor.name()); assertEquals("hello", listDescriptor.description()); assertEquals(Arrays.asList("v1", "v2"), listDescriptor.defaultValue()); - assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("foo|bar")); + assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("foo,bar")); } private enum SampleEnum { A, B, C } @@ -262,7 +262,7 @@ public class PropertyDescriptorTest { assertEquals("enumListProp", listDescriptor.name()); assertEquals("hello", listDescriptor.description()); assertEquals(Arrays.asList(SampleEnum.A, SampleEnum.B), listDescriptor.defaultValue()); - assertEquals(Arrays.asList(SampleEnum.B, SampleEnum.C), listDescriptor.valueFrom("TEST_B|TEST_C")); + assertEquals(Arrays.asList(SampleEnum.B, SampleEnum.C), listDescriptor.valueFrom("TEST_B,TEST_C")); } diff --git a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml index da81d702d1..1579f1c61a 100644 --- a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml +++ b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml @@ -160,7 +160,7 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index 4ea5b89cc4..94377f9f88 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -63,14 +63,13 @@ public class GuardLogStatementRule extends AbstractJavaRule implements Rule { .desc("LogLevels to guard") .defaultValues("trace", "debug", "info", "warn", "error", "log", "finest", "finer", "fine", "info", "warning", "severe") - .delim(',') .build(); private static final PropertyDescriptor> GUARD_METHODS = stringListProperty("guardsMethods") .desc("Method use to guard the log statement") .defaultValues("isTraceEnabled", "isDebugEnabled", "isInfoEnabled", "isWarnEnabled", "isErrorEnabled", "isLoggable") - .delim(',').build(); + .build(); private Map guardStmtByLogLevel = new HashMap<>(12); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java index 6697414b0b..9b142b3fd5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java @@ -38,10 +38,10 @@ import net.sourceforge.pmd.properties.PropertySource; public class LoosePackageCouplingRule extends AbstractJavaRule { public static final PropertyDescriptor> PACKAGES_DESCRIPTOR = - stringListProperty("packages").desc("Restricted packages").emptyDefaultValue().delim(',').build(); + stringListProperty("packages").desc("Restricted packages").emptyDefaultValue().build(); public static final PropertyDescriptor> CLASSES_DESCRIPTOR = - stringListProperty("classes").desc("Allowed classes").emptyDefaultValue().delim(',').build(); + stringListProperty("classes").desc("Allowed classes").emptyDefaultValue().build(); // The package of this source file private String thisPackage; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java index 4175814ccf..cda0b86019 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java @@ -45,7 +45,6 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule { + "Components of this list should not be surrounded by double quotes.") .map(Collectors.toSet()) .defaultValue(Collections.emptySet()) - .delim(',') .build(); private Map> literals = new HashMap<>(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java index c32a294994..1285fbf175 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java @@ -71,13 +71,13 @@ public class CloseResourceRule extends AbstractJavaRule { stringListProperty("closeTargets") .desc("Methods which may close this resource") .emptyDefaultValue() - .delim(',').build(); + .build(); private static final PropertyDescriptor> TYPES_DESCRIPTOR = stringListProperty("types") .desc("Affected types") .defaultValues("java.lang.AutoCloseable", "java.sql.Connection", "java.sql.Statement", "java.sql.ResultSet") - .delim(',').build(); + .build(); private static final PropertyDescriptor USE_CLOSE_AS_DEFAULT_TARGET = booleanProperty("closeAsDefaultTarget") diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 6b30d5122d..d36f1d36d8 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1791,7 +1791,7 @@ preserved. 3 - + diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 58acd68651..a207be4927 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -2356,7 +2356,7 @@ See the property `annotations`. 3 - + From 2434482774961dfb4358f24b11ea70c8f7fd2db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 19:28:28 +0200 Subject: [PATCH 059/347] Remove duplicated properties --- pmd-java/src/main/resources/category/java/errorprone.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index a207be4927..4c447acccf 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1173,7 +1173,6 @@ should be annotated with @Test and @Ignore. ] ]]> - Date: Fri, 19 Jun 2020 19:35:59 +0200 Subject: [PATCH 060/347] Equals problem with Regex --- .../sourceforge/pmd/properties/PropertyDescriptor.java | 8 ++------ pmd-plsql/src/main/resources/category/plsql/codestyle.xml | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 779236f292..5fd4597ef1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -20,9 +20,6 @@ import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; *

A property descriptor provides validation, * serialization, and default values for a datatype {@code }. * Property descriptors are immutable and can be shared freely. - * Property descriptors do not override {@link Object#equals(Object)} - * or {@link Object#hashCode()}. Pre 6.0.0 two descriptors were equal - * if they had the same name. * *

Upcoming API changes to the properties framework

* see pmd/pmd#1432 @@ -183,12 +180,11 @@ public final class PropertyDescriptor { } PropertyDescriptor that = (PropertyDescriptor) o; return name.equals(that.name) - && description.equals(that.description) - && Objects.equals(defaultValue, that.defaultValue); + && description.equals(that.description); } @Override public int hashCode() { - return Objects.hash(name, description, defaultValue); + return Objects.hash(name, description); } } diff --git a/pmd-plsql/src/main/resources/category/plsql/codestyle.xml b/pmd-plsql/src/main/resources/category/plsql/codestyle.xml index 2e01fea8c7..3673688364 100644 --- a/pmd-plsql/src/main/resources/category/plsql/codestyle.xml +++ b/pmd-plsql/src/main/resources/category/plsql/codestyle.xml @@ -164,7 +164,7 @@ In case you have loops please name the loop variables more meaningful.
- + From e63129e79769284ecb74b6951a31a05336236d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 19:51:38 +0200 Subject: [PATCH 061/347] Fix tests, add isXPathAvailable --- .../pmd/properties/PropertyBuilder.java | 26 +++++++++++++++---- .../pmd/properties/PropertyDescriptor.java | 18 ++++++++++--- .../pmd/properties/PropertyTypeId.java | 2 +- .../pmd/lang/java/rule/XPathRuleTest.java | 3 ++- .../lang/java/metrics/impl/xml/CycloTest.xml | 2 +- .../xml/AvoidUsingHardCodedIP.xml | 2 +- .../codestyle/xml/FieldNamingConventions.xml | 2 +- .../rule/documentation/xml/CommentContent.xml | 2 +- .../AvoidBranchingStatementAsLastInLoop.xml | 6 ++--- 9 files changed, 45 insertions(+), 18 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 5f90f79a50..6f0beb134c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -16,6 +16,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -56,7 +57,8 @@ public abstract class PropertyBuilder, T> { private final String name; private String description; private T defaultValue; - protected PropertyTypeId typeId; + protected @Nullable PropertyTypeId typeId; + protected boolean isXPathAvailable = false; PropertyBuilder(String name) { @@ -122,6 +124,20 @@ public abstract class PropertyBuilder, T> { return (B) this; } + /** + * If true, the property will be made available to XPath queries as + * an XPath variable. The default is false (except for properties + * of XPath rules that were defined in XML). + * + * @param b Whether to enable or not + * + * @return This builder + */ + public B availableInXPath(boolean b) { + this.isXPathAvailable = b; + return (B) this; + } + /** * Add a constraint on the values that this property may take. * The validity of values will be checked when parsing the XML, @@ -304,8 +320,8 @@ public abstract class PropertyBuilder, T> { getDescription(), getDefaultValue(), parser, - typeId - ); + typeId, + isXPathAvailable); } } @@ -487,8 +503,8 @@ public abstract class PropertyBuilder, T> { getDescription(), getDefaultValue(), syntax, - typeId - ); + typeId, + isXPathAvailable); } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 5fd4597ef1..8d249befaf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -8,6 +8,7 @@ import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.RuleSetWriter; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; @@ -40,18 +41,21 @@ public final class PropertyDescriptor { private final String name; private final String description; private final T defaultValue; + private final boolean isXPathAvailable; PropertyDescriptor(String name, String description, T defaultValue, XmlMapper parser, - @Nullable PropertyTypeId typeId) { + @Nullable PropertyTypeId typeId, + boolean isXPathAvailable) { this.name = name; this.description = description; this.defaultValue = defaultValue; this.parser = parser; this.typeId = typeId; + this.isXPathAvailable = isXPathAvailable; XmlSyntaxUtils.checkConstraintsThrow( defaultValue, @@ -120,15 +124,21 @@ public final class PropertyDescriptor { /** * Returns the type ID which was used to define this property. Returns - * null if this property was defined in Java code and not in XML. - * - *

This replaces isDefinedExternally for the RulesetWriter. + * null if this property was defined in Java code and not in XML. This + * is used to write the property back to XML, when using a {@link RuleSetWriter}. */ @InternalApi public @Nullable PropertyTypeId getTypeId() { return typeId; } + /** + * Returns whether the property is available to XPath queries. + */ + public boolean isXPathAvailable() { + return isXPathAvailable; + } + /** * TODO port tests to use the mapper directly. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 7c8ab90ec0..681a475906 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -89,7 +89,7 @@ public enum PropertyTypeId { @Override public PropertyBuilder newBuilder(String name) { - return factory.apply(name).typeId(PropertyTypeId.this); + return factory.apply(name).typeId(PropertyTypeId.this).availableInXPath(true); } }; } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java index 36d5fda8fe..ea47e81c6c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java @@ -66,6 +66,7 @@ public class XPathRuleTest extends RuleTst { = PropertyFactory.stringListProperty("forbiddenNames") .desc("Forbidden names") .defaultValues("forbid1", "forbid2") + .availableInXPath(true) .build(); rule.definePropertyDescriptor(varDescriptor); @@ -80,7 +81,7 @@ public class XPathRuleTest extends RuleTst { XPathRule rule = makeXPath("//VariableDeclaratorId[@Name=$var]"); rule.setMessage("Avoid vars"); PropertyDescriptor varDescriptor = - PropertyFactory.stringProperty("var").desc("Test var").defaultValue("").build(); + PropertyFactory.stringProperty("var").desc("Test var").defaultValue("").availableInXPath(true).build(); rule.definePropertyDescriptor(varDescriptor); rule.setProperty(varDescriptor, "fiddle"); Report report = getReportForTestString(rule, TEST2); diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml index 621adf49de..e9c1d3889d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml @@ -135,7 +135,7 @@ public class Complicated { Full example - considerAssert + ignoreBooleanPaths - ignoreBooleanPaths|considerAssert + ignoreBooleanPaths,considerAssert 8 'Complicated#exception()' has value 4. diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml index 39afc069c1..361b575064 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml @@ -150,7 +150,7 @@ public class Foo { Comprehensive, check for IPv6 and IPv4 mapped IPv6 - IPv6|IPv4 mapped IPv6 + IPv6,IPv4 mapped IPv6 15 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml index af2fc5fdbe..541d6c4456 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml @@ -205,7 +205,7 @@ public class Foo implements Serializable { More exclusions can be configured - m$mangled|serialVersionUID + m$mangled,serialVersionUID 0 Includes bad words - idiot|jerk + idiot,jerk 2 violations: break:for/do/while - for|do|while + for,do,while 3 @@ -116,7 +116,7 @@ public class Foo { violations: continue:for/do/while - for|do|while + for,do,while 3 @@ -126,7 +126,7 @@ public class Foo { violations: return:for/do/while - for|do|while + for,do,while 3 From b344875247a799c50cdc04238458c2a3319c8720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 20:20:10 +0200 Subject: [PATCH 062/347] Update rule doc generator --- .../pmd/properties/PropertyDescriptor.java | 1 + .../pmd/properties/PropertyTypeId.java | 15 ++++- .../sourceforge/pmd/rules/RuleFactory.java | 19 +++--- .../pmd/docs/RuleDocGenerator.java | 38 +++++------ pmd-doc/src/test/resources/expected/sample.md | 64 +++++++++---------- .../resources/rulesets/ruledoctest/sample.xml | 2 +- 6 files changed, 72 insertions(+), 67 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 8d249befaf..5a1429c55b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -180,6 +180,7 @@ public final class PropertyDescriptor { + ", defaultValue=" + defaultValue + '}'; } + // TODO these equality routines needs to go away, should be implemented in Rule::equals @Override public boolean equals(Object o) { if (this == o) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index 681a475906..db0cd9f39e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -71,8 +71,14 @@ public enum PropertyTypeId { this.factory = factory; } - // this is provided so that the mapper and the factory may be related - // through the same type parameter, so that capture works well + /** + * An factory for new properties, whose default value must be deserialized + * using an {@link XmlMapper}. This is provided so that the mapper and + * the factory may be related through the same type parameter, so that + * capture works well. + * + * @param Type of values of the property. + */ public interface BuilderAndMapper { XmlMapper getXmlMapper(); @@ -80,6 +86,11 @@ public enum PropertyTypeId { PropertyBuilder newBuilder(String name); } + /** + * Returns the object used to create new properties with the type + * of this constant. + */ + @SuppressWarnings("rawtypes") public BuilderAndMapper getBuilderUtils() { return new BuilderAndMapper() { @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index afd0b2c9de..78d03f73a6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -29,7 +29,6 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.xml.SchemaConstants; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.lang.rule.RuleReference; -import net.sourceforge.pmd.properties.PropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; @@ -288,19 +287,21 @@ public class RuleFactory { private static PropertyDescriptor propertyDefCapture(Element propertyElement, XmlErrorReporter err, BuilderAndMapper factory) { + // TODO support constraints like numeric range String name = SchemaConstants.NAME.getAttributeOrThrow(propertyElement, err); String description = SchemaConstants.DESCRIPTION.getAttributeOrThrow(propertyElement, err); - final PropertyBuilder builder = factory.newBuilder(name).desc(description); + try { + return factory.newBuilder(name) + .desc(description) + .defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper())) + .build(); - // parse the value - - builder.defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper())); - - // TODO support constraints like numeric range - - return builder.build(); + } catch (IllegalArgumentException e) { + // builder threw, rethrow with XML location + throw err.error(propertyElement, e); + } } private static T parsePropertyValue(Element propertyElt, XmlErrorReporter err, XmlMapper syntax) { diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index 868428f5d2..f922780df0 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -446,8 +446,8 @@ public class RuleDocGenerator { if (!properties.isEmpty()) { lines.add("**This rule has the following properties:**"); lines.add(""); - lines.add("|Name|Default Value|Description|Multivalued|"); - lines.add("|----|-------------|-----------|-----------|"); + lines.add("|Name|Default Value|Description|"); + lines.add("|----|-------------|-----------|"); for (PropertyDescriptor propertyDescriptor : properties) { String description = propertyDescriptor.description(); final boolean isDeprecated = isDeprecated(propertyDescriptor); @@ -457,20 +457,16 @@ public class RuleDocGenerator { String defaultValue = determineDefaultValueAsString(propertyDescriptor, rule, true); - String multiValued = "no"; // TODO document property syntax - // if (propertyDescriptor.isMultiValue()) { - // MultiValuePropertyDescriptor multiValuePropertyDescriptor = - // (MultiValuePropertyDescriptor) propertyDescriptor; - // multiValued = "yes. Delimiter is '" - // + multiValuePropertyDescriptor.multiValueDelimiter() + "'."; - // } - lines.add("|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(propertyDescriptor.name())) - + "|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(defaultValue)) + "|" - + EscapeUtils.escapeMarkdown((isDeprecated ? DEPRECATION_LABEL_SMALL : "") - + StringEscapeUtils.escapeHtml4(description)) - + "|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(multiValued)) + "|"); + lines.add("|" + + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(propertyDescriptor.name())) + + "|" + + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(defaultValue)) + + "|" + + EscapeUtils.escapeMarkdown((isDeprecated ? DEPRECATION_LABEL_SMALL : "") + StringEscapeUtils.escapeHtml4(description)) + + "|" + ); } lines.add(""); } @@ -530,15 +526,11 @@ public class RuleDocGenerator { if (realDefaultValue != null) { defaultValue = propertyDescriptor.asDelimitedString(realDefaultValue); - // TODO document multi value properties - // if (pad && propertyDescriptor.isMultiValue()) { - // MultiValuePropertyDescriptor> multiPropertyDescriptor = (MultiValuePropertyDescriptor>) propertyDescriptor; - // // surround the delimiter with spaces, so that the browser can wrap - // // the value nicely - // defaultValue = defaultValue.replaceAll(Pattern.quote( - // String.valueOf(multiPropertyDescriptor.multiValueDelimiter())), - // " " + multiPropertyDescriptor.multiValueDelimiter() + " "); - // } + if (pad && realDefaultValue instanceof Collection) { + // surround the delimiter with spaces, so that the browser can wrap + // the value nicely + defaultValue = defaultValue.replaceAll(",", " , "); + } } return defaultValue; } diff --git a/pmd-doc/src/test/resources/expected/sample.md b/pmd-doc/src/test/resources/expected/sample.md index ff3c6fc81d..e4bbb09f21 100644 --- a/pmd-doc/src/test/resources/expected/sample.md +++ b/pmd-doc/src/test/resources/expected/sample.md @@ -71,18 +71,18 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i **This rule has the following properties:** -|Name|Default Value|Description|Multivalued| -|----|-------------|-----------|-----------| -|sampleAdditionalProperty|the value|This is a additional property for tests|no| -|sampleMultiStringProperty|Value1 \| Value2|Test property with multiple strings|yes. Delimiter is '\|'.| -|sampleDeprecatedProperty|test|Deprecated This is a sample deprecated property for tests|no| -|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|no| -|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|no| -|sampleRegexProperty3|\\s+|The property is of type regex|no| -|sampleRegexProperty4|\_dd\_|The property is of type regex|no| -|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|no| -|sampleRegexProperty6|\\b|The property is of type regex|no| -|sampleRegexProperty7|\\n|The property is of type regex|no| +|Name|Default Value|Description| +|----|-------------|-----------| +|sampleAdditionalProperty|the value|This is a additional property for tests| +|sampleMultiStringProperty|Value1 , Value2|Test property with multiple strings| +|sampleDeprecatedProperty|test|Deprecated This is a sample deprecated property for tests| +|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex| +|sampleRegexProperty2|\[a-z\]\*|The property is of type regex| +|sampleRegexProperty3|\\s+|The property is of type regex| +|sampleRegexProperty4|\_dd\_|The property is of type regex| +|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex| +|sampleRegexProperty6|\\b|The property is of type regex| +|sampleRegexProperty7|\\n|The property is of type regex| **Use this rule with the default properties by just referencing it:** ``` xml @@ -94,7 +94,7 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i - + @@ -242,18 +242,18 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i **This rule has the following properties:** -|Name|Default Value|Description|Multivalued| -|----|-------------|-----------|-----------| -|sampleAdditionalProperty|the value|This is a additional property for tests|no| -|sampleMultiStringProperty|Value1 \| Value2|Test property with multiple strings|yes. Delimiter is '\|'.| -|sampleDeprecatedProperty|test|Deprecated This is a sample deprecated property for tests|no| -|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|no| -|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|no| -|sampleRegexProperty3|\\s+|The property is of type regex|no| -|sampleRegexProperty4|\_dd\_|The property is of type regex|no| -|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|no| -|sampleRegexProperty6|\\b|The property is of type regex|no| -|sampleRegexProperty7|\\n|The property is of type regex|no| +|Name|Default Value|Description| +|----|-------------|-----------| +|sampleAdditionalProperty|the value|This is a additional property for tests| +|sampleMultiStringProperty|Value1 , Value2|Test property with multiple strings| +|sampleDeprecatedProperty|test|Deprecated This is a sample deprecated property for tests| +|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex| +|sampleRegexProperty2|\[a-z\]\*|The property is of type regex| +|sampleRegexProperty3|\\s+|The property is of type regex| +|sampleRegexProperty4|\_dd\_|The property is of type regex| +|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex| +|sampleRegexProperty6|\\b|The property is of type regex| +|sampleRegexProperty7|\\n|The property is of type regex| **Use this rule with the default properties by just referencing it:** ``` xml @@ -265,7 +265,7 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i - + @@ -339,11 +339,11 @@ if (0 > 1 && 0 < 1) { **This rule has the following properties:** -|Name|Default Value|Description|Multivalued| -|----|-------------|-----------|-----------| -|sampleRegexProperty|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|no| -|XSSpropertyTest <script>alert('XSS');</script>|<script>alert('XSS');</script>|<script>alert('XSS');</script>|no| -|escapingNeeded|this is escaped: \||You should be able to use \| in the description|no| +|Name|Default Value|Description| +|----|-------------|-----------| +|sampleRegexProperty|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex| +|XSSpropertyTest|<script>alert('XSS');</script>|<script>alert('XSS');</script>| +|escapingNeeded|this is escaped: \||You should be able to use \| in the description| **Use this rule with the default properties by just referencing it:** ``` xml @@ -355,7 +355,7 @@ if (0 > 1 && 0 < 1) { - + diff --git a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml index 1579f1c61a..1c680ed7f3 100644 --- a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml +++ b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml @@ -66,7 +66,7 @@ Here might be <script>alert('XSS');</script> as well. And "quotes". - + From eacc33d5759a8e13b0c815ade17d7be8c0331867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 22:44:56 +0200 Subject: [PATCH 063/347] Fix test schema --- .../net/sourceforge/pmd/RuleSetWriter.java | 2 +- .../pmd/internal/util/xml/SchemaConstant.java | 97 +++++++++ .../internal/util/xml/SchemaConstants.java | 102 +-------- .../pmd/internal/util/xml/XmlUtil.java | 8 +- .../pmd/renderers/RendererFactory.java | 2 +- .../sourceforge/pmd/rules/RuleFactory.java | 6 +- .../pmd/util/treeexport/TreeExportCli.java | 4 +- .../pmd/testframework/RuleTst.java | 200 ++++++++---------- .../internal/TestSchemaConstants.java | 25 +++ 9 files changed, 238 insertions(+), 208 deletions(-) create mode 100755 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java create mode 100644 pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 94079dbe3a..997b6c982f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -317,7 +317,7 @@ public class RuleSetWriter { final Element element = createPropertyValueElement(propertyDescriptor, propertyDescriptor.defaultValue()); SchemaConstants.NAME.setOn(element, propertyDescriptor.name()); - SchemaConstants.TYPE.setOn(element, typeId.getStringId()); + SchemaConstants.PROPERTY_TYPE.setOn(element, typeId.getStringId()); SchemaConstants.DESCRIPTION.setOn(element, propertyDescriptor.description()); // TODO support property constraints in XML return element; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java new file mode 100755 index 0000000000..3f6621f452 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java @@ -0,0 +1,97 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal.util.xml; + +import static net.sourceforge.pmd.util.CollectionUtil.setOf; + +import java.util.List; +import java.util.stream.Collectors; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; + +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; + + +/** + * Constants of the ruleset schema. + */ +public class SchemaConstant { + + private final String name; + + + public SchemaConstant(String name) { + this.name = name; + } + + + public boolean getAsBooleanAttr(Element e, boolean defaultValue) { + String attr = e.getAttribute(name); + return attr != null ? Boolean.parseBoolean(attr) : defaultValue; + } + + @NonNull + public String getAttributeOrThrow(Element element, XmlErrorReporter err) { + String attribute = element.getAttribute(name); + if (attribute == null) { + throw err.error(element, XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); + } + + return attribute; + } + + @Nullable + public String getAttributeOpt(Element element) { + String attr = element.getAttribute(name); + return attr.isEmpty() ? null : attr; + } + + @Nullable + public Attr getAttributeNode(Element element) { + return element.getAttributeNode(name); + } + + public List getChildrenIn(Element elt) { + return XmlUtil.getElementChildrenNamed(elt, name) + .collect(Collectors.toList()); + } + + public List getElementChildrenNamedReportOthers(Element elt, XmlErrorReporter err) { + return XmlUtil.getElementChildrenNamedReportOthers(elt, setOf(name), err) + .collect(Collectors.toList()); + } + + public Element getSingleChildIn(Element elt, XmlErrorReporter err) { + return XmlUtil.getSingleChildIn(elt, true, err, setOf(name)); + } + + public Element getOptChildIn(Element elt, XmlErrorReporter err) { + return XmlUtil.getSingleChildIn(elt, false, err, setOf(name)); + } + + public void setOn(Element element, String value) { + element.setAttribute(name, value); + } + + /** + * Returns the String name of this attribute. + * + * @return The attribute's name + */ + public String xmlName() { + return name; + } + + + @Override + public String toString() { + return xmlName(); + } + + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java index 0eed1c8f02..6e504f6e1d 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java @@ -4,106 +4,24 @@ package net.sourceforge.pmd.internal.util.xml; -import static net.sourceforge.pmd.util.CollectionUtil.setOf; - -import java.util.List; -import java.util.stream.Collectors; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; - -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; - /** * Constants of the ruleset schema. */ -public enum SchemaConstants { +public final class SchemaConstants { - /** The type of the property. */ - TYPE("type"), - /** The name of the property. */ - NAME("name"), - /** The description of the property. */ - DESCRIPTION("description"), - /** The default value. */ - PROPERTY_VALUE("value"), + public static final SchemaConstant PROPERTY_TYPE = new SchemaConstant("type"); + public static final SchemaConstant NAME = new SchemaConstant("name"); + public static final SchemaConstant DESCRIPTION = new SchemaConstant("description"); + public static final SchemaConstant PROPERTY_VALUE = new SchemaConstant("value"); - PROPERTY_ELT("property"), + public static final SchemaConstant PROPERTY_ELT = new SchemaConstant("property"); - PROPERTIES("properties"), - DEPRECATED("deprecated"), - - ; // SUPPRESS CHECKSTYLE enum trailing semi is awesome - - private final String name; + public static final SchemaConstant PROPERTIES = new SchemaConstant("properties"); + public static final SchemaConstant DEPRECATED = new SchemaConstant("deprecated"); - SchemaConstants(String name) { - this.name = name; + private SchemaConstants() { + // utility class } - - - public boolean getAsBooleanAttr(Element e, boolean defaultValue) { - String attr = e.getAttribute(name); - return attr != null ? Boolean.parseBoolean(attr) : defaultValue; - } - - @NonNull - public String getAttributeOrThrow(Element element, XmlErrorReporter err) { - String attribute = element.getAttribute(name); - if (attribute == null) { - throw err.error(element, XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); - } - - return attribute; - } - - @Nullable - public String getAttributeOpt(Element element) { - String attr = element.getAttribute(name); - return attr.isEmpty() ? null : attr; - } - - @Nullable - public Attr getAttributeNode(Element element) { - return element.getAttributeNode(name); - } - - public List getChildrenIn(Element elt) { - return XmlUtil.getElementChildrenNamed(elt, name) - .collect(Collectors.toList()); - } - - public List getElementChildrenNamedReportOthers(Element elt, XmlErrorReporter err) { - return XmlUtil.getElementChildrenNamedReportOthers(elt, setOf(name), err) - .collect(Collectors.toList()); - } - - public Element getSingleChildIn(Element elt, XmlErrorReporter err) { - return XmlUtil.getSingleChildIn(elt, err, setOf(name)); - } - - public void setOn(Element element, String value) { - element.setAttribute(name, value); - } - - /** - * Returns the String name of this attribute. - * - * @return The attribute's name - */ - public String xmlName() { - return name; - } - - - @Override - public String toString() { - return xmlName(); - } - - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java index a928c4c948..4bc9fa7d24 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java @@ -84,12 +84,16 @@ public final class XmlUtil { }).collect(Collectors.toList()); } - public static Element getSingleChildIn(Element elt, XmlErrorReporter err, Set names) { + public static Element getSingleChildIn(Element elt, boolean throwOnMissing, XmlErrorReporter err, Set names) { List children = getElementChildrenNamed(elt, names).collect(Collectors.toList()); if (children.size() == 1) { return children.get(0); } else if (children.isEmpty()) { - throw err.error(elt, ERR__MISSING_REQUIRED_ELEMENT, formatPossibleNames(names)); + if (throwOnMissing) { + throw err.error(elt, ERR__MISSING_REQUIRED_ELEMENT, formatPossibleNames(names)); + } else { + return null; + } } else { for (int i = 1; i < children.size(); i++) { Element child = children.get(i); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java index 18abb8a29d..c1cbe155b0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java @@ -76,7 +76,7 @@ public final class RendererFactory { if (value != null) { @SuppressWarnings("unchecked") PropertyDescriptor prop2 = (PropertyDescriptor) prop; - Object valueFrom = prop2.valueFrom(value); + Object valueFrom = prop2.xmlMapper().fromString(value); renderer.setProperty(prop2, valueFrom); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 78d03f73a6..782183d923 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -261,7 +261,7 @@ public class RuleFactory { * @return True if this element defines a new property, false if this is just stating a value */ private static boolean isPropertyDefinition(Element node) { - return node.hasAttribute(SchemaConstants.TYPE.xmlName()); + return node.hasAttribute(SchemaConstants.PROPERTY_TYPE.xmlName()); } /** @@ -274,7 +274,7 @@ public class RuleFactory { */ private static PropertyDescriptor parsePropertyDefinition(Element propertyElement, XmlErrorReporter err) { - String typeId = SchemaConstants.TYPE.getAttributeOrThrow(propertyElement, err); + String typeId = SchemaConstants.PROPERTY_TYPE.getAttributeOrThrow(propertyElement, err); PropertyTypeId factory = PropertyTypeId.lookupMnemonic(typeId); if (factory == null) { @@ -326,7 +326,7 @@ public class RuleFactory { } } else { - Element child = getSingleChildIn(propertyElt, err, syntax.getReadElementNames()); + Element child = getSingleChildIn(propertyElt, true, err, syntax.getReadElementNames()); // this will report the correct error if any return syntax.fromXml(child, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java index 0ae6fad1db..a20cde7456 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java @@ -176,7 +176,7 @@ public class TreeExportCli { Logger.getLogger(Attribute.class.getName()).setLevel(Level.OFF); try (Reader reader = source) { - RootNode root = (RootNode) parser.parse(file, reader); + RootNode root = parser.parse(file, reader); AstAnalysisContext ctx = new AstAnalysisContext() { @Override @@ -217,7 +217,7 @@ public class TreeExportCli { } private static void setProperty(PropertyDescriptor descriptor, PropertySource bundle, String value) { - bundle.setProperty(descriptor, descriptor.valueFrom(value)); + bundle.setProperty(descriptor, descriptor.xmlMapper().fromString(value)); } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index a4a6fa85b6..5c3b2448db 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -7,30 +7,34 @@ package net.sourceforge.pmd.testframework; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Properties; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; @@ -44,11 +48,21 @@ import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.internal.util.xml.SchemaConstants; +import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.renderers.TextRenderer; +import net.sourceforge.pmd.testframework.internal.TestSchemaConstants; + +import com.github.oowekyala.ooxml.messages.DefaultXmlErrorReporter; +import com.github.oowekyala.ooxml.messages.PositionedXmlDoc; +import com.github.oowekyala.ooxml.messages.XmlErrorReporter; +import com.github.oowekyala.ooxml.messages.XmlMessageHandler; +import com.github.oowekyala.ooxml.messages.XmlMessageUtils; /** * Advanced methods for test cases @@ -118,56 +132,27 @@ public abstract class RuleTst { * Run the rule on the given code, and check the expected number of * violations. */ - @SuppressWarnings("unchecked") public void runTest(TestDescriptor test) { Rule rule = test.getRule(); - if (test.getReinitializeRule()) { - rule = reinitializeRule(rule); - } - - Map, Object> oldProperties = rule.getPropertiesByPropertyDescriptor(); + int res; + Report report; try { - int res; - Report report; - try { - // Set test specific properties onto the Rule - if (test.getProperties() != null) { - for (Map.Entry entry : test.getProperties().entrySet()) { - String propertyName = (String) entry.getKey(); - PropertyDescriptor propertyDescriptor = rule.getPropertyDescriptor(propertyName); - if (propertyDescriptor == null) { - throw new IllegalArgumentException( - "No such property '" + propertyName + "' on Rule " + rule.getName()); - } - - Object value = propertyDescriptor.valueFrom((String) entry.getValue()); - rule.setProperty(propertyDescriptor, value); - } - } - - report = processUsingStringReader(test, rule); - res = report.getViolations().size(); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException('"' + test.getDescription() + "\" failed", e); - } - if (test.getNumberOfProblemsExpected() != res) { - printReport(test, report); - } - assertEquals('"' + test.getDescription() + "\" resulted in wrong number of failures,", - test.getNumberOfProblemsExpected(), res); - assertMessages(report, test); - assertLineNumbers(report, test); - } finally { - // Restore old properties - for (Map.Entry, Object> entry : oldProperties.entrySet()) { - rule.setProperty((PropertyDescriptor) entry.getKey(), entry.getValue()); - } + report = processUsingStringReader(test, rule); + res = report.getViolations().size(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException('"' + test.getDescription() + "\" failed", e); } + if (test.getNumberOfProblemsExpected() != res) { + printReport(test, report); + } + assertEquals('"' + test.getDescription() + "\" resulted in wrong number of failures,", + test.getNumberOfProblemsExpected(), res); + assertMessages(report, test); + assertLineNumbers(report, test); } - /** * Code to be executed if the rule is reinitialised. * @@ -338,20 +323,37 @@ public abstract class RuleTst { * should be ./xml/[testsFileName].xml relative to the test class. The * format is defined in test-data.xsd. */ + @SuppressWarnings("PMD.CloseResource") public TestDescriptor[] extractTestsFromXml(Rule rule, String testsFileName, String baseDirectory) { String testXmlFileName = baseDirectory + testsFileName + ".xml"; + InputStream inputStream = getClass().getResourceAsStream(testXmlFileName); + if (inputStream == null) { + throw new RuntimeException("Couldn't find " + testXmlFileName); + } - Document doc; - try (InputStream inputStream = getClass().getResourceAsStream(testXmlFileName)) { - if (inputStream == null) { - throw new RuntimeException("Couldn't find " + testXmlFileName); + try (InputStream is = inputStream; + Reader reader = new BufferedReader(new InputStreamReader(is))) { + InputSource inputSource = new InputSource(testXmlFileName); + inputSource.setCharacterStream(reader); + PositionedXmlDoc positionedXmlDoc = XmlMessageUtils.getInstance().parse(documentBuilder, inputSource, XmlMessageHandler.SYSTEM_ERR); + + try (XmlErrorReporter err = getReporter(positionedXmlDoc)) { + return parseTests(rule, positionedXmlDoc.getDocument(), err); } - doc = documentBuilder.parse(inputStream); - } catch (FactoryConfigurationError | IOException | SAXException e) { + } catch (IOException e) { throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + e, e); } - return parseTests(rule, doc); + } + + @NonNull + private DefaultXmlErrorReporter getReporter(PositionedXmlDoc positionedXmlDoc) { + return new DefaultXmlErrorReporter(XmlMessageHandler.SYSTEM_ERR, positionedXmlDoc.getPositioner()) { + @Override + protected String template(String message, Object... args) { + return new MessageFormat(message).format(args); + } + }; } /** @@ -376,69 +378,46 @@ public abstract class RuleTst { * Run a set of tests of a certain sourceType. */ public void runTests(TestDescriptor[] tests) { - for (int i = 0; i < tests.length; i++) { - runTest(tests[i]); + for (TestDescriptor test : tests) { + runTest(test); } } - private TestDescriptor[] parseTests(Rule rule, Document doc) { + private TestDescriptor[] parseTests(Rule baseRule, Document doc, XmlErrorReporter err) { Element root = doc.getDocumentElement(); NodeList testCodes = root.getElementsByTagName("test-code"); TestDescriptor[] tests = new TestDescriptor[testCodes.getLength()]; for (int i = 0; i < testCodes.getLength(); i++) { + Rule rule = baseRule.deepCopy(); Element testCode = (Element) testCodes.item(i); - boolean reinitializeRule = true; - Node reinitializeRuleAttribute = testCode.getAttributes().getNamedItem("reinitializeRule"); - if (reinitializeRuleAttribute != null) { - String reinitializeRuleValue = reinitializeRuleAttribute.getNodeValue(); - if ("false".equalsIgnoreCase(reinitializeRuleValue) || "0".equalsIgnoreCase(reinitializeRuleValue)) { - reinitializeRule = false; + boolean isRegressionTest = TestSchemaConstants.REGRESSION_TEST.getAsBooleanAttr(testCode, true); + boolean isUseAuxClasspath = TestSchemaConstants.USE_AUXCLASSPATH.getAsBooleanAttr(testCode, true); + + // set properties on the rule copy + for (Element ruleProperty : TestSchemaConstants.RULE_PROPERTY.getChildrenIn(testCode)) { + String name = SchemaConstants.NAME.getAttributeOrThrow(ruleProperty, err); + PropertyDescriptor descriptor = rule.getPropertyDescriptor(name); + if (descriptor == null) { + throw err.error(ruleProperty, XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST, name, rule.getName()); } + + setPropertyCapture(rule, descriptor, ruleProperty, err); } - boolean isRegressionTest = true; - Node regressionTestAttribute = testCode.getAttributes().getNamedItem("regressionTest"); - if (regressionTestAttribute != null) { - String reinitializeRuleValue = regressionTestAttribute.getNodeValue(); - if ("false".equalsIgnoreCase(reinitializeRuleValue)) { - isRegressionTest = false; - } - } - - boolean isUseAuxClasspath = true; - Node useAuxClasspathAttribute = testCode.getAttributes().getNamedItem("useAuxClasspath"); - if (useAuxClasspathAttribute != null) { - String useAuxClasspathValue = useAuxClasspathAttribute.getNodeValue(); - if ("false".equalsIgnoreCase(useAuxClasspathValue)) { - isUseAuxClasspath = false; - } - } - - NodeList ruleProperties = testCode.getElementsByTagName("rule-property"); - Properties properties = new Properties(); - for (int j = 0; j < ruleProperties.getLength(); j++) { - Node ruleProperty = ruleProperties.item(j); - String propertyName = ruleProperty.getAttributes().getNamedItem("name").getNodeValue(); - properties.setProperty(propertyName, parseTextNode(ruleProperty)); - } - - NodeList expectedMessagesNodes = testCode.getElementsByTagName("expected-messages"); List messages = new ArrayList<>(); - if (expectedMessagesNodes != null && expectedMessagesNodes.getLength() > 0) { - Element item = (Element) expectedMessagesNodes.item(0); - NodeList messagesNodes = item.getElementsByTagName("message"); - for (int j = 0; j < messagesNodes.getLength(); j++) { - messages.add(parseTextNode(messagesNodes.item(j))); + @Nullable Element expectedMessagesNodes = TestSchemaConstants.EXPECTED_MESSAGES.getOptChildIn(testCode, err); + if (expectedMessagesNodes != null) { + for (Element message : TestSchemaConstants.MESSAGE.getChildrenIn(expectedMessagesNodes)) { + messages.add(parseTextNode(message)); } } - NodeList expectedLineNumbersNodes = testCode.getElementsByTagName("expected-linenumbers"); List expectedLineNumbers = new ArrayList<>(); - if (expectedLineNumbersNodes != null && expectedLineNumbersNodes.getLength() > 0) { - Element item = (Element) expectedLineNumbersNodes.item(0); - String numbers = item.getTextContent(); + @Nullable Element expectedLineNumbersNodes = TestSchemaConstants.EXPECTED_LINE_NUMBERS.getOptChildIn(testCode, err); + if (expectedLineNumbersNodes != null) { + String numbers = expectedLineNumbersNodes.getTextContent(); for (String n : numbers.split(" *, *")) { expectedLineNumbers.add(Integer.valueOf(n)); } @@ -471,28 +450,35 @@ public abstract class RuleTst { int expectedProblems = Integer.parseInt(getNodeValue(testCode, "expected-problems", true)); String languageVersionString = getNodeValue(testCode, "source-type", false); + final TestDescriptor descriptor; if (languageVersionString == null) { - tests[i] = new TestDescriptor(code, description, expectedProblems, rule); + descriptor = new TestDescriptor(code, description, expectedProblems, rule); } else { LanguageVersion languageVersion = parseSourceType(languageVersionString); if (languageVersion != null) { - tests[i] = new TestDescriptor(code, description, expectedProblems, rule, languageVersion); + descriptor = new TestDescriptor(code, description, expectedProblems, rule, languageVersion); } else { throw new RuntimeException("Unknown LanguageVersion for test: " + languageVersionString); } } - tests[i].setReinitializeRule(reinitializeRule); - tests[i].setRegressionTest(isRegressionTest); - tests[i].setUseAuxClasspath(isUseAuxClasspath); - tests[i].setExpectedMessages(messages); - tests[i].setExpectedLineNumbers(expectedLineNumbers); - tests[i].setProperties(properties); - tests[i].setNumberInDocument(i + 1); + + descriptor.setRegressionTest(isRegressionTest); + descriptor.setUseAuxClasspath(isUseAuxClasspath); + descriptor.setExpectedMessages(messages); + descriptor.setExpectedLineNumbers(expectedLineNumbers); + descriptor.setNumberInDocument(i + 1); + tests[i] = descriptor; } return tests; } + private static void setPropertyCapture(PropertySource properties, PropertyDescriptor descriptor, Element valueElement, XmlErrorReporter err) { + valueElement.setNodeValue("value"); + T value = descriptor.xmlMapper().fromXml(valueElement, err); + properties.setProperty(descriptor, value); + } + /** FIXME this is stupid, the language version may be of a different language than the Rule... */ private static LanguageVersion parseSourceType(String terseNameAndVersion) { final String version; diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java new file mode 100644 index 0000000000..5fe50f86e7 --- /dev/null +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.testframework.internal; + +import net.sourceforge.pmd.internal.util.xml.SchemaConstant; + +/** + * + */ +public final class TestSchemaConstants { + + public static final SchemaConstant EXPECTED_MESSAGES = new SchemaConstant("expected-messages"); + public static final SchemaConstant MESSAGE = new SchemaConstant("message"); + public static final SchemaConstant EXPECTED_LINE_NUMBERS = new SchemaConstant("expected-linenumbers"); + public static final SchemaConstant RULE_PROPERTY = new SchemaConstant("rule-property"); + public static final SchemaConstant USE_AUXCLASSPATH = new SchemaConstant("useAuxClasspath"); + public static final SchemaConstant REGRESSION_TEST = new SchemaConstant("regressionTest"); + + private TestSchemaConstants() { + // utility class + } + +} From eb1491d4008faa68ca31fe03d375af7ee17445a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 19 Jun 2020 23:36:31 +0200 Subject: [PATCH 064/347] REVERT ME Revert changes to test framework This can be merge later --- .../pmd/testframework/RuleTst.java | 200 ++++++++++-------- .../internal/TestSchemaConstants.java | 25 --- 2 files changed, 107 insertions(+), 118 deletions(-) delete mode 100644 pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index 5c3b2448db..a4a6fa85b6 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -7,34 +7,30 @@ package net.sourceforge.pmd.testframework; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Properties; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.apache.commons.lang3.StringUtils; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; @@ -48,21 +44,11 @@ import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.RulesetsFactoryUtils; -import net.sourceforge.pmd.internal.util.xml.SchemaConstants; -import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.renderers.TextRenderer; -import net.sourceforge.pmd.testframework.internal.TestSchemaConstants; - -import com.github.oowekyala.ooxml.messages.DefaultXmlErrorReporter; -import com.github.oowekyala.ooxml.messages.PositionedXmlDoc; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; -import com.github.oowekyala.ooxml.messages.XmlMessageHandler; -import com.github.oowekyala.ooxml.messages.XmlMessageUtils; /** * Advanced methods for test cases @@ -132,27 +118,56 @@ public abstract class RuleTst { * Run the rule on the given code, and check the expected number of * violations. */ + @SuppressWarnings("unchecked") public void runTest(TestDescriptor test) { Rule rule = test.getRule(); - int res; - Report report; + if (test.getReinitializeRule()) { + rule = reinitializeRule(rule); + } + + Map, Object> oldProperties = rule.getPropertiesByPropertyDescriptor(); try { - report = processUsingStringReader(test, rule); - res = report.getViolations().size(); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException('"' + test.getDescription() + "\" failed", e); + int res; + Report report; + try { + // Set test specific properties onto the Rule + if (test.getProperties() != null) { + for (Map.Entry entry : test.getProperties().entrySet()) { + String propertyName = (String) entry.getKey(); + PropertyDescriptor propertyDescriptor = rule.getPropertyDescriptor(propertyName); + if (propertyDescriptor == null) { + throw new IllegalArgumentException( + "No such property '" + propertyName + "' on Rule " + rule.getName()); + } + + Object value = propertyDescriptor.valueFrom((String) entry.getValue()); + rule.setProperty(propertyDescriptor, value); + } + } + + report = processUsingStringReader(test, rule); + res = report.getViolations().size(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException('"' + test.getDescription() + "\" failed", e); + } + if (test.getNumberOfProblemsExpected() != res) { + printReport(test, report); + } + assertEquals('"' + test.getDescription() + "\" resulted in wrong number of failures,", + test.getNumberOfProblemsExpected(), res); + assertMessages(report, test); + assertLineNumbers(report, test); + } finally { + // Restore old properties + for (Map.Entry, Object> entry : oldProperties.entrySet()) { + rule.setProperty((PropertyDescriptor) entry.getKey(), entry.getValue()); + } } - if (test.getNumberOfProblemsExpected() != res) { - printReport(test, report); - } - assertEquals('"' + test.getDescription() + "\" resulted in wrong number of failures,", - test.getNumberOfProblemsExpected(), res); - assertMessages(report, test); - assertLineNumbers(report, test); } + /** * Code to be executed if the rule is reinitialised. * @@ -323,37 +338,20 @@ public abstract class RuleTst { * should be ./xml/[testsFileName].xml relative to the test class. The * format is defined in test-data.xsd. */ - @SuppressWarnings("PMD.CloseResource") public TestDescriptor[] extractTestsFromXml(Rule rule, String testsFileName, String baseDirectory) { String testXmlFileName = baseDirectory + testsFileName + ".xml"; - InputStream inputStream = getClass().getResourceAsStream(testXmlFileName); - if (inputStream == null) { - throw new RuntimeException("Couldn't find " + testXmlFileName); - } - try (InputStream is = inputStream; - Reader reader = new BufferedReader(new InputStreamReader(is))) { - InputSource inputSource = new InputSource(testXmlFileName); - inputSource.setCharacterStream(reader); - PositionedXmlDoc positionedXmlDoc = XmlMessageUtils.getInstance().parse(documentBuilder, inputSource, XmlMessageHandler.SYSTEM_ERR); - - try (XmlErrorReporter err = getReporter(positionedXmlDoc)) { - return parseTests(rule, positionedXmlDoc.getDocument(), err); + Document doc; + try (InputStream inputStream = getClass().getResourceAsStream(testXmlFileName)) { + if (inputStream == null) { + throw new RuntimeException("Couldn't find " + testXmlFileName); } - } catch (IOException e) { + doc = documentBuilder.parse(inputStream); + } catch (FactoryConfigurationError | IOException | SAXException e) { throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + e, e); } - } - - @NonNull - private DefaultXmlErrorReporter getReporter(PositionedXmlDoc positionedXmlDoc) { - return new DefaultXmlErrorReporter(XmlMessageHandler.SYSTEM_ERR, positionedXmlDoc.getPositioner()) { - @Override - protected String template(String message, Object... args) { - return new MessageFormat(message).format(args); - } - }; + return parseTests(rule, doc); } /** @@ -378,46 +376,69 @@ public abstract class RuleTst { * Run a set of tests of a certain sourceType. */ public void runTests(TestDescriptor[] tests) { - for (TestDescriptor test : tests) { - runTest(test); + for (int i = 0; i < tests.length; i++) { + runTest(tests[i]); } } - private TestDescriptor[] parseTests(Rule baseRule, Document doc, XmlErrorReporter err) { + private TestDescriptor[] parseTests(Rule rule, Document doc) { Element root = doc.getDocumentElement(); NodeList testCodes = root.getElementsByTagName("test-code"); TestDescriptor[] tests = new TestDescriptor[testCodes.getLength()]; for (int i = 0; i < testCodes.getLength(); i++) { - Rule rule = baseRule.deepCopy(); Element testCode = (Element) testCodes.item(i); - boolean isRegressionTest = TestSchemaConstants.REGRESSION_TEST.getAsBooleanAttr(testCode, true); - boolean isUseAuxClasspath = TestSchemaConstants.USE_AUXCLASSPATH.getAsBooleanAttr(testCode, true); - - // set properties on the rule copy - for (Element ruleProperty : TestSchemaConstants.RULE_PROPERTY.getChildrenIn(testCode)) { - String name = SchemaConstants.NAME.getAttributeOrThrow(ruleProperty, err); - PropertyDescriptor descriptor = rule.getPropertyDescriptor(name); - if (descriptor == null) { - throw err.error(ruleProperty, XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST, name, rule.getName()); + boolean reinitializeRule = true; + Node reinitializeRuleAttribute = testCode.getAttributes().getNamedItem("reinitializeRule"); + if (reinitializeRuleAttribute != null) { + String reinitializeRuleValue = reinitializeRuleAttribute.getNodeValue(); + if ("false".equalsIgnoreCase(reinitializeRuleValue) || "0".equalsIgnoreCase(reinitializeRuleValue)) { + reinitializeRule = false; } - - setPropertyCapture(rule, descriptor, ruleProperty, err); } + boolean isRegressionTest = true; + Node regressionTestAttribute = testCode.getAttributes().getNamedItem("regressionTest"); + if (regressionTestAttribute != null) { + String reinitializeRuleValue = regressionTestAttribute.getNodeValue(); + if ("false".equalsIgnoreCase(reinitializeRuleValue)) { + isRegressionTest = false; + } + } + + boolean isUseAuxClasspath = true; + Node useAuxClasspathAttribute = testCode.getAttributes().getNamedItem("useAuxClasspath"); + if (useAuxClasspathAttribute != null) { + String useAuxClasspathValue = useAuxClasspathAttribute.getNodeValue(); + if ("false".equalsIgnoreCase(useAuxClasspathValue)) { + isUseAuxClasspath = false; + } + } + + NodeList ruleProperties = testCode.getElementsByTagName("rule-property"); + Properties properties = new Properties(); + for (int j = 0; j < ruleProperties.getLength(); j++) { + Node ruleProperty = ruleProperties.item(j); + String propertyName = ruleProperty.getAttributes().getNamedItem("name").getNodeValue(); + properties.setProperty(propertyName, parseTextNode(ruleProperty)); + } + + NodeList expectedMessagesNodes = testCode.getElementsByTagName("expected-messages"); List messages = new ArrayList<>(); - @Nullable Element expectedMessagesNodes = TestSchemaConstants.EXPECTED_MESSAGES.getOptChildIn(testCode, err); - if (expectedMessagesNodes != null) { - for (Element message : TestSchemaConstants.MESSAGE.getChildrenIn(expectedMessagesNodes)) { - messages.add(parseTextNode(message)); + if (expectedMessagesNodes != null && expectedMessagesNodes.getLength() > 0) { + Element item = (Element) expectedMessagesNodes.item(0); + NodeList messagesNodes = item.getElementsByTagName("message"); + for (int j = 0; j < messagesNodes.getLength(); j++) { + messages.add(parseTextNode(messagesNodes.item(j))); } } + NodeList expectedLineNumbersNodes = testCode.getElementsByTagName("expected-linenumbers"); List expectedLineNumbers = new ArrayList<>(); - @Nullable Element expectedLineNumbersNodes = TestSchemaConstants.EXPECTED_LINE_NUMBERS.getOptChildIn(testCode, err); - if (expectedLineNumbersNodes != null) { - String numbers = expectedLineNumbersNodes.getTextContent(); + if (expectedLineNumbersNodes != null && expectedLineNumbersNodes.getLength() > 0) { + Element item = (Element) expectedLineNumbersNodes.item(0); + String numbers = item.getTextContent(); for (String n : numbers.split(" *, *")) { expectedLineNumbers.add(Integer.valueOf(n)); } @@ -450,35 +471,28 @@ public abstract class RuleTst { int expectedProblems = Integer.parseInt(getNodeValue(testCode, "expected-problems", true)); String languageVersionString = getNodeValue(testCode, "source-type", false); - final TestDescriptor descriptor; if (languageVersionString == null) { - descriptor = new TestDescriptor(code, description, expectedProblems, rule); + tests[i] = new TestDescriptor(code, description, expectedProblems, rule); } else { LanguageVersion languageVersion = parseSourceType(languageVersionString); if (languageVersion != null) { - descriptor = new TestDescriptor(code, description, expectedProblems, rule, languageVersion); + tests[i] = new TestDescriptor(code, description, expectedProblems, rule, languageVersion); } else { throw new RuntimeException("Unknown LanguageVersion for test: " + languageVersionString); } } - - descriptor.setRegressionTest(isRegressionTest); - descriptor.setUseAuxClasspath(isUseAuxClasspath); - descriptor.setExpectedMessages(messages); - descriptor.setExpectedLineNumbers(expectedLineNumbers); - descriptor.setNumberInDocument(i + 1); - tests[i] = descriptor; + tests[i].setReinitializeRule(reinitializeRule); + tests[i].setRegressionTest(isRegressionTest); + tests[i].setUseAuxClasspath(isUseAuxClasspath); + tests[i].setExpectedMessages(messages); + tests[i].setExpectedLineNumbers(expectedLineNumbers); + tests[i].setProperties(properties); + tests[i].setNumberInDocument(i + 1); } return tests; } - private static void setPropertyCapture(PropertySource properties, PropertyDescriptor descriptor, Element valueElement, XmlErrorReporter err) { - valueElement.setNodeValue("value"); - T value = descriptor.xmlMapper().fromXml(valueElement, err); - properties.setProperty(descriptor, value); - } - /** FIXME this is stupid, the language version may be of a different language than the Rule... */ private static LanguageVersion parseSourceType(String terseNameAndVersion) { final String version; diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java deleted file mode 100644 index 5fe50f86e7..0000000000 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/internal/TestSchemaConstants.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.testframework.internal; - -import net.sourceforge.pmd.internal.util.xml.SchemaConstant; - -/** - * - */ -public final class TestSchemaConstants { - - public static final SchemaConstant EXPECTED_MESSAGES = new SchemaConstant("expected-messages"); - public static final SchemaConstant MESSAGE = new SchemaConstant("message"); - public static final SchemaConstant EXPECTED_LINE_NUMBERS = new SchemaConstant("expected-linenumbers"); - public static final SchemaConstant RULE_PROPERTY = new SchemaConstant("rule-property"); - public static final SchemaConstant USE_AUXCLASSPATH = new SchemaConstant("useAuxClasspath"); - public static final SchemaConstant REGRESSION_TEST = new SchemaConstant("regressionTest"); - - private TestSchemaConstants() { - // utility class - } - -} From 211ef2da0f2fed7d95196c84ffb78d074fb46d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 29 Jul 2020 17:10:00 +0200 Subject: [PATCH 065/347] Remove useless method --- .../net/sourceforge/pmd/RuleSetFactory.java | 30 ++----------------- .../pmd/internal/util/xml/XmlUtil.java | 17 +++-------- 2 files changed, 7 insertions(+), 40 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 121e950bed..38b60b0e84 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -35,6 +35,7 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; +import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.MockRule; @@ -451,7 +452,7 @@ public class RuleSetFactory { for (Element node : DomUtils.elementsIn(ruleSetElement)) { String nodeName = node.getNodeName(); - String text = parseTextNode(node); + String text = XmlUtil.parseTextNode(node); switch (nodeName) { case RuleFactory.DESCRIPTION: builder.withDescription(text); @@ -593,7 +594,7 @@ public class RuleSetFactory { String excludedRuleName = excludeElement.getAttribute("name"); excludedRulesCheck.add(excludedRuleName); } else if (isElementNode(child, RuleFactory.PRIORITY)) { - priority = parseTextNode(child).trim(); + priority = XmlUtil.parseTextNode(child).trim(); } } final RuleSetReference ruleSetReference = new RuleSetReference(ref, true, excludedRulesCheck); @@ -829,31 +830,6 @@ public class RuleSetFactory { return node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(name); } - /** - * Parse a String from a textually type node. - * - * @param node - * The node. - * @return The String. - */ - private static String parseTextNode(Node node) { - - final int nodeCount = node.getChildNodes().getLength(); - if (nodeCount == 0) { - return ""; - } - - StringBuilder buffer = new StringBuilder(); - - for (int i = 0; i < nodeCount; i++) { - Node childNode = node.getChildNodes().item(i); - if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) { - buffer.append(childNode.getNodeValue()); - } - } - return buffer.toString(); - } - /** * Determine if the specified rule element will represent a Rule with the diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java index 4bc9fa7d24..fda873ad58 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java @@ -8,7 +8,6 @@ import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__MISSIN import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__DUPLICATE_CHILD_ELEMENT; import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__UNEXPECTED_ELEMENT; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; @@ -18,10 +17,10 @@ import java.util.stream.Stream; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import net.sourceforge.pmd.properties.xml.XmlMapper; +import com.github.oowekyala.ooxml.DomUtils; import com.github.oowekyala.ooxml.messages.XmlErrorReporter; public final class XmlUtil { @@ -30,18 +29,10 @@ public final class XmlUtil { } - public static List toList(NodeList lst) { - ArrayList nodes = new ArrayList<>(); - for (int i = 0; i < lst.getLength(); i++) { - nodes.add(lst.item(i)); - } - return nodes; - } - public static Stream getElementChildren(Element parent) { - return toList(parent.getChildNodes()).stream() - .filter(it -> it.getNodeType() == Node.ELEMENT_NODE) - .map(Element.class::cast); + return DomUtils.asList(parent.getChildNodes()).stream() + .filter(it -> it.getNodeType() == Node.ELEMENT_NODE) + .map(Element.class::cast); } public static Stream getElementChildrenNamed(Element parent, Set names) { From 356c6e0d64e2378edbb0bf1f03b45924268b64c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 29 Jul 2020 17:31:01 +0200 Subject: [PATCH 066/347] Make violation suppress regex a Pattern property --- pmd-core/src/main/java/net/sourceforge/pmd/Rule.java | 8 +++++--- .../java/net/sourceforge/pmd/ViolationSuppressor.java | 6 +++--- .../src/test/java/net/sourceforge/pmd/ReportTest.java | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java index 9005643592..f2a36cd80b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd; import java.util.List; import java.util.Optional; +import java.util.regex.Pattern; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.Language; @@ -29,13 +30,14 @@ import net.sourceforge.pmd.properties.PropertySource; */ public interface Rule extends PropertySource { + // TODO these should not be properties + /** * The property descriptor to universally suppress violations with messages * matching a regular expression. */ - // TODO 7.0.0 use PropertyDescriptor> - PropertyDescriptor> VIOLATION_SUPPRESS_REGEX_DESCRIPTOR = - PropertyFactory.stringProperty("violationSuppressRegex") + PropertyDescriptor> VIOLATION_SUPPRESS_REGEX_DESCRIPTOR = + PropertyFactory.regexProperty("violationSuppressRegex") .desc("Suppress violations with messages matching a regular expression") .toOptional() .defaultValue(Optional.empty()) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java b/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java index d5ecf97192..eb0e261d2f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ViolationSuppressor.java @@ -39,10 +39,10 @@ public interface ViolationSuppressor { @Override public @Nullable SuppressedViolation suppressOrNull(RuleViolation rv, @NonNull Node node) { - Optional regex = rv.getRule().getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR); // Regex + Optional regex = rv.getRule().getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR); // Regex if (regex.isPresent() && rv.getDescription() != null) { - if (Pattern.matches(regex.get(), rv.getDescription())) { - return new SuppressedViolation(rv, this, regex.get()); + if (regex.get().matcher(rv.getDescription()).matches()) { + return new SuppressedViolation(rv, this, regex.get().pattern()); } } return null; diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java index f5a58668cb..e2e2297b01 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ReportTest.java @@ -9,6 +9,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Optional; +import java.util.regex.Pattern; import org.junit.Test; @@ -32,7 +33,7 @@ public class ReportTest extends RuleTst { public void testExclusionsInReportWithRuleViolationSuppressRegex() { Report rpt = new Report(); Rule rule = new FooRule(); - rule.setProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR, Optional.of(".*No Foo.*")); + rule.setProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR, Optional.of(Pattern.compile(".*No Foo.*"))); runTestFromString(TEST1, rule, rpt, defaultLanguage); assertTrue(rpt.getViolations().isEmpty()); assertEquals(1, rpt.getSuppressedViolations().size()); From c0ca9764a33be11f2229d130380e73484b755ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 30 Jan 2022 17:16:55 +0100 Subject: [PATCH 067/347] Remove rule builder, improve error reporting for invalid rule --- .../net/sourceforge/pmd/RulePriority.java | 15 ++ .../net/sourceforge/pmd/RuleSetFactory.java | 23 +- .../pmd/internal/util/xml/SchemaConstant.java | 28 ++- .../internal/util/xml/XmlErrorMessages.java | 5 + .../sourceforge/pmd/rules/RuleBuilder.java | 212 ------------------ .../sourceforge/pmd/rules/RuleFactory.java | 183 ++++++++++----- .../net/sourceforge/pmd/util/StringUtil.java | 15 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 66 +++--- 8 files changed, 224 insertions(+), 323 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulePriority.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulePriority.java index bdfd2c3882..7fb816bd80 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulePriority.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulePriority.java @@ -94,4 +94,19 @@ public enum RulePriority { return LOW; } } + + /** + * Returns the priority which corresponds to the given number as returned by + * {@link RulePriority#getPriority()}. If the number is an invalid value, + * then null will be returned. + * + * @param priority The numeric priority value. + */ + public static RulePriority valueOfNullable(int priority) { + try { + return RulePriority.values()[priority - 1]; + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 697ce6f92e..1d189988d2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -134,7 +134,7 @@ final class RuleSetFactory { * * @return The new RuleSet. * - * @throws RulesetParseException If the ruleset cannot be parsed (eg IO exception, malformed XML, validation errors) + * @throws RuleSetLoadException If the ruleset cannot be parsed (eg IO exception, malformed XML, validation errors) */ private RuleSet readDocument(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { @@ -163,27 +163,12 @@ final class RuleSetFactory { err.close(e.getSeverity(), Severity.ERROR); throw e; } - } catch (ParserConfigurationException | IOException | XmlException ex) { - if (!(ex instanceof XmlException)) { // NOPMD would already have been reported - ex.printStackTrace(); - } - throw new RulesetParseException("Couldn't read the ruleset " + ruleSetReferenceId, ex); + } catch (ParserConfigurationException | IOException ex) { + throw new RuleSetLoadException("Couldn't read the ruleset " + ruleSetReferenceId, ex); } } - static class RulesetParseException extends RuntimeException { - - RulesetParseException(String message, Throwable cause) { - super(message, cause); - } - - RulesetParseException(Throwable cause) { - super(cause); - } - } - - @NonNull - private AccumulatingErrorReporter makeReporter(LoggerMessageHandler handler, PositionedXmlDoc parsed) { + private @NonNull AccumulatingErrorReporter makeReporter(LoggerMessageHandler handler, PositionedXmlDoc parsed) { return new AccumulatingErrorReporter(handler, parsed.getPositioner(), Severity.WARNING) { @Override protected String template(String message, Object... args) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java index 3f6621f452..78c643432f 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java @@ -9,6 +9,7 @@ import static net.sourceforge.pmd.util.CollectionUtil.setOf; import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; @@ -32,30 +33,41 @@ public class SchemaConstant { public boolean getAsBooleanAttr(Element e, boolean defaultValue) { String attr = e.getAttribute(name); - return attr != null ? Boolean.parseBoolean(attr) : defaultValue; + return e.hasAttribute(name) ? Boolean.parseBoolean(attr) : defaultValue; } - @NonNull - public String getAttributeOrThrow(Element element, XmlErrorReporter err) { + public @NonNull String getAttributeOrThrow(Element element, XmlErrorReporter err) { String attribute = element.getAttribute(name); - if (attribute == null) { + if (!element.hasAttribute(name)) { throw err.error(element, XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); } return attribute; } - @Nullable - public String getAttributeOpt(Element element) { + public @NonNull String getNonBlankAttributeOrThrow(Element element, XmlErrorReporter err) { + String attribute = element.getAttribute(name); + if (!element.hasAttribute(name)) { + throw err.error(element, XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); + } else if (StringUtils.isBlank(attribute)) { + throw err.error(element, XmlErrorMessages.ERR__BLANK_REQUIRED_ATTRIBUTE, name); + } + return attribute; + } + + public @Nullable String getAttributeOpt(Element element) { String attr = element.getAttribute(name); return attr.isEmpty() ? null : attr; } - @Nullable - public Attr getAttributeNode(Element element) { + public @Nullable Attr getAttributeNode(Element element) { return element.getAttributeNode(name); } + public boolean hasAttribute(Element element) { + return element.hasAttribute(name); + } + public List getChildrenIn(Element elt) { return XmlUtil.getElementChildrenNamed(elt, name) .collect(Collectors.toList()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java index 5227dbdf8f..a701c70fd2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java @@ -11,6 +11,7 @@ public final class XmlErrorMessages { public static final String ERR__UNEXPECTED_ELEMENT = "Unexpected element ''{0}'', expecting {1}"; public static final String ERR__UNEXPECTED_ELEMENT_IN = "Unexpected element ''{0}'' in {1}, expecting {1}"; public static final String ERR__MISSING_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is missing"; + public static final String ERR__BLANK_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is blank"; public static final String ERR__MISSING_REQUIRED_ELEMENT = "Required child element named {0} is missing"; public static final String IGNORED__UNEXPECTED_ELEMENT = ERR__UNEXPECTED_ELEMENT + THIS_WILL_BE_IGNORED; @@ -21,8 +22,12 @@ public final class XmlErrorMessages { public static final String ERR__PROPERTY_DOES_NOT_EXIST = "Cannot set non-existent property ''{0}'' on rule {1}"; public static final String ERR__CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied: {0}"; public static final String ERR__LIST_CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied on items"; + public static final String ERR__INVALID_VERSION_RANGE = "Invalid language version range, minimum version ''{0}'' is greater than maximum version ''{1}''"; + public static final String ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION = "Invalid language version ''{0}'' for language ''{1}'', the language has no named versions"; + public static final String ERR__INVALID_LANG_VERSION = "Invalid language version ''{0}'' for language ''{1}'', supported versions are {2}"; public static final String WARN__DEPRECATED_USE_OF_ATTRIBUTE = "The use of the ''{0}'' attribute is deprecated. Use a nested element, e.g. {1}"; + public static final String WARN__INVALID_PRIORITY_VALUE = "Not a valid priority ''{}'', expected a number in [1,5]"; private XmlErrorMessages() { // utility class diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java deleted file mode 100644 index 7febb8723d..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java +++ /dev/null @@ -1,212 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.rules; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.w3c.dom.Element; - -import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.RulePriority; -import net.sourceforge.pmd.RuleSetReference; -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.util.ResourceLoader; - - -/** - * Builds a rule, validating its parameters throughout. The builder can define property descriptors, but not override - * them. For that, use {@link RuleFactory#decorateRule(Rule, RuleSetReference, Element, com.github.oowekyala.ooxml.messages.XmlErrorReporter)}. - * - * @author Clรฉment Fournier - * @since 6.0.0 - */ -@InternalApi -@Deprecated -public class RuleBuilder { - - private List> definedProperties = new ArrayList<>(); - private String name; - private ResourceLoader resourceLoader; - private String clazz; - private Language language; - private String minimumVersion; - private String maximumVersion; - private String since; - private String message; - private String externalInfoUrl; - private String description; - private List examples = new ArrayList<>(1); - private RulePriority priority; - private boolean isDeprecated; - - /** - * @deprecated Use {@link #RuleBuilder(String, ResourceLoader, String, String)} with the - * proper {@link ResourceLoader} instead. The resource loader is used to load the - * rule implementation class from the class path. - */ - @Deprecated - public RuleBuilder(String name, String clazz, String language) { - this(name, new ResourceLoader(), clazz, language); - } - - public RuleBuilder(String name, ResourceLoader resourceLoader, String clazz, String language) { - this.name = name; - this.resourceLoader = resourceLoader; - language(language); - className(clazz); - } - - private void language(String languageName) { - if (StringUtils.isBlank(languageName)) { - // Some languages don't need the attribute because the rule's - // constructor calls setLanguage, see e.g. AbstractJavaRule - return; - } - - Language lang = LanguageRegistry.findLanguageByTerseName(languageName); - if (lang == null) { - throw new IllegalArgumentException( - "Unknown Language '" + languageName + "' for rule" + name + ", supported Languages are " - + LanguageRegistry.getLanguages().stream().map(Language::getTerseName).collect(Collectors.joining(", ")) - ); - } - language = lang; - } - - private void className(String className) { - if (StringUtils.isBlank(className)) { - throw new IllegalArgumentException("The 'class' field of rule can't be null, nor empty."); - } - - this.clazz = className; - } - - public void minimumLanguageVersion(String minimum) { - minimumVersion = minimum; - } - - public void maximumLanguageVersion(String maximum) { - maximumVersion = maximum; - } - - private void checkLanguageVersionsAreOrdered(Rule rule) { - if (rule.getMinimumLanguageVersion() != null && rule.getMaximumLanguageVersion() != null - && rule.getMinimumLanguageVersion().compareTo(rule.getMaximumLanguageVersion()) > 0) { - throw new IllegalArgumentException( - "The minimum Language Version '" + rule.getMinimumLanguageVersion().getTerseName() - + "' must be prior to the maximum Language Version '" - + rule.getMaximumLanguageVersion().getTerseName() + "' for Rule '" + name - + "'; perhaps swap them around?"); - } - } - - public void since(String sinceStr) { - if (StringUtils.isNotBlank(sinceStr)) { - since = sinceStr; - } - } - - public void externalInfoUrl(String externalInfoUrl) { - this.externalInfoUrl = externalInfoUrl; - } - - public void message(String message) { - this.message = message; - } - - public void defineProperty(PropertyDescriptor descriptor) { - definedProperties.add(descriptor); - } - - - public void setDeprecated(boolean deprecated) { - isDeprecated = deprecated; - } - - - public void description(String description) { - this.description = description; - } - - - public void addExample(String example) { - examples.add(example); - } - - - public void priority(int priorityString) { - this.priority = RulePriority.valueOf(priorityString); - } - - // Must be loaded after rule construction to know the Language - private void loadLanguageMinMaxVersions(Rule rule) { - - if (minimumVersion != null) { - LanguageVersion minimumLanguageVersion = rule.getLanguage().getVersion(minimumVersion); - if (minimumLanguageVersion == null) { - throwUnknownLanguageVersionException("minimum", minimumVersion, rule.getLanguage()); - } else { - rule.setMinimumLanguageVersion(minimumLanguageVersion); - } - } - - if (maximumVersion != null) { - LanguageVersion maximumLanguageVersion = rule.getLanguage().getVersion(maximumVersion); - if (maximumLanguageVersion == null) { - throwUnknownLanguageVersionException("maximum", maximumVersion, rule.getLanguage()); - } else { - rule.setMaximumLanguageVersion(maximumLanguageVersion); - } - } - - checkLanguageVersionsAreOrdered(rule); - } - - private void throwUnknownLanguageVersionException(String minOrMax, String unknownVersion, Language lang) { - throw new IllegalArgumentException("Unknown " + minOrMax + " Language Version '" + unknownVersion - + "' for Language '" + lang.getTerseName() - + "' for Rule " + name - + "; supported Language Versions are: " - + lang.getVersions().stream().map(LanguageVersion::getVersion).collect(Collectors.joining(", "))); - } - - public Rule build() throws ClassNotFoundException, IllegalAccessException, InstantiationException { - Rule rule = resourceLoader.loadRuleFromClassPath(clazz); - - rule.setName(name); - rule.setRuleClass(clazz); - - if (rule.getLanguage() == null) { - rule.setLanguage(language); - } - - loadLanguageMinMaxVersions(rule); - rule.setSince(since); - rule.setMessage(message); - rule.setExternalInfoUrl(externalInfoUrl); - rule.setDeprecated(isDeprecated); - rule.setDescription(description); - rule.setPriority(priority == null ? RulePriority.LOW : priority); - - for (String example : examples) { - rule.addExample(example); - } - - for (PropertyDescriptor descriptor : definedProperties) { - if (!rule.getPropertyDescriptors().contains(descriptor)) { - rule.definePropertyDescriptor(descriptor); - } - } - - return rule; - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 28fc6ebfba..4ae25b9f04 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -4,19 +4,20 @@ package net.sourceforge.pmd.rules; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PROPERTY_TYPE; import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PROPERTY_VALUE; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION; +import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION; import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST; import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE; import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER; import static net.sourceforge.pmd.internal.util.xml.XmlUtil.getSingleChildIn; -import static net.sourceforge.pmd.internal.util.xml.XmlUtil.parseTextNode; -import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; @@ -29,12 +30,16 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.DOMUtils; import net.sourceforge.pmd.internal.util.xml.SchemaConstants; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.util.ResourceLoader; +import net.sourceforge.pmd.util.StringUtil; import com.github.oowekyala.ooxml.DomUtils; import com.github.oowekyala.ooxml.messages.XmlErrorReporter; @@ -52,6 +57,7 @@ public class RuleFactory { private static final String DEPRECATED = "deprecated"; private static final String NAME = "name"; + private static final String LANGUAGE = "language"; private static final String MESSAGE = "message"; private static final String EXTERNAL_INFO_URL = "externalInfoUrl"; private static final String MINIMUM_LANGUAGE_VERSION = "minimumLanguageVersion"; @@ -65,8 +71,6 @@ public class RuleFactory { public static final String DESCRIPTION = "description"; private static final String CLASS = "class"; - private static final List REQUIRED_ATTRIBUTES = Collections.unmodifiableList(Arrays.asList(NAME, CLASS)); - private final ResourceLoader resourceLoader; /** @@ -120,9 +124,11 @@ public class RuleFactory { setPropertyValues(ruleReference, node, err); break; default: - throw err.error(node, - XmlErrorMessages.ERR__UNEXPECTED_ELEMENT_IN, - "rule " + ruleReference.getName()); + throw err.error( + node, + XmlErrorMessages.ERR__UNEXPECTED_ELEMENT_IN, + "rule " + ruleReference.getName() + ); } } @@ -142,40 +148,46 @@ public class RuleFactory { * @throws IllegalArgumentException if the element doesn't describe a valid rule. */ public Rule buildRule(Element ruleElement, XmlErrorReporter err) { - checkRequiredAttributesArePresent(ruleElement); - RuleBuilder builder = new RuleBuilder( - ruleElement.getAttribute(NAME), - resourceLoader, - ruleElement.getAttribute(CLASS), - ruleElement.getAttribute("language") - ); + Rule rule; + try { + String clazz = getNonBlankAttribute(ruleElement, err, CLASS); + rule = resourceLoader.loadRuleFromClassPath(clazz); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { + throw err.fatal(ruleElement.getAttributeNode(CLASS), e); + } - DomUtils.getAttributeOpt(ruleElement, MINIMUM_LANGUAGE_VERSION).ifPresent(builder::minimumLanguageVersion); - DomUtils.getAttributeOpt(ruleElement, MAXIMUM_LANGUAGE_VERSION).ifPresent(builder::maximumLanguageVersion); - DomUtils.getAttributeOpt(ruleElement, SINCE).ifPresent(builder::since); + rule.setName(getNonBlankAttribute(ruleElement, err, NAME)); + if (rule.getLanguage() == null) { + setLanguage(ruleElement, err, rule); + } + Language language = rule.getLanguage(); + assert language != null; - builder.message(ruleElement.getAttribute(MESSAGE)); - builder.externalInfoUrl(ruleElement.getAttribute(EXTERNAL_INFO_URL)); - builder.setDeprecated(SchemaConstants.DEPRECATED.getAsBooleanAttr(ruleElement, false)); - - Element propertiesElement = null; + rule.setMinimumLanguageVersion(getLanguageVersion(ruleElement, err, language, MINIMUM_LANGUAGE_VERSION)); + rule.setMaximumLanguageVersion(getLanguageVersion(ruleElement, err, language, MAXIMUM_LANGUAGE_VERSION)); + checkVersionsAreOrdered(ruleElement, err, rule); + DomUtils.getAttributeOpt(ruleElement, SINCE).ifPresent(rule::setSince); + DomUtils.getAttributeOpt(ruleElement, MESSAGE).ifPresent(rule::setMessage); + DomUtils.getAttributeOpt(ruleElement, EXTERNAL_INFO_URL).ifPresent(rule::setExternalInfoUrl); + rule.setDeprecated(SchemaConstants.DEPRECATED.getAsBooleanAttr(ruleElement, false)); for (Element node : DomUtils.elementsIn(ruleElement)) { switch (node.getNodeName()) { case DESCRIPTION: - builder.description(DOMUtils.parseTextNode(node)); + rule.setDescription(DOMUtils.parseTextNode(node)); break; case EXAMPLE: - builder.addExample(DOMUtils.parseTextNode(node)); + rule.addExample(DOMUtils.parseTextNode(node)); break; case PRIORITY: - builder.priority(Integer.parseInt(DOMUtils.parseTextNode(node).trim())); + RulePriority rp = parsePriority(err, node, DOMUtils.parseTextNode(node)); + rule.setPriority(rp); break; case PROPERTIES: - parsePropertiesForDefinitions(builder, node, err); - propertiesElement = node; + parsePropertiesForDefinitions(rule, node, err); + setPropertyValues(rule, node, err); break; default: throw err.error(node, @@ -184,42 +196,102 @@ public class RuleFactory { } } - Rule rule; - try { - rule = builder.build(); - } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - throw err.fatal(ruleElement, e); - } - - if (propertiesElement != null) { - setPropertyValues(rule, propertiesElement, err); - } - return rule; } - private void checkRequiredAttributesArePresent(Element ruleElement) { - // add an attribute name here to make it required + private void checkVersionsAreOrdered(Element ruleElement, XmlErrorReporter err, Rule rule) { + if (rule.getMinimumLanguageVersion() != null && rule.getMaximumLanguageVersion() != null + && rule.getMinimumLanguageVersion().compareTo(rule.getMaximumLanguageVersion()) > 0) { + throw err.fatal( + ruleElement.getAttributeNode(MINIMUM_LANGUAGE_VERSION), + XmlErrorMessages.ERR__INVALID_VERSION_RANGE, + rule.getMinimumLanguageVersion(), + rule.getMaximumLanguageVersion() + ); + } + } - for (String att : REQUIRED_ATTRIBUTES) { - if (!ruleElement.hasAttribute(att)) { - throw new IllegalArgumentException("Missing '" + att + "' attribute"); + + private @NonNull RulePriority parsePriority(XmlErrorReporter err, Element node, String trim) { + try { + int i = Integer.parseInt(trim.trim()); + RulePriority rp = RulePriority.valueOfNullable(i); + if (rp == null) { + err.warn(node, XmlErrorMessages.WARN__INVALID_PRIORITY_VALUE, i); + return RulePriority.MEDIUM; + } else { + return rp; + } + } catch (NumberFormatException e) { + err.warn(node, XmlErrorMessages.WARN__INVALID_PRIORITY_VALUE, trim); + return RulePriority.MEDIUM; + } + } + + private LanguageVersion getLanguageVersion(Element ruleElement, XmlErrorReporter err, Language language, String attrName) { + if (ruleElement.hasAttribute(attrName)) { + String attrValue = ruleElement.getAttribute(attrName); + LanguageVersion version = language.getVersion(attrValue); + if (version == null) { + String supportedVersions = language.getVersions().stream() + .map(LanguageVersion::getVersion) + .filter(it -> !it.isEmpty()) + .map(StringUtil::inSingleQuotes) + .collect(Collectors.joining(", ")); + String message = supportedVersions.isEmpty() + ? ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION + : ERR__INVALID_LANG_VERSION; + throw err.fatal( + ruleElement.getAttributeNode(attrName), + message, + attrValue, + language.getTerseName(), + supportedVersions + ); + } + return version; + } + return null; + } + + private void setLanguage(Element ruleElement, XmlErrorReporter err, Rule rule) { + String langId = getNonBlankAttribute(ruleElement, err, LANGUAGE); + Language lang = LanguageRegistry.findLanguageByTerseName(langId); + if (lang == null) { + throw err.fatal(ruleElement.getAttributeNode(LANGUAGE), "Invalid language ''{0}'', possible values are {1}", langId, supportedLanguages()); + } + rule.setLanguage(lang); + } + + private @NonNull String supportedLanguages() { + return LanguageRegistry.getLanguages().stream().map(Language::getTerseName).map(StringUtil::inSingleQuotes).collect(Collectors.joining(", ")); + } + + private @NonNull String getNonBlankAttribute(Element ruleElement, XmlErrorReporter err, String attrName) { + String clazz = ruleElement.getAttribute(attrName); + if (StringUtils.isBlank(clazz)) { + Attr attr = ruleElement.getAttributeNode(attrName); + if (attr == null) { + throw err.fatal(ruleElement, "Missing {0} attribute", attrName); + } else { + throw err.fatal(attr, "This attribute may not be blank"); } } + return clazz; } /** * Parses the properties node and adds property definitions to the builder. Doesn't care for value overriding, that * will be handled after the rule instantiation. * - * @param builder Rule builder + * @param rule Rule builder * @param propertiesNode Node to parse * @param err Error reporter */ - private void parsePropertiesForDefinitions(RuleBuilder builder, Element propertiesNode, @NonNull XmlErrorReporter err) { + private void parsePropertiesForDefinitions(Rule rule, Element propertiesNode, @NonNull XmlErrorReporter err) { for (Element child : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesNode, err)) { if (isPropertyDefinition(child)) { - builder.defineProperty(parsePropertyDefinition(child, err)); + rule.definePropertyDescriptor(parsePropertyDefinition(child, err)); } } } @@ -262,7 +334,7 @@ public class RuleFactory { * @return True if this element defines a new property, false if this is just stating a value */ private static boolean isPropertyDefinition(Element node) { - return node.hasAttribute(SchemaConstants.PROPERTY_TYPE.xmlName()); + return SchemaConstants.PROPERTY_TYPE.hasAttribute(node); } /** @@ -279,7 +351,11 @@ public class RuleFactory { PropertyTypeId factory = PropertyTypeId.lookupMnemonic(typeId); if (factory == null) { - throw new IllegalArgumentException("No property descriptor factory for type: " + typeId); + throw err.fatal( + PROPERTY_TYPE.getAttributeNode(propertyElement), + "Unsupported property type ''{0}''", + typeId + ); } return propertyDefCapture(propertyElement, err, factory.getBuilderUtils()); @@ -290,8 +366,9 @@ public class RuleFactory { BuilderAndMapper factory) { // TODO support constraints like numeric range - String name = SchemaConstants.NAME.getAttributeOrThrow(propertyElement, err); - String description = SchemaConstants.DESCRIPTION.getAttributeOrThrow(propertyElement, err); + String name = SchemaConstants.NAME.getNonBlankAttributeOrThrow(propertyElement, err); + String description = SchemaConstants.DESCRIPTION.getNonBlankAttributeOrThrow(propertyElement, err); + try { return factory.newBuilder(name) @@ -301,7 +378,7 @@ public class RuleFactory { } catch (IllegalArgumentException e) { // builder threw, rethrow with XML location - throw err.error(propertyElement, e); + throw err.error(propertyElement, e.getMessage()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java index d25bf1027a..3e100095a9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java @@ -29,14 +29,21 @@ public final class StringUtil { private static final String[] EMPTY_STRINGS = new String[0]; private static final Pattern XML_10_INVALID_CHARS = Pattern.compile( - "\\x00|\\x01|\\x02|\\x03|\\x04|\\x05|\\x06|\\x07|\\x08|" - + "\\x0b|\\x0c|\\x0e|\\x0f|" - + "\\x10|\\x11|\\x12|\\x13|\\x14|\\x15|\\x16|\\x17|\\x18|" - + "\\x19|\\x1a|\\x1b|\\x1c|\\x1d|\\x1e|\\x1f"); + "\\x00|\\x01|\\x02|\\x03|\\x04|\\x05|\\x06|\\x07|\\x08|" + + "\\x0b|\\x0c|\\x0e|\\x0f|" + + "\\x10|\\x11|\\x12|\\x13|\\x14|\\x15|\\x16|\\x17|\\x18|" + + "\\x19|\\x1a|\\x1b|\\x1c|\\x1d|\\x1e|\\x1f"); private StringUtil() { } + public static String inSingleQuotes(String s) { + if (s == null) { + s = ""; + } + return "'" + s + "'"; + } + /** * Returns the (1-based) line number of the character at the given index. diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index e735af11eb..3abe49d17e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -4,13 +4,13 @@ package net.sourceforge.pmd; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isA; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -29,7 +29,6 @@ import org.junit.Assert; import org.junit.Test; import org.junit.rules.ExpectedException; -import net.sourceforge.pmd.RuleSetFactory.RulesetParseException; import net.sourceforge.pmd.junit.JavaUtilLoggingRule; import net.sourceforge.pmd.junit.LocaleRule; import net.sourceforge.pmd.lang.DummyLanguageModule; @@ -418,7 +417,7 @@ public class RuleSetFactoryTest { @Test public void testExternalReferenceOverrideNonExistent() { - ex.expect(RulesetParseException.class); + ex.expect(RuleSetLoadException.class); ex.expectCause(allOf(isA(XmlException.class), hasProperty("simpleMessage", is("Cannot set non-existent property 'test4' on rule TestNameOverride")))); loadFirstRule(REF_OVERRIDE_NONEXISTENT); @@ -601,26 +600,32 @@ public class RuleSetFactoryTest { @Test public void testIncorrectMinimumLanguageVersion() { - ex.expect(IllegalArgumentException.class); - ex.expectMessage(containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - loadFirstRule(INCORRECT_MINIMUM_LANGUAGE_VERSION); + RuleSetLoadException ex = assertCannotParse(INCORRECT_MINIMUM_LANGUAGE_VERSION); + Throwable cause = ex.getCause(); + assertThat(cause, instanceOf(XmlException.class)); + assertThat(cause.getMessage(), containsString("valid language version")); + assertThat(cause.getMessage(), containsString("'1.0', '1.1', '1.2'")); // and not "dummy 1.0, dummy 1.1, ..." } @Test public void testIncorrectMinimumLanguageVersionWithLanguageSetInJava() { - assertCannotParse("\n" - + "\n" - + " TODO\n" - + "\n" - + " \n" - + " TODO\n" - + " 2\n" - + " \n" - + "\n" - + ""); + RuleSetLoadException ex = + assertCannotParse("\n" + + "\n" + + " TODO\n" + + "\n" + + " \n" + + " TODO\n" + + " 2\n" + + " \n" + + "\n" + + ""); + Throwable cause = ex.getCause(); + assertThat(cause, instanceOf(XmlException.class)); + assertThat(cause.getMessage(), containsString("valid language version")); } @Test @@ -631,15 +636,20 @@ public class RuleSetFactoryTest { } @Test - public void testIncorrectMaximumLanguageVersion() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage(containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - loadFirstRule(INCORRECT_MAXIMUM_LANGUAGE_VERSION); + public void testIncorrectMaximumLanguageVersion() { + RuleSetLoadException ex = assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); + Throwable cause = ex.getCause(); + assertThat(cause, instanceOf(XmlException.class)); + assertThat(cause.getMessage(), containsString("valid language version")); + assertThat(cause.getMessage(), containsString("'1.0', '1.1', '1.2'")); // and not "dummy 1.0, dummy 1.1, ..." } @Test public void testInvertedMinimumMaximumLanguageVersions() { - assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); + RuleSetLoadException ex = assertCannotParse(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS); + Throwable cause = ex.getCause(); + assertThat(cause, instanceOf(XmlException.class)); + assertThat(cause.getMessage(), containsString("version range")); } @Test @@ -1039,8 +1049,6 @@ public class RuleSetFactoryTest { + "\n" + "\n" + "\n" - + "\n" - + "\n" + "\n" + "\n" + "\n" @@ -1228,4 +1236,8 @@ public class RuleSetFactoryTest { return new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).loadFromString("testRuleset.xml", ruleSetXml); } + private RuleSetLoadException assertCannotParse(String xmlContent) { + return assertThrows(RuleSetLoadException.class, () -> loadFirstRule(xmlContent)); + } + } From 60914cb9c09b0b8171a17d1b138169500faaaaf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 30 Jan 2022 18:19:55 +0100 Subject: [PATCH 068/347] Change default delimiter for strings --- .../net/sourceforge/pmd/RuleSetFactory.java | 1 + .../pmd/properties/PropertyBuilder.java | 31 +++++++++++++++++-- .../pmd/properties/PropertyFactory.java | 26 +++++++++++----- .../pmd/properties/xml/XmlSyntaxUtils.java | 19 +++++++----- .../sourceforge/pmd/RuleSetFactoryTest.java | 2 +- .../properties/PropertyDescriptorTest.java | 6 +++- 6 files changed, 65 insertions(+), 20 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 1d189988d2..7f04ced64d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -329,6 +329,7 @@ final class RuleSetFactory { * The RuleSet reference. * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ + // todo error reporting private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref, Set rulesetReferences) { String priority = null; NodeList childNodes = ruleElement.getChildNodes(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 6f0beb134c..df6eda13e2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -412,6 +412,8 @@ public abstract class PropertyBuilder, T> { private XmlMapper itemParser; private final Collector collector; private final List> collectionConstraints = new ArrayList<>(); + private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; + private boolean allowsStringSyntaxIfPossible = true; /** @@ -449,6 +451,30 @@ public abstract class PropertyBuilder, T> { return this; } + /** + * Specify a delimiter character. By default it's {@value PropertyFactory#DEFAULT_DELIMITER}. + * This is only used for properties that are parsed from a value attribute. + * If the item type is not parsable from a string, then the delimiter + * is ignored as the property can only be parsed using the {@code } syntax. + * + * @param delim Delimiter + * + * @return The same builder + */ + public GenericCollectionPropertyBuilder delim(char delim) { + this.multiValueDelimiter = delim; + return this; + } + + /** + * Specify that this property may not be parsed from a string. + * This is the case for lists of patterns, for instance. + */ + GenericCollectionPropertyBuilder onlyAllowSeqSyntax() { + this.allowsStringSyntaxIfPossible = false; + return this; + } + /** * Specify default values. To specify an empty @@ -489,11 +515,10 @@ public abstract class PropertyBuilder, T> { return this; } - @Override public PropertyDescriptor build() { - XmlMapper syntax = itemParser.supportsStringMapping() - ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, PropertyFactory.DEFAULT_DELIMITER) + XmlMapper syntax = itemParser.supportsStringMapping() && allowsStringSyntaxIfPossible + ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, multiValueDelimiter) : XmlSyntaxUtils.onlySeq(itemParser, collector); syntax = XmlSyntaxUtils.withAllConstraints(syntax, collectionConstraints); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 61311adcc6..9832ad2bb5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; @@ -87,8 +88,8 @@ import net.sourceforge.pmd.util.CollectionUtil; public final class PropertyFactory { - /** Default delimiter for multi-valued properties other than numeric ones. */ - static final char DEFAULT_DELIMITER = ','; + /** Default delimiter for all multi-valued properties. */ + public static final char DEFAULT_DELIMITER = ','; private PropertyFactory() { @@ -207,9 +208,6 @@ public final class PropertyFactory { * as pattern compilation, including syntax errors, are handled transparently to * the rule. * - *

This type of property is not available as a list, because the delimiters - * could be part of the regex. This restriction will be lifted with 7.0.0. - * * @param name Name of the property to build * * @return A new builder @@ -218,12 +216,27 @@ public final class PropertyFactory { return new RegexPropertyBuilder(name); } + /** + * Returns a builder for a property having as value a list of regex patterns. + * The format of the individual items is the same as for {@linkplain #regexProperty(String) regexProperty}. + * This property may only be written with the structured {@code } syntax + * in an XML ruleset. + * + * @param name Name of the property to build + * + * @return A new builder + */ + public static GenericCollectionPropertyBuilder> regexListProperty(String name) { + return regexProperty(name).toList().onlyAllowSeqSyntax(); + } + /** * Returns a builder for a string property. The property descriptor * will accept any string, and performs no expansion of escape * sequences (e.g. {@code \n} in the XML will be represented as the * character sequence '\' 'n' and not the line-feed character '\n'). + * The value of a string attribute is trimmed. * This behaviour could be changed with PMD 7.0.0. * * @param name Name of the property to build @@ -312,9 +325,6 @@ public final class PropertyFactory { * * @return A new builder */ - // Note: there is a bug, whereby the default value can be set on - // the builder, even if it wasn't registered in the constants - // This is fixed in the framework refactoring public static GenericPropertyBuilder enumProperty(String name, Map nameToValue) { XmlMapper parser = enumerationParser( nameToValue, diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 750df3f082..74fc94a69d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -20,6 +20,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.internal.util.xml.XmlUtil; +import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; /** @@ -28,7 +29,7 @@ import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @InternalApi public final class XmlSyntaxUtils { - public static final ValueSyntax STRING = ValueSyntax.withDefaultToString(Function.identity()); + public static final ValueSyntax STRING = ValueSyntax.withDefaultToString(String::trim); public static final ValueSyntax CHARACTER = ValueSyntax.partialFunction( c -> Character.toString(c), @@ -39,10 +40,10 @@ public final class XmlSyntaxUtils { )); public static final ValueSyntax REGEX = ValueSyntax.withDefaultToString(Pattern::compile); - public static final ValueSyntax INTEGER = ValueSyntax.withDefaultToString(Integer::valueOf); - public static final ValueSyntax LONG = ValueSyntax.withDefaultToString(Long::valueOf); - public static final ValueSyntax BOOLEAN = ValueSyntax.withDefaultToString(Boolean::valueOf); - public static final ValueSyntax DOUBLE = ValueSyntax.withDefaultToString(Double::valueOf); + public static final ValueSyntax INTEGER = ValueSyntax.withDefaultToString(preTrim(Integer::valueOf)); + public static final ValueSyntax LONG = ValueSyntax.withDefaultToString(preTrim(Long::valueOf)); + public static final ValueSyntax BOOLEAN = ValueSyntax.withDefaultToString(preTrim(Boolean::valueOf)); + public static final ValueSyntax DOUBLE = ValueSyntax.withDefaultToString(preTrim(Double::valueOf)); public static final XmlMapper> INTEGER_LIST = numberList(INTEGER); @@ -58,11 +59,15 @@ public final class XmlSyntaxUtils { private static XmlMapper> numberList(ValueSyntax valueSyntax) { - return seqAndDelimited(valueSyntax, Collectors.toList(), true, ','); + return seqAndDelimited(valueSyntax, Collectors.toList(), true, PropertyFactory.DEFAULT_DELIMITER); } private static XmlMapper> otherList(ValueSyntax valueSyntax) { - return seqAndDelimited(valueSyntax, Collectors.toList(), true /* for now */, '|'); + return seqAndDelimited(valueSyntax, Collectors.toList(), /* prefer old syntax for now */ true, PropertyFactory.DEFAULT_DELIMITER); + } + + private static Function preTrim(Function parser) { + return parser.compose(String::trim); } public static XmlMapper> toOptional(XmlMapper itemSyntax) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 3abe49d17e..009872e409 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -395,7 +395,7 @@ public class RuleSetFactoryTest { Rule r = loadFirstRule(XPATH); PropertyDescriptor xpathProperty = (PropertyDescriptor) r.getPropertyDescriptor("xpath"); assertNotNull("xpath property descriptor", xpathProperty); - assertNotSame(r.getProperty(xpathProperty).indexOf(" //Block "), -1); + assertNotSame(r.getProperty(xpathProperty).indexOf("//Block"), -1); } @Test diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index 564910ae9c..c512802960 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -228,6 +228,10 @@ public class PropertyDescriptorTest { assertEquals("foo", descriptor.valueFrom("foo")); assertEquals("foo", descriptor.valueFrom(" foo ")); + } + + @Test + public void testStringListProperty() { PropertyDescriptor> listDescriptor = PropertyFactory.stringListProperty("stringListProp") .desc("hello") .defaultValues("v1", "v2") @@ -236,7 +240,7 @@ public class PropertyDescriptorTest { assertEquals("hello", listDescriptor.description()); assertEquals(Arrays.asList("v1", "v2"), listDescriptor.defaultValue()); assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("foo,bar")); - assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom(" foo | bar ")); + assertEquals(Arrays.asList("foo | bar"), listDescriptor.valueFrom(" foo | bar ")); } private enum SampleEnum { A, B, C } From b59bf94dd846a0fcc5e8cef1605c05ea3379dd0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 30 Jan 2022 18:30:36 +0100 Subject: [PATCH 069/347] Remove duplication --- .../sourceforge/pmd/internal/DOMUtils.java | 40 ------------------- .../pmd/internal/util/IteratorUtil.java | 1 - .../pmd/properties/xml/OptionalSyntax.java | 2 +- .../sourceforge/pmd/rules/RuleFactory.java | 14 +++---- 4 files changed, 8 insertions(+), 49 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/DOMUtils.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/DOMUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/DOMUtils.java deleted file mode 100644 index b28059f4d5..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/DOMUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal; - -import org.w3c.dom.Node; - -import net.sourceforge.pmd.annotation.InternalApi; - -@InternalApi -public final class DOMUtils { - private DOMUtils() { - // utility - } - - /** - * Parse a String from a textually type node. - * - * @param node The node. - * - * @return The String. - */ - public static String parseTextNode(Node node) { - final int nodeCount = node.getChildNodes().getLength(); - if (nodeCount == 0) { - return ""; - } - - StringBuilder buffer = new StringBuilder(); - - for (int i = 0; i < nodeCount; i++) { - Node childNode = node.getChildNodes().item(i); - if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) { - buffer.append(childNode.getNodeValue()); - } - } - return buffer.toString(); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java index 443355ecd3..416a1525b1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java @@ -14,7 +14,6 @@ import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; -import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; import java.util.function.Function; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index 6059fb257c..d38ce1aaf2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -70,7 +70,7 @@ final class OptionalSyntax extends XmlMapper> { @Override public Optional fromXml(Element element, XmlErrorReporter err) { - if (element.getTagName().equals(EMPTY_NAME)) { + if (EMPTY_NAME.equals(element.getTagName())) { return Optional.empty(); } else { return Optional.ofNullable(itemSyntax.fromXml(element, err)); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 4ae25b9f04..086a003ac8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -27,9 +27,9 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSetReference; import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.internal.DOMUtils; import net.sourceforge.pmd.internal.util.xml.SchemaConstants; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; +import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -112,13 +112,13 @@ public class RuleFactory { for (Element node : DomUtils.elementsIn(ruleElement)) { switch (node.getNodeName()) { case DESCRIPTION: - ruleReference.setDescription(DOMUtils.parseTextNode(node)); + ruleReference.setDescription(XmlUtil.parseTextNode(node)); break; case EXAMPLE: - ruleReference.addExample(DOMUtils.parseTextNode(node)); + ruleReference.addExample(XmlUtil.parseTextNode(node)); break; case PRIORITY: - ruleReference.setPriority(RulePriority.valueOf(Integer.parseInt(DOMUtils.parseTextNode(node)))); + ruleReference.setPriority(RulePriority.valueOf(Integer.parseInt(XmlUtil.parseTextNode(node)))); break; case PROPERTIES: setPropertyValues(ruleReference, node, err); @@ -176,13 +176,13 @@ public class RuleFactory { for (Element node : DomUtils.elementsIn(ruleElement)) { switch (node.getNodeName()) { case DESCRIPTION: - rule.setDescription(DOMUtils.parseTextNode(node)); + rule.setDescription(XmlUtil.parseTextNode(node)); break; case EXAMPLE: - rule.addExample(DOMUtils.parseTextNode(node)); + rule.addExample(XmlUtil.parseTextNode(node)); break; case PRIORITY: - RulePriority rp = parsePriority(err, node, DOMUtils.parseTextNode(node)); + RulePriority rp = parsePriority(err, node, XmlUtil.parseTextNode(node)); rule.setPriority(rp); break; case PROPERTIES: From d777d1f8ce64c2829571ed790d9b54004ee69401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 9 Apr 2022 22:52:54 +0200 Subject: [PATCH 070/347] Better xml error reporting --- .../net/sourceforge/pmd/RuleSetFactory.java | 63 +++++++++++-------- .../sourceforge/pmd/rules/RuleFactory.java | 17 ++++- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 7c859cf4ca..21a4e8bdaf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -426,10 +426,11 @@ final class RuleSetFactory { rule.setRuleSetName(ruleSetBuilder.getName()); if (warnDeprecated && StringUtils.isBlank(ruleElement.getAttribute("language"))) { - LOG.warn("Rule {}/{} does not mention attribute language='{}'," - + " please mention it explicitly to be compatible with PMD 7", - ruleSetReferenceId.getRuleSetFileName(), rule.getName(), - rule.getLanguage().getTerseName()); + err.warn(ruleElement, + "Rule {0}/{1} does not mention attribute language='{2}'," + + " please mention it explicitly to be compatible with PMD 7", + ruleSetReferenceId.getRuleSetFileName(), rule.getName(), + rule.getLanguage().getTerseName()); } ruleSetBuilder.addRule(rule); @@ -482,37 +483,47 @@ final class RuleSetFactory { Rule referencedRule = ruleSetFactory.createRule(otherRuleSetReferenceId, true); if (referencedRule == null) { - throw new IllegalArgumentException("Unable to find referenced rule " + otherRuleSetReferenceId.getRuleName() - + "; perhaps the rule name is misspelled?"); + throw err.error(ruleNode, + "Unable to find referenced rule {0}" + + "; perhaps the rule name is misspelled?", + otherRuleSetReferenceId.getRuleName()); } if (warnDeprecated && referencedRule.isDeprecated()) { if (referencedRule instanceof RuleReference) { RuleReference ruleReference = (RuleReference) referencedRule; - LOG.warn("Use Rule name {}/{} instead of the deprecated Rule name {}. PMD {}" - + " will remove support for this deprecated Rule name usage.", - ruleReference.getRuleSetReference().getRuleSetFileName(), - ruleReference.getOriginalName(), otherRuleSetReferenceId, - PMDVersion.getNextMajorRelease()); + err.warn(ruleElement, + "Use Rule name {0}/{1} instead of the deprecated Rule name {2}. PMD {3}" + + " will remove support for this deprecated Rule name usage.", + ruleReference.getRuleSetReference().getRuleSetFileName(), + ruleReference.getOriginalName(), otherRuleSetReferenceId, + PMDVersion.getNextMajorRelease()); } else { - LOG.warn("Discontinue using Rule name {} as it is scheduled for removal from PMD." - + " PMD {} will remove support for this Rule.", - otherRuleSetReferenceId, PMDVersion.getNextMajorRelease()); + err.warn(ruleElement, + "Discontinue using Rule name {0} as it is scheduled for removal from PMD." + + " PMD {1} will remove support for this Rule.", + otherRuleSetReferenceId, PMDVersion.getNextMajorRelease()); } } RuleSetReference ruleSetReference = new RuleSetReference(otherRuleSetReferenceId.getRuleSetFileName(), false); - RuleReference ruleReference = new RuleFactory(resourceLoader).decorateRule(referencedRule, ruleSetReference, ruleElement, err); + RuleReference ruleReference; + try { + ruleReference = new RuleFactory(resourceLoader).decorateRule(referencedRule, ruleSetReference, ruleElement, err); + } catch (XmlException e) { + throw err.error(ruleElement, e, "Error while parsing rule reference"); + } if (warnDeprecated && ruleReference.isDeprecated() && !isSameRuleSet) { - LOG.warn("Use Rule name {}/{} instead of the deprecated Rule name {}/{}. PMD {}" - + " will remove support for this deprecated Rule name usage.", - ruleReference.getRuleSetReference().getRuleSetFileName(), - ruleReference.getOriginalName(), - ruleSetReferenceId.getRuleSetFileName(), - ruleReference.getName(), - PMDVersion.getNextMajorRelease()); + err.warn(ruleElement, + "Use Rule name {0}/{1} instead of the deprecated Rule name {2}/{3}. PMD {4}" + + " will remove support for this deprecated Rule name usage.", + ruleReference.getRuleSetReference().getRuleSetFileName(), + ruleReference.getOriginalName(), + ruleSetReferenceId.getRuleSetFileName(), + ruleReference.getName(), + PMDVersion.getNextMajorRelease()); } if (withDeprecatedRuleReferences || !isSameRuleSet || !ruleReference.isDeprecated()) { @@ -523,10 +534,10 @@ final class RuleSetFactory { // which means, it is a plain reference. And the new reference overrides. // for all other cases, we should log a warning if (existingRuleReference.hasOverriddenAttributes() || !ruleReference.hasOverriddenAttributes()) { - LOG.warn("The rule {} is referenced multiple times in \"{}\". " - + "Only the last rule configuration is used.", - ruleReference.getName(), - ruleSetBuilder.getName()); + err.warn(ruleElement, "The rule {0} is referenced multiple times in \"{1}\". " + + "Only the last rule configuration is used.", + ruleReference.getName(), + ruleSetBuilder.getName()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 086a003ac8..0899ad1a51 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -43,6 +43,7 @@ import net.sourceforge.pmd.util.StringUtil; import com.github.oowekyala.ooxml.DomUtils; import com.github.oowekyala.ooxml.messages.XmlErrorReporter; +import com.github.oowekyala.ooxml.messages.XmlException; /** @@ -154,7 +155,7 @@ public class RuleFactory { String clazz = getNonBlankAttribute(ruleElement, err, CLASS); rule = resourceLoader.loadRuleFromClassPath(clazz); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - throw err.fatal(ruleElement.getAttributeNode(CLASS), e); + throw err.error(ruleElement.getAttributeNode(CLASS), e); } rule.setName(getNonBlankAttribute(ruleElement, err, NAME)); @@ -305,6 +306,7 @@ public class RuleFactory { private void setPropertyValues(Rule rule, Element propertiesElt, XmlErrorReporter err) { Set overridden = new HashSet<>(); + XmlException exception = null; for (Element element : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesElt, err)) { String name = SchemaConstants.NAME.getAttributeOrThrow(element, err); if (!overridden.add(name)) { @@ -317,7 +319,18 @@ public class RuleFactory { // todo just warn and ignore throw err.error(element, ERR__PROPERTY_DOES_NOT_EXIST, name, rule.getName()); } - setRulePropertyCapture(rule, desc, element, err); + try { + setRulePropertyCapture(rule, desc, element, err); + } catch (XmlException e) { + if (exception == null) { + exception = e; + } else { + exception.addSuppressed(e); + } + } + } + if (exception != null) { + throw exception; } } From 95b57dcb286d23f1b5b0206398ef9e239c7b50fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 10 Apr 2022 00:51:03 +0200 Subject: [PATCH 071/347] Even better --- .../net/sourceforge/pmd/RuleSetFactory.java | 119 +++++++++++---- .../sourceforge/pmd/RuleSetLoadException.java | 10 +- .../net/sourceforge/pmd/RuleSetLoader.java | 7 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 136 +++++++++++------- 4 files changed, 188 insertions(+), 84 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 21a4e8bdaf..bedf62ea0e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -22,9 +22,10 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.checkerframework.checker.nullness.qual.NonNull; +import org.slf4j.event.Level; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -32,18 +33,21 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; +import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.rules.RuleFactory; import net.sourceforge.pmd.util.ResourceLoader; +import net.sourceforge.pmd.util.log.MessageReporter; import com.github.oowekyala.ooxml.DomUtils; import com.github.oowekyala.ooxml.messages.AccumulatingErrorReporter; -import com.github.oowekyala.ooxml.messages.LoggerMessageHandler; import com.github.oowekyala.ooxml.messages.PositionedXmlDoc; import com.github.oowekyala.ooxml.messages.XmlErrorReporter; import com.github.oowekyala.ooxml.messages.XmlException; import com.github.oowekyala.ooxml.messages.XmlException.Severity; +import com.github.oowekyala.ooxml.messages.XmlMessageHandler; +import com.github.oowekyala.ooxml.messages.XmlMessageKind; import com.github.oowekyala.ooxml.messages.XmlMessageUtils; /** @@ -59,6 +63,7 @@ final class RuleSetFactory { private final RulePriority minimumPriority; private final boolean warnDeprecated; private final RuleSetFactoryCompatibility compatibilityFilter; + private final MessageReporter reporter; private final boolean includeDeprecatedRuleReferences; private final Map parsedRulesets = new HashMap<>(); @@ -67,13 +72,15 @@ final class RuleSetFactory { RulePriority minimumPriority, boolean warnDeprecated, RuleSetFactoryCompatibility compatFilter, - boolean includeDeprecatedRuleReferences) { + boolean includeDeprecatedRuleReferences, + MessageReporter reporter) { this.resourceLoader = resourceLoader; this.minimumPriority = minimumPriority; this.warnDeprecated = warnDeprecated; this.includeDeprecatedRuleReferences = includeDeprecatedRuleReferences; this.compatibilityFilter = compatFilter; + this.reporter = reporter; } @@ -81,15 +88,15 @@ final class RuleSetFactory { * Create a RuleSet from a RuleSetReferenceId. Priority filtering is ignored * when loading a single Rule. The currently configured ResourceLoader is used. * - * @param ruleSetReferenceId - * The RuleSetReferenceId of the RuleSet to create. + * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet to create. + * * @return A new RuleSet. */ - RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) { + @NonNull RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) { return createRuleSet(ruleSetReferenceId, includeDeprecatedRuleReferences); } - private RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) + private @NonNull RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) throws RuleSetLoadException { return readDocument(ruleSetReferenceId, withDeprecatedRuleReferences); } @@ -136,7 +143,7 @@ final class RuleSetFactory { * * @throws RuleSetLoadException If the ruleset cannot be parsed (eg IO exception, malformed XML, validation errors) */ - private RuleSet readDocument(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { + private @NonNull RuleSet readDocument(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { try (CheckedInputStream inputStream = new CheckedInputStream(ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { if (!ruleSetReferenceId.isExternal()) { @@ -144,7 +151,7 @@ final class RuleSetFactory { "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); } - LoggerMessageHandler handler = new LoggerMessageHandler(LOG, false); + XmlMessageHandler handler = adapt(reporter); DocumentBuilder builder = createDocumentBuilder(); InputSource inputSource = new InputSource(inputStream); inputSource.setSystemId(ruleSetReferenceId.getRuleSetFileName()); @@ -152,27 +159,47 @@ final class RuleSetFactory { PositionedXmlDoc parsed = XmlMessageUtils.getInstance().parse(builder, inputSource, handler); @SuppressWarnings("PMD.CloseResource") - AccumulatingErrorReporter err = makeReporter(handler, parsed); + PmdXmlErrorRenderer err = new PmdXmlErrorRenderer(handler, parsed, Severity.WARNING); + Severity minSeverity = Severity.WARNING; try { RuleSetBuilder ruleSetBuilder = new RuleSetBuilder(inputStream.getChecksum().getValue()).withFileName(ruleSetReferenceId.getRuleSetFileName()); RuleSet ruleSet = parseRulesetNode(ruleSetReferenceId, withDeprecatedRuleReferences, parsed, ruleSetBuilder, err); - err.close(Severity.WARNING, Severity.ERROR); + if (err.errCount > 0) { + // note this makes us jump to the catch branch + // these might have been non-fatal errors + minSeverity = Severity.ERROR; + String message; + if (err.errCount == 1) { + message = "An XML validation error occurred"; + } else { + message = err.errCount + " XML validation errors occurred"; + } + throw new RuleSetLoadException(ruleSetReferenceId, message); + } return ruleSet; - } catch (XmlException e) { - err.close(e.getSeverity(), Severity.ERROR); + } catch (Exception | Error e) { + minSeverity = Severity.ERROR; throw e; + } finally { + err.close(minSeverity, Severity.ERROR); } } catch (ParserConfigurationException | IOException ex) { - throw new RuleSetLoadException("Couldn't read the ruleset " + ruleSetReferenceId, ex); + throw new RuleSetLoadException(ruleSetReferenceId, ex); } } - private @NonNull AccumulatingErrorReporter makeReporter(LoggerMessageHandler handler, PositionedXmlDoc parsed) { - return new AccumulatingErrorReporter(handler, parsed.getPositioner(), Severity.WARNING) { + private @NonNull XmlMessageHandler adapt(final MessageReporter reporter) { + return new XmlMessageHandler() { + @Override - protected String template(String message, Object... args) { - return MessageFormat.format(message, args); + public boolean supportsAnsiColors() { + return false; // todo + } + + @Override + public void printMessageLn(XmlMessageKind kind, Severity severity, String message) { + reporter.log(toLevel(severity), message); } }; } @@ -181,7 +208,7 @@ final class RuleSetFactory { boolean withDeprecatedRuleReferences, PositionedXmlDoc parsed, RuleSetBuilder builder, - XmlErrorReporter err) { + PmdXmlErrorRenderer err) { Element ruleSetElement = parsed.getDocument().getDocumentElement(); if (ruleSetElement.hasAttribute("name")) { @@ -217,7 +244,12 @@ final class RuleSetFactory { break; } case RuleFactory.RULE: - parseRuleNode(ruleSetReferenceId, builder, node, withDeprecatedRuleReferences, rulesetReferences, err); + try { + parseRuleNode(ruleSetReferenceId, builder, node, withDeprecatedRuleReferences, rulesetReferences, err); + } catch (XmlException recoveredFrom) { + // will be thrown later. + err.delayedExceptions.add(recoveredFrom); + } break; default: throw err.error(node, "Unexpected element as child of "); @@ -376,7 +408,7 @@ final class RuleSetFactory { // all rules in the ruleset have been deprecated - the ruleset itself is considered to be deprecated rulesetDeprecated = true; LOG.warn("The RuleSet {} has been deprecated and will be removed in PMD {}", - ref, PMDVersion.getNextMajorRelease()); + ref, PMDVersion.getNextMajorRelease()); } for (RuleReference r : potentialRules) { @@ -391,7 +423,7 @@ final class RuleSetFactory { if (!excludedRulesCheck.isEmpty()) { LOG.warn( "Unable to exclude rules {} from ruleset reference {}" - + "; perhaps the rule name is misspelled or the rule doesn't exist anymore?", + + "; perhaps the rule name is misspelled or the rule doesn't exist anymore?", excludedRulesCheck, ref); } @@ -471,11 +503,11 @@ final class RuleSetFactory { boolean isSameRuleSet = false; RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0); if (!otherRuleSetReferenceId.isExternal() - && containsRule(ruleSetReferenceId, otherRuleSetReferenceId.getRuleName())) { + && containsRule(ruleSetReferenceId, otherRuleSetReferenceId.getRuleName())) { otherRuleSetReferenceId = new RuleSetReferenceId(ref, ruleSetReferenceId); isSameRuleSet = true; } else if (otherRuleSetReferenceId.isExternal() - && otherRuleSetReferenceId.getRuleSetFileName().equals(ruleSetReferenceId.getRuleSetFileName())) { + && otherRuleSetReferenceId.getRuleSetFileName().equals(ruleSetReferenceId.getRuleSetFileName())) { otherRuleSetReferenceId = new RuleSetReferenceId(otherRuleSetReferenceId.getRuleName(), ruleSetReferenceId); isSameRuleSet = true; } @@ -570,7 +602,7 @@ final class RuleSetFactory { } } } catch (Exception e) { - throw new RuleSetLoadException("Cannot load " + ruleSetReferenceId, e); + throw new RuleSetLoadException(ruleSetReferenceId, e); } return found; @@ -613,5 +645,42 @@ final class RuleSetFactory { .includeDeprecatedRuleReferences(includeDeprecatedRuleReferences); } + private static final class PmdXmlErrorRenderer extends AccumulatingErrorReporter { + private int errCount; + private List delayedExceptions = new ArrayList<>(); + + public PmdXmlErrorRenderer(XmlMessageHandler handler, PositionedXmlDoc parsed, Severity minSeverity) { + super(handler, parsed.getPositioner(), minSeverity); + } + + @Override + protected void handle(XmlException ex, String message) { + if (ex.getSeverity().compareTo(Severity.ERROR) >= 0) { + this.errCount++; + } + LOG.atLevel(Level.DEBUG).log(ex.toString()); + super.handle(ex, message); // print nicely + } + + @Override + protected String template(String message, Object... args) { + return MessageFormat.format(message, args); + } + } + + private static Level toLevel(Severity severity) { + switch (severity) { + case DEBUG: + return Level.DEBUG; + case INFO: + return Level.INFO; + case WARNING: + return Level.WARN; + case ERROR: + case FATAL: + return Level.ERROR; + } + throw AssertionUtil.shouldNotReachHere("exhaustive"); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java index 4fa14bcb62..b83355e1c6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.annotation.InternalApi; /** @@ -16,14 +18,14 @@ public final class RuleSetLoadException extends RuntimeException { /** Constructors are internal. */ @InternalApi - public RuleSetLoadException(String message, Throwable cause) { - super(message, cause); + public RuleSetLoadException(RuleSetReferenceId rsetId, @NonNull Throwable cause) { + super("Cannot load ruleset " + rsetId + ": " + cause.getMessage(), cause); } /** Constructors are internal. */ @InternalApi - public RuleSetLoadException(String message) { - super(message); + public RuleSetLoadException(RuleSetReferenceId rsetId, String message) { + super("Cannot load ruleset " + rsetId + ": " + message); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java index 1a75e5d3a5..f75997e3ee 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -139,7 +139,8 @@ public final class RuleSetLoader { this.minimumPriority, this.warnDeprecated, this.compatFilter, - this.includeDeprecatedRuleReferences + this.includeDeprecatedRuleReferences, + this.reporter ); } @@ -254,8 +255,10 @@ public final class RuleSetLoader { RuleSet loadFromResource(RuleSetReferenceId ruleSetReferenceId) { try { return toFactory().createRuleSet(ruleSetReferenceId); + } catch (RuleSetLoadException e) { + throw e; } catch (Exception e) { - throw new RuleSetLoadException("Cannot parse " + ruleSetReferenceId, e); + throw new RuleSetLoadException(ruleSetReferenceId, e); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 2b519c00da..314535bd40 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -4,13 +4,6 @@ package net.sourceforge.pmd; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.hasProperty; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.isA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -23,11 +16,17 @@ import java.io.InputStream; import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; import net.sourceforge.pmd.junit.LocaleRule; import net.sourceforge.pmd.lang.DummyLanguageModule; @@ -36,17 +35,27 @@ import net.sourceforge.pmd.lang.rule.MockRule; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.util.ResourceLoader; - -import com.github.oowekyala.ooxml.messages.XmlException; +import net.sourceforge.pmd.util.log.MessageReporter; public class RuleSetFactoryTest { + private static final Logger LOG = LoggerFactory.getLogger(RuleSetFactoryTest.class); + @org.junit.Rule public LocaleRule localeRule = LocaleRule.en(); @org.junit.Rule public final SystemErrRule systemErrRule = new SystemErrRule().muteForSuccessfulTests().enableLog(); + private MessageReporter mockReporter; + + + @Before + public void setup() { + // mockReporter = Mockito.spy(new SimpleMessageReporter(LOG)); + mockReporter = Mockito.mock(MessageReporter.class); + } + @Test public void testRuleSetFileName() { RuleSet rs = new RuleSetLoader().loadFromString("dummyRuleset.xml", EMPTY_RULESET); @@ -256,9 +265,10 @@ public class RuleSetFactoryTest { Rule rule = rs.getRuleByName("OldNameOfDummyBasicMockRule"); assertNotNull(rule); - assertEquals(1, - StringUtils.countMatches(systemErrRule.getLog(), - "WARN net.sourceforge.pmd.RuleSetFactory - Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic.xml/OldNameOfDummyBasicMockRule.")); + verifyFoundAWarningWithMessage( + containing("Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule " + + "instead of the deprecated Rule name rulesets/dummy/basic.xml/OldNameOfDummyBasicMockRule") + ); } /** @@ -413,10 +423,11 @@ public class RuleSetFactoryTest { @Test public void testExternalReferenceOverrideNonExistent() { - ex.expect(RuleSetLoadException.class); - ex.expectCause(allOf(isA(XmlException.class), - hasProperty("simpleMessage", is("Cannot set non-existent property 'test4' on rule TestNameOverride")))); - loadFirstRule(REF_OVERRIDE_NONEXISTENT); + assertThrows(RuleSetLoadException.class, + () -> loadFirstRule(REF_OVERRIDE_NONEXISTENT)); + verifyFoundAnErrorWithMessage( + containing("Cannot set non-existent property 'test4' on rule TestNameOverride") + ); } @Test @@ -596,32 +607,32 @@ public class RuleSetFactoryTest { @Test public void testIncorrectMinimumLanguageVersion() { - RuleSetLoadException ex = assertCannotParse(INCORRECT_MINIMUM_LANGUAGE_VERSION); - Throwable cause = ex.getCause(); - assertThat(cause, instanceOf(XmlException.class)); - assertThat(cause.getMessage(), containsString("valid language version")); - assertThat(cause.getMessage(), containsString("'1.0', '1.1', '1.2'")); // and not "dummy 1.0, dummy 1.1, ..." + assertCannotParse(INCORRECT_MINIMUM_LANGUAGE_VERSION); + verifyFoundAnErrorWithMessage( + containing("valid language version") + .and(containing("'1.0', '1.1', '1.2'")) // and not "dummy 1.0, dummy 1.1, ..." + ); } @Test public void testIncorrectMinimumLanguageVersionWithLanguageSetInJava() { - RuleSetLoadException ex = - assertCannotParse("\n" - + "\n" - + " TODO\n" - + "\n" - + " \n" - + " TODO\n" - + " 2\n" - + " \n" - + "\n" - + ""); - Throwable cause = ex.getCause(); - assertThat(cause, instanceOf(XmlException.class)); - assertThat(cause.getMessage(), containsString("valid language version")); + assertCannotParse("\n" + + "\n" + + " TODO\n" + + "\n" + + " \n" + + " TODO\n" + + " 2\n" + + " \n" + + "\n" + + ""); + + verifyFoundAnErrorWithMessage( + containing("valid language version") + ); } @Test @@ -633,19 +644,31 @@ public class RuleSetFactoryTest { @Test public void testIncorrectMaximumLanguageVersion() { - RuleSetLoadException ex = assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); - Throwable cause = ex.getCause(); - assertThat(cause, instanceOf(XmlException.class)); - assertThat(cause.getMessage(), containsString("valid language version")); - assertThat(cause.getMessage(), containsString("'1.0', '1.1', '1.2'")); // and not "dummy 1.0, dummy 1.1, ..." + assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); + verifyFoundAnErrorWithMessage( + containing("valid language version") + .and(containing("'1.0', '1.1', '1.2'")) + ); } @Test public void testInvertedMinimumMaximumLanguageVersions() { - RuleSetLoadException ex = assertCannotParse(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS); - Throwable cause = ex.getCause(); - assertThat(cause, instanceOf(XmlException.class)); - assertThat(cause.getMessage(), containsString("version range")); + assertCannotParse(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS); + verifyFoundAnErrorWithMessage(containing("versionRange")); + } + + private void verifyFoundAnErrorWithMessage(Predicate messageTest) { + Mockito.verify(mockReporter, Mockito.times(1)) + .log(Mockito.eq(Level.ERROR), Mockito.argThat(messageTest::test), Mockito.any()); + } + + private void verifyFoundAWarningWithMessage(Predicate messageTest) { + Mockito.verify(mockReporter, Mockito.times(1)) + .log(Mockito.eq(Level.WARN), Mockito.argThat(messageTest::test), Mockito.any()); + } + + private static Predicate containing(String part) { + return it -> it.contains(part); } @Test @@ -872,7 +895,7 @@ public class RuleSetFactoryTest { + " \n" ); - assertTrue(systemErrRule.getLog().contains("RuleSet name is missing.")); + verifyFoundAWarningWithMessage(containing("RuleSet name is missing.")); } @Test @@ -885,7 +908,7 @@ public class RuleSetFactoryTest { + " \n" + " \n" ); - assertTrue(systemErrRule.getLog().contains("RuleSet description is missing.")); + verifyFoundAWarningWithMessage(containing("RuleSet description is missing.")); } private static final String REF_OVERRIDE_ORIGINAL_NAME = "\n" @@ -1225,15 +1248,22 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSet(String ruleSetXml) { - return new RuleSetLoader().loadFromString("dummyRuleset.xml", ruleSetXml); + try (PmdAnalysis pmd = PmdAnalysis.create(new PMDConfiguration(), mockReporter)) { + return pmd.newRuleSetLoader() + .loadFromString("dummyRuleset.xml", ruleSetXml); + } } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) { - return new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).loadFromString("testRuleset.xml", ruleSetXml); + try (PmdAnalysis pmd = PmdAnalysis.create(new PMDConfiguration(), mockReporter)) { + return pmd.newRuleSetLoader() + .warnDeprecated(true) + .enableCompatibility(false).loadFromString("dummyRuleset.xml", ruleSetXml); + } } - private RuleSetLoadException assertCannotParse(String xmlContent) { - return assertThrows(RuleSetLoadException.class, () -> loadFirstRule(xmlContent)); + private void assertCannotParse(String xmlContent) { + assertThrows(RuleSetLoadException.class, () -> loadFirstRule(xmlContent)); } } From a5f9ed8f7e058d8747a27d768586e8dd062e5397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 13 Apr 2022 19:36:28 +0200 Subject: [PATCH 072/347] Use new ooxml lib --- pmd-core/pom.xml | 2 +- .../net/sourceforge/pmd/RuleSetFactory.java | 300 ++++++++++-------- .../pmd/internal/util/xml/PmdXmlReporter.java | 19 ++ .../pmd/internal/util/xml/SchemaConstant.java | 22 +- .../internal/util/xml/SchemaConstants.java | 9 + .../pmd/internal/util/xml/XmlUtil.java | 19 +- .../properties/xml/ConstraintDecorator.java | 9 +- .../pmd/properties/xml/MapperSet.java | 7 +- .../pmd/properties/xml/OptionalSyntax.java | 5 +- .../pmd/properties/xml/SeqSyntax.java | 7 +- .../pmd/properties/xml/ValueSyntax.java | 7 +- .../pmd/properties/xml/XmlMapper.java | 9 +- .../sourceforge/pmd/rules/RuleFactory.java | 102 +++--- .../pmd/util/log/MessageReporter.java | 26 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 6 +- 15 files changed, 313 insertions(+), 236 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 7c7d8f7796..848cda9ea5 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -124,7 +124,7 @@ com.github.oowekyala.ooxml nice-xml-messages - 1.0-SNAPSHOT + 2.0-SNAPSHOT diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index bedf62ea0e..dce19fb873 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -4,6 +4,13 @@ package net.sourceforge.pmd; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.DESCRIPTION; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.EXCLUDE; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.EXCLUDE_PATTERN; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.INCLUDE_PATTERN; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PRIORITY; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.RULE; + import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; @@ -13,6 +20,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import java.util.zip.Adler32; @@ -23,6 +31,7 @@ import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.event.Level; @@ -33,7 +42,7 @@ import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; -import net.sourceforge.pmd.internal.util.AssertionUtil; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.rules.RuleFactory; @@ -41,14 +50,15 @@ import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.log.MessageReporter; import com.github.oowekyala.ooxml.DomUtils; -import com.github.oowekyala.ooxml.messages.AccumulatingErrorReporter; +import com.github.oowekyala.ooxml.messages.AccumulatingMessageHandler; +import com.github.oowekyala.ooxml.messages.NiceXmlMessageSpec; +import com.github.oowekyala.ooxml.messages.OoxmlFacade; import com.github.oowekyala.ooxml.messages.PositionedXmlDoc; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; import com.github.oowekyala.ooxml.messages.XmlException; -import com.github.oowekyala.ooxml.messages.XmlException.Severity; -import com.github.oowekyala.ooxml.messages.XmlMessageHandler; -import com.github.oowekyala.ooxml.messages.XmlMessageKind; -import com.github.oowekyala.ooxml.messages.XmlMessageUtils; +import com.github.oowekyala.ooxml.messages.XmlMessageReporterBase; +import com.github.oowekyala.ooxml.messages.XmlPosition; +import com.github.oowekyala.ooxml.messages.XmlPositioner; +import com.github.oowekyala.ooxml.messages.XmlSeverity; /** * RuleSetFactory is responsible for creating RuleSet instances from XML @@ -145,22 +155,25 @@ final class RuleSetFactory { */ private @NonNull RuleSet readDocument(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { - try (CheckedInputStream inputStream = new CheckedInputStream(ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { + try (CheckedInputStream inputStream = new CheckedInputStream(ruleSetReferenceId.getInputStream(resourceLoader), new Adler32())) { if (!ruleSetReferenceId.isExternal()) { throw new IllegalArgumentException( "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); } - XmlMessageHandler handler = adapt(reporter); + AccumulatingMessageHandler handler = getXmlMessagePrinter(); DocumentBuilder builder = createDocumentBuilder(); InputSource inputSource = new InputSource(inputStream); inputSource.setSystemId(ruleSetReferenceId.getRuleSetFileName()); - PositionedXmlDoc parsed = XmlMessageUtils.getInstance().parse(builder, inputSource, handler); + OoxmlFacade ooxml = new OoxmlFacade() + .withPrinter(handler) + .withAnsiColors(false); + PositionedXmlDoc parsed = ooxml.parse(builder, inputSource); @SuppressWarnings("PMD.CloseResource") - PmdXmlErrorRenderer err = new PmdXmlErrorRenderer(handler, parsed, Severity.WARNING); - Severity minSeverity = Severity.WARNING; + PmdXmlReporterImpl err = new PmdXmlReporterImpl(reporter, ooxml, parsed.getPositioner()); + XmlSeverity minSeverity = XmlSeverity.WARNING; try { RuleSetBuilder ruleSetBuilder = new RuleSetBuilder(inputStream.getChecksum().getValue()).withFileName(ruleSetReferenceId.getRuleSetFileName()); @@ -168,7 +181,7 @@ final class RuleSetFactory { if (err.errCount > 0) { // note this makes us jump to the catch branch // these might have been non-fatal errors - minSeverity = Severity.ERROR; + minSeverity = XmlSeverity.ERROR; String message; if (err.errCount == 1) { message = "An XML validation error occurred"; @@ -179,85 +192,63 @@ final class RuleSetFactory { } return ruleSet; } catch (Exception | Error e) { - minSeverity = Severity.ERROR; + minSeverity = XmlSeverity.ERROR; throw e; } finally { - err.close(minSeverity, Severity.ERROR); + handler.close(minSeverity, XmlSeverity.ERROR); } } catch (ParserConfigurationException | IOException ex) { throw new RuleSetLoadException(ruleSetReferenceId, ex); } } - private @NonNull XmlMessageHandler adapt(final MessageReporter reporter) { - return new XmlMessageHandler() { - - @Override - public boolean supportsAnsiColors() { - return false; // todo - } - - @Override - public void printMessageLn(XmlMessageKind kind, Severity severity, String message) { - reporter.log(toLevel(severity), message); - } - }; - } private RuleSet parseRulesetNode(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences, PositionedXmlDoc parsed, RuleSetBuilder builder, - PmdXmlErrorRenderer err) { + PmdXmlReporter err) { Element ruleSetElement = parsed.getDocument().getDocumentElement(); if (ruleSetElement.hasAttribute("name")) { builder.withName(ruleSetElement.getAttribute("name")); } else { - err.warn(ruleSetElement, "RuleSet name is missing. Future versions of PMD will require it."); + err.at(ruleSetElement).warn("RuleSet name is missing. Future versions of PMD will require it."); builder.withName("Missing RuleSet Name"); } Set rulesetReferences = new HashSet<>(); for (Element node : DomUtils.elementsIn(ruleSetElement)) { - String nodeName = node.getNodeName(); String text = XmlUtil.parseTextNode(node); - switch (nodeName) { - case RuleFactory.DESCRIPTION: + if (DESCRIPTION.isElementWithName(node)) { builder.withDescription(text); - break; - case "include-pattern": { + } else if (INCLUDE_PATTERN.isElementWithName(node)) { final Pattern pattern = parseRegex(node, text, err); if (pattern == null) { continue; } builder.withFileInclusions(pattern); - break; - } - case "exclude-pattern": { + } else if (EXCLUDE_PATTERN.isElementWithName(node)) { final Pattern pattern = parseRegex(node, text, err); if (pattern == null) { continue; } builder.withFileExclusions(pattern); - break; - } - case RuleFactory.RULE: + } else if (RULE.isElementWithName(node)) { try { parseRuleNode(ruleSetReferenceId, builder, node, withDeprecatedRuleReferences, rulesetReferences, err); } catch (XmlException recoveredFrom) { // will be thrown later. - err.delayedExceptions.add(recoveredFrom); + err.addExceptionToThrowLater(recoveredFrom); } - break; - default: - throw err.error(node, "Unexpected element as child of "); + } else { + throw err.at(node).error("Unexpected element as child of "); } } if (!builder.hasDescription()) { - err.warn(ruleSetElement, "RuleSet description is missing. Future versions of PMD will require it."); + err.at(ruleSetElement).warn("RuleSet description is missing. Future versions of PMD will require it."); builder.withDescription("Missing description"); } @@ -266,12 +257,12 @@ final class RuleSetFactory { return builder.build(); } - private Pattern parseRegex(Element node, String text, XmlErrorReporter err) { + private Pattern parseRegex(Element node, String text, PmdXmlReporter err) { final Pattern pattern; try { pattern = Pattern.compile(text); } catch (PatternSyntaxException pse) { - err.error(node, pse); + err.addExceptionToThrowLater((XmlException) err.at(node).error(pse)); return null; } return pattern; @@ -328,18 +319,17 @@ final class RuleSetFactory { */ private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode, + Element ruleNode, boolean withDeprecatedRuleReferences, Set rulesetReferences, - XmlErrorReporter err) { - Element ruleElement = (Element) ruleNode; - String ref = ruleElement.getAttribute("ref"); + PmdXmlReporter err) { + String ref = ruleNode.getAttribute("ref"); ref = compatibilityFilter.applyRef(ref, this.warnDeprecated); if (ref == null) { return; // deleted rule } if (ref.endsWith("xml")) { - parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref, rulesetReferences); + parseRuleSetReferenceNode(ruleSetBuilder, ruleNode, ref, rulesetReferences); } else if (StringUtils.isBlank(ref)) { parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, err); } else { @@ -368,14 +358,14 @@ final class RuleSetFactory { Set excludedRulesCheck = new HashSet<>(); for (int i = 0; i < childNodes.getLength(); i++) { Node child = childNodes.item(i); - if (isElementNode(child, "exclude")) { + if (EXCLUDE.isElementWithName(child)) { Element excludeElement = (Element) child; String excludedRuleName = excludeElement.getAttribute("name"); excludedRuleName = compatibilityFilter.applyExclude(ref, excludedRuleName, this.warnDeprecated); if (excludedRuleName != null) { excludedRulesCheck.add(excludedRuleName); } - } else if (isElementNode(child, RuleFactory.PRIORITY)) { + } else if (PRIORITY.isElementWithName(child)) { priority = XmlUtil.parseTextNode(child).trim(); } } @@ -444,25 +434,24 @@ final class RuleSetFactory { */ private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode, - XmlErrorReporter err) { - Element ruleElement = (Element) ruleNode; + Element ruleNode, + PmdXmlReporter err) { // Stop if we're looking for a particular Rule, and this element is not // it. if (StringUtils.isNotBlank(ruleSetReferenceId.getRuleName()) - && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { + && !isRuleName(ruleNode, ruleSetReferenceId.getRuleName())) { return; } - Rule rule = new RuleFactory(resourceLoader).buildRule(ruleElement, err); + Rule rule = new RuleFactory(resourceLoader).buildRule(ruleNode, err); rule.setRuleSetName(ruleSetBuilder.getName()); - if (warnDeprecated && StringUtils.isBlank(ruleElement.getAttribute("language"))) { - err.warn(ruleElement, - "Rule {0}/{1} does not mention attribute language='{2}'," - + " please mention it explicitly to be compatible with PMD 7", - ruleSetReferenceId.getRuleSetFileName(), rule.getName(), - rule.getLanguage().getTerseName()); + if (warnDeprecated && StringUtils.isBlank(ruleNode.getAttribute("language"))) { + err.at(ruleNode).warn( + "Rule {0}/{1} does not mention attribute language='{2}'," + + " please mention it explicitly to be compatible with PMD 7", + ruleSetReferenceId.getRuleSetFileName(), rule.getName(), + rule.getLanguage().getTerseName()); } ruleSetBuilder.addRule(rule); @@ -483,16 +472,15 @@ final class RuleSetFactory { */ private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode, + Element ruleNode, String ref, boolean withDeprecatedRuleReferences, - XmlErrorReporter err) { - Element ruleElement = (Element) ruleNode; + PmdXmlReporter err) { // Stop if we're looking for a particular Rule, and this element is not // it. if (StringUtils.isNotBlank(ruleSetReferenceId.getRuleName()) - && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { + && !isRuleName(ruleNode, ruleSetReferenceId.getRuleName())) { return; } @@ -515,26 +503,26 @@ final class RuleSetFactory { Rule referencedRule = ruleSetFactory.createRule(otherRuleSetReferenceId, true); if (referencedRule == null) { - throw err.error(ruleNode, - "Unable to find referenced rule {0}" - + "; perhaps the rule name is misspelled?", - otherRuleSetReferenceId.getRuleName()); + throw err.at(ruleNode).error( + "Unable to find referenced rule {0}" + + "; perhaps the rule name is misspelled?", + otherRuleSetReferenceId.getRuleName()); } if (warnDeprecated && referencedRule.isDeprecated()) { if (referencedRule instanceof RuleReference) { RuleReference ruleReference = (RuleReference) referencedRule; - err.warn(ruleElement, - "Use Rule name {0}/{1} instead of the deprecated Rule name {2}. PMD {3}" - + " will remove support for this deprecated Rule name usage.", - ruleReference.getRuleSetReference().getRuleSetFileName(), - ruleReference.getOriginalName(), otherRuleSetReferenceId, - PMDVersion.getNextMajorRelease()); + err.at(ruleNode).warn( + "Use Rule name {0}/{1} instead of the deprecated Rule name {2}. PMD {3}" + + " will remove support for this deprecated Rule name usage.", + ruleReference.getRuleSetReference().getRuleSetFileName(), + ruleReference.getOriginalName(), otherRuleSetReferenceId, + PMDVersion.getNextMajorRelease()); } else { - err.warn(ruleElement, - "Discontinue using Rule name {0} as it is scheduled for removal from PMD." - + " PMD {1} will remove support for this Rule.", - otherRuleSetReferenceId, PMDVersion.getNextMajorRelease()); + err.at(ruleNode).warn( + "Discontinue using Rule name {0} as it is scheduled for removal from PMD." + + " PMD {1} will remove support for this Rule.", + otherRuleSetReferenceId, PMDVersion.getNextMajorRelease()); } } @@ -542,20 +530,20 @@ final class RuleSetFactory { RuleReference ruleReference; try { - ruleReference = new RuleFactory(resourceLoader).decorateRule(referencedRule, ruleSetReference, ruleElement, err); + ruleReference = new RuleFactory(resourceLoader).decorateRule(referencedRule, ruleSetReference, ruleNode, err); } catch (XmlException e) { - throw err.error(ruleElement, e, "Error while parsing rule reference"); + throw err.at(ruleNode).error(e, "Error while parsing rule reference"); } if (warnDeprecated && ruleReference.isDeprecated() && !isSameRuleSet) { - err.warn(ruleElement, - "Use Rule name {0}/{1} instead of the deprecated Rule name {2}/{3}. PMD {4}" - + " will remove support for this deprecated Rule name usage.", - ruleReference.getRuleSetReference().getRuleSetFileName(), - ruleReference.getOriginalName(), - ruleSetReferenceId.getRuleSetFileName(), - ruleReference.getName(), - PMDVersion.getNextMajorRelease()); + err.at(ruleNode).warn( + "Use Rule name {0}/{1} instead of the deprecated Rule name {2}/{3}. PMD {4}" + + " will remove support for this deprecated Rule name usage.", + ruleReference.getRuleSetReference().getRuleSetFileName(), + ruleReference.getOriginalName(), + ruleSetReferenceId.getRuleSetFileName(), + ruleReference.getName(), + PMDVersion.getNextMajorRelease()); } if (withDeprecatedRuleReferences || !isSameRuleSet || !ruleReference.isDeprecated()) { @@ -566,10 +554,11 @@ final class RuleSetFactory { // which means, it is a plain reference. And the new reference overrides. // for all other cases, we should log a warning if (existingRuleReference.hasOverriddenAttributes() || !ruleReference.hasOverriddenAttributes()) { - err.warn(ruleElement, "The rule {0} is referenced multiple times in \"{1}\". " - + "Only the last rule configuration is used.", - ruleReference.getName(), - ruleSetBuilder.getName()); + err.at(ruleNode).warn( + "The rule {0} is referenced multiple times in \"{1}\". " + + "Only the last rule configuration is used.", + ruleReference.getName(), + ruleSetBuilder.getName()); } } @@ -608,10 +597,6 @@ final class RuleSetFactory { return found; } - private static boolean isElementNode(Node node, String name) { - return node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(name); - } - /** * Determine if the specified rule element will represent a Rule with the * given name. @@ -645,42 +630,89 @@ final class RuleSetFactory { .includeDeprecatedRuleReferences(includeDeprecatedRuleReferences); } - private static final class PmdXmlErrorRenderer extends AccumulatingErrorReporter { - - private int errCount; - private List delayedExceptions = new ArrayList<>(); - - public PmdXmlErrorRenderer(XmlMessageHandler handler, PositionedXmlDoc parsed, Severity minSeverity) { - super(handler, parsed.getPositioner(), minSeverity); - } - - @Override - protected void handle(XmlException ex, String message) { - if (ex.getSeverity().compareTo(Severity.ERROR) >= 0) { - this.errCount++; + private @NonNull AccumulatingMessageHandler getXmlMessagePrinter() { + return new AccumulatingMessageHandler( + entry -> { + Level level = entry.getSeverity() == XmlSeverity.WARNING ? Level.WARN : Level.ERROR; + reporter.logEx(level, entry.toString(), new Object[0], entry.getCause()); + }, + XmlSeverity.WARNING + ) { + @Override + protected void printSummaryLine(String kind, XmlSeverity severity, String message) { + Level level = severity == XmlSeverity.WARNING ? Level.WARN : Level.ERROR; + reporter.log(level, message); } - LOG.atLevel(Level.DEBUG).log(ex.toString()); - super.handle(ex, message); // print nicely + }; + } + + private static final class PmdXmlReporterImpl + extends XmlMessageReporterBase + implements PmdXmlReporter { + + private final MessageReporter pmdReporter; + private int errCount; + private final List delayedExceptions = new ArrayList<>(); + + @Override + public void addExceptionToThrowLater(XmlException e) { + delayedExceptions.add(e); + } + + public PmdXmlReporterImpl(MessageReporter pmdReporter, OoxmlFacade ooxml, XmlPositioner positioner) { + super(ooxml, positioner); + this.pmdReporter = pmdReporter; } @Override - protected String template(String message, Object... args) { - return MessageFormat.format(message, args); + protected MessageReporter create2ndStage(XmlPosition position, XmlPositioner positioner, Consumer handleEx) { + return new MessageReporter() { + @Override + public boolean isLoggable(Level level) { + return pmdReporter.isLoggable(level); + } + + + @Override + public void log(Level level, String message, Object... formatArgs) { + logEx(level, message, formatArgs, null); + } + + @Override + public void logEx(Level level, String message, Object[] formatArgs, @Nullable Throwable error) { + XmlException ex = newException(level, error, message, formatArgs); + ooxml.getPrinter().accept(ex); + } + + @Override + public XmlException newException(Level level, Throwable cause, String message, Object... formatArgs) { + XmlSeverity severity; + switch (level) { + case WARN: + severity = XmlSeverity.WARNING; + break; + case ERROR: + errCount++; + severity = XmlSeverity.ERROR; + break; + default: + throw new IllegalArgumentException("unexpected!"); + } + + NiceXmlMessageSpec spec = + new NiceXmlMessageSpec(position, MessageFormat.format(message, formatArgs)) + .withSeverity(severity) + .withCause(cause); + String fullMessage = ooxml.getFormatter().formatSpec(ooxml, spec, positioner); + return new XmlException(spec, fullMessage); + } + + @Override + public int numErrors() { + return pmdReporter.numErrors(); + } + }; } } - private static Level toLevel(Severity severity) { - switch (severity) { - case DEBUG: - return Level.DEBUG; - case INFO: - return Level.INFO; - case WARNING: - return Level.WARN; - case ERROR: - case FATAL: - return Level.ERROR; - } - throw AssertionUtil.shouldNotReachHere("exhaustive"); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java new file mode 100644 index 0000000000..bd0b1dc979 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java @@ -0,0 +1,19 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal.util.xml; + +import net.sourceforge.pmd.util.log.MessageReporter; + +import com.github.oowekyala.ooxml.messages.XmlException; +import com.github.oowekyala.ooxml.messages.XmlMessageReporter; + +/** + * @author Clรฉment Fournier + */ +public interface PmdXmlReporter extends XmlMessageReporter { + + void addExceptionToThrowLater(XmlException e); + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java index 78c643432f..4d5bd8ef43 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java @@ -14,8 +14,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Attr; import org.w3c.dom.Element; - -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; +import org.w3c.dom.Node; /** @@ -36,21 +35,21 @@ public class SchemaConstant { return e.hasAttribute(name) ? Boolean.parseBoolean(attr) : defaultValue; } - public @NonNull String getAttributeOrThrow(Element element, XmlErrorReporter err) { + public @NonNull String getAttributeOrThrow(Element element, PmdXmlReporter err) { String attribute = element.getAttribute(name); if (!element.hasAttribute(name)) { - throw err.error(element, XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); + throw err.at(element).error(XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); } return attribute; } - public @NonNull String getNonBlankAttributeOrThrow(Element element, XmlErrorReporter err) { + public @NonNull String getNonBlankAttributeOrThrow(Element element, PmdXmlReporter err) { String attribute = element.getAttribute(name); if (!element.hasAttribute(name)) { - throw err.error(element, XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); + throw err.at(element).error(XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); } else if (StringUtils.isBlank(attribute)) { - throw err.error(element, XmlErrorMessages.ERR__BLANK_REQUIRED_ATTRIBUTE, name); + throw err.at(element).error(XmlErrorMessages.ERR__BLANK_REQUIRED_ATTRIBUTE, name); } return attribute; } @@ -73,16 +72,16 @@ public class SchemaConstant { .collect(Collectors.toList()); } - public List getElementChildrenNamedReportOthers(Element elt, XmlErrorReporter err) { + public List getElementChildrenNamedReportOthers(Element elt, PmdXmlReporter err) { return XmlUtil.getElementChildrenNamedReportOthers(elt, setOf(name), err) .collect(Collectors.toList()); } - public Element getSingleChildIn(Element elt, XmlErrorReporter err) { + public Element getSingleChildIn(Element elt, PmdXmlReporter err) { return XmlUtil.getSingleChildIn(elt, true, err, setOf(name)); } - public Element getOptChildIn(Element elt, XmlErrorReporter err) { + public Element getOptChildIn(Element elt, PmdXmlReporter err) { return XmlUtil.getSingleChildIn(elt, false, err, setOf(name)); } @@ -106,4 +105,7 @@ public class SchemaConstant { } + public boolean isElementWithName(Node node) { + return node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(name); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java index 6e504f6e1d..fb82b2ee95 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java @@ -21,6 +21,15 @@ public final class SchemaConstants { public static final SchemaConstant DEPRECATED = new SchemaConstant("deprecated"); + // ruleset + public static final SchemaConstant EXCLUDE_PATTERN = new SchemaConstant("exclude-pattern"); + public static final SchemaConstant INCLUDE_PATTERN = new SchemaConstant("include-pattern"); + public static final SchemaConstant RULE = new SchemaConstant("rule"); + + public static final SchemaConstant EXCLUDE = new SchemaConstant("exclude"); + public static final SchemaConstant PRIORITY = new SchemaConstant("priority"); + + private SchemaConstants() { // utility class } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java index fda873ad58..cf09d3de45 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java @@ -21,7 +21,6 @@ import org.w3c.dom.Node; import net.sourceforge.pmd.properties.xml.XmlMapper; import com.github.oowekyala.ooxml.DomUtils; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; public final class XmlUtil { @@ -39,13 +38,13 @@ public final class XmlUtil { return getElementChildren(parent).filter(e -> names.contains(e.getTagName())); } - public static Stream getElementChildrenNamedReportOthers(Element parent, Set names, XmlErrorReporter err) { + public static Stream getElementChildrenNamedReportOthers(Element parent, Set names, PmdXmlReporter err) { return getElementChildren(parent) .map(it -> { if (names.contains(it.getTagName())) { return it; } else { - err.warn(it, IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), formatPossibleNames(names)); + err.at(it).warn(IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), formatPossibleNames(names)); return null; } }).filter(Objects::nonNull); @@ -55,10 +54,10 @@ public final class XmlUtil { return getElementChildren(parent).filter(e -> name.equals(e.getTagName())); } - public static T expectElement(XmlErrorReporter err, Element elt, XmlMapper syntax) { + public static T expectElement(PmdXmlReporter err, Element elt, XmlMapper syntax) { if (!syntax.getReadElementNames().contains(elt.getTagName())) { - err.warn(elt, "Wrong name, expected " + formatPossibleNames(syntax.getReadElementNames())); + err.at(elt).warn("Wrong name, expected " + formatPossibleNames(syntax.getReadElementNames())); } else { return syntax.fromXml(elt, err); } @@ -67,28 +66,28 @@ public final class XmlUtil { } - public static List getChildrenExpectSingleName(Element elt, String name, XmlErrorReporter err) { + public static List getChildrenExpectSingleName(Element elt, String name, PmdXmlReporter err) { return XmlUtil.getElementChildren(elt).peek(it -> { if (!it.getTagName().equals(name)) { - err.warn(it, IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), name); + err.at(it).warn(IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), name); } }).collect(Collectors.toList()); } - public static Element getSingleChildIn(Element elt, boolean throwOnMissing, XmlErrorReporter err, Set names) { + public static Element getSingleChildIn(Element elt, boolean throwOnMissing, PmdXmlReporter err, Set names) { List children = getElementChildrenNamed(elt, names).collect(Collectors.toList()); if (children.size() == 1) { return children.get(0); } else if (children.isEmpty()) { if (throwOnMissing) { - throw err.error(elt, ERR__MISSING_REQUIRED_ELEMENT, formatPossibleNames(names)); + throw err.at(elt).error(ERR__MISSING_REQUIRED_ELEMENT, formatPossibleNames(names)); } else { return null; } } else { for (int i = 1; i < children.size(); i++) { Element child = children.get(i); - err.warn(child, IGNORED__DUPLICATE_CHILD_ELEMENT, child.getTagName()); + err.at(child).warn(IGNORED__DUPLICATE_CHILD_ELEMENT, child.getTagName()); } return children.get(0); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index 3a32e9eafd..b9539f3e1b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -10,19 +10,18 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; - /** * Decorates an XmlMapper with some {@link PropertyConstraint}s. * Those are checked when the value is parsed. This is used to * report errors on the most specific failing element. * *

Note that this is the only XmlMapper that *applies* constraints - * in {@link #fromXml(Element, XmlErrorReporter)}. A {@link SeqSyntax} + * in {@link #fromXml(Element, PmdXmlReporter)}. A {@link SeqSyntax} * or {@link OptionalSyntax} may return some constraints in {@link #getConstraints()} * that are derived from the constraints of the item, yet not check them * on elements (they will be applied on each element by the {@link XmlMapper} @@ -40,13 +39,13 @@ class ConstraintDecorator extends XmlMapper { } @Override - public T fromXml(Element element, XmlErrorReporter err) { + public T fromXml(Element element, PmdXmlReporter err) { T t = xmlMapper.fromXml(element, err); XmlSyntaxUtils.checkConstraintsThrow( t, constraints, - s -> err.error(element, XmlErrorMessages.ERR__CONSTRAINT_NOT_SATISFIED, s) + s -> err.at(element).error(XmlErrorMessages.ERR__CONSTRAINT_NOT_SATISFIED, s) ); return t; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index 977fd80e34..d0c81defa8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -15,13 +15,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; - /** * A set of syntaxes for read and write. One special syntax is designated * as the one used to write elements, the others are used to read. @@ -122,10 +121,10 @@ final class MapperSet extends XmlMapper { } @Override - public T fromXml(Element element, XmlErrorReporter err) { + public T fromXml(Element element, PmdXmlReporter err) { XmlMapper syntax = readIndex.get(element.getTagName()); if (syntax == null) { - throw err.error(element, XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, element.getTagName(), XmlUtil.formatPossibleNames(readIndex.keySet())); + throw err.at(element).error(XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, element.getTagName(), XmlUtil.formatPossibleNames(readIndex.keySet())); } else { return syntax.fromXml(element, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index d38ce1aaf2..6c89e78532 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -13,11 +13,10 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; - /** * Serialize an optional value. If the value is itself an {@code Optional}, * then mentioning {@code } will yield a toplevel empty optional. So @@ -69,7 +68,7 @@ final class OptionalSyntax extends XmlMapper> { } @Override - public Optional fromXml(Element element, XmlErrorReporter err) { + public Optional fromXml(Element element, PmdXmlReporter err) { if (EMPTY_NAME.equals(element.getTagName())) { return Optional.empty(); } else { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index 5047372187..43401e114c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -12,13 +12,12 @@ import java.util.stream.Collectors; import org.w3c.dom.Element; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; - /** * Serialize to and from a simple string. Examples: * @@ -47,8 +46,8 @@ final class SeqSyntax> extends StableXmlMapper { } @Override - public C fromXml(Element element, XmlErrorReporter err) { - RuntimeException aggregateEx = err.error(element, XmlErrorMessages.ERR__LIST_CONSTRAINT_NOT_SATISFIED); + public C fromXml(Element element, PmdXmlReporter err) { + RuntimeException aggregateEx = err.at(element).error(XmlErrorMessages.ERR__LIST_CONSTRAINT_NOT_SATISFIED); C result = XmlUtil.getElementChildren(element) .map(child -> { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index cc01bb7cf0..c8e8d7292d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -15,11 +15,10 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.PredicateUtil; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; - /** * Serialize to and from a simple string. Examples: * @@ -77,11 +76,11 @@ class ValueSyntax extends StableXmlMapper { } @Override - public T fromXml(Element element, XmlErrorReporter err) { + public T fromXml(Element element, PmdXmlReporter err) { try { return fromString.apply(element.getTextContent()); } catch (IllegalArgumentException e) { - throw err.error(element, e); + throw err.at(element).error(e); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index 90eba14748..bca972b184 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -12,12 +12,13 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import org.w3c.dom.Node; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.util.log.MessageReporter; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; +import com.github.oowekyala.ooxml.messages.XmlException; /** @@ -35,11 +36,11 @@ public abstract class XmlMapper { /** * Extract the value from an XML element. If an error occurs, throws - * an exception with {@link XmlErrorReporter#error(Node, Throwable)} + * an {@link XmlException} with {@link MessageReporter#error(Throwable)} * on the most specific node (the type of exception is unspecified). * This will check property constraints if any. */ - public abstract T fromXml(Element element, XmlErrorReporter err); + public abstract T fromXml(Element element, PmdXmlReporter err); /** Write the value into the given XML element. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 0899ad1a51..e3e1624c79 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -27,6 +27,7 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSetReference; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.internal.util.xml.SchemaConstants; import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.internal.util.xml.XmlUtil; @@ -34,6 +35,7 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.RuleReference; +import net.sourceforge.pmd.lang.rule.internal.CommonPropertyDescriptors; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; @@ -42,7 +44,6 @@ import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.StringUtil; import com.github.oowekyala.ooxml.DomUtils; -import com.github.oowekyala.ooxml.messages.XmlErrorReporter; import com.github.oowekyala.ooxml.messages.XmlException; @@ -102,7 +103,7 @@ public class RuleFactory { * * @return A rule reference to the referenced rule */ - public RuleReference decorateRule(Rule referencedRule, RuleSetReference ruleSetReference, Element ruleElement, XmlErrorReporter err) { + public RuleReference decorateRule(Rule referencedRule, RuleSetReference ruleSetReference, Element ruleElement, PmdXmlReporter err) { RuleReference ruleReference = new RuleReference(referencedRule, ruleSetReference); DomUtils.getAttributeOpt(ruleElement, DEPRECATED).map(Boolean::parseBoolean).ifPresent(ruleReference::setDeprecated); @@ -125,8 +126,7 @@ public class RuleFactory { setPropertyValues(ruleReference, node, err); break; default: - throw err.error( - node, + throw err.at(node).error( XmlErrorMessages.ERR__UNEXPECTED_ELEMENT_IN, "rule " + ruleReference.getName() ); @@ -148,14 +148,14 @@ public class RuleFactory { * * @throws IllegalArgumentException if the element doesn't describe a valid rule. */ - public Rule buildRule(Element ruleElement, XmlErrorReporter err) { + public Rule buildRule(Element ruleElement, PmdXmlReporter err) { Rule rule; try { String clazz = getNonBlankAttribute(ruleElement, err, CLASS); rule = resourceLoader.loadRuleFromClassPath(clazz); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - throw err.error(ruleElement.getAttributeNode(CLASS), e); + throw err.at(ruleElement.getAttributeNode(CLASS)).error(e); } rule.setName(getNonBlankAttribute(ruleElement, err, NAME)); @@ -191,45 +191,45 @@ public class RuleFactory { setPropertyValues(rule, node, err); break; default: - throw err.error(node, - XmlErrorMessages.ERR__UNEXPECTED_ELEMENT_IN, - "rule " + ruleElement.getAttribute(NAME)); + throw err.at(node).error( + XmlErrorMessages.ERR__UNEXPECTED_ELEMENT_IN, + "rule " + ruleElement.getAttribute(NAME)); } } return rule; } - private void checkVersionsAreOrdered(Element ruleElement, XmlErrorReporter err, Rule rule) { + private void checkVersionsAreOrdered(Element ruleElement, PmdXmlReporter err, Rule rule) { if (rule.getMinimumLanguageVersion() != null && rule.getMaximumLanguageVersion() != null && rule.getMinimumLanguageVersion().compareTo(rule.getMaximumLanguageVersion()) > 0) { - throw err.fatal( - ruleElement.getAttributeNode(MINIMUM_LANGUAGE_VERSION), - XmlErrorMessages.ERR__INVALID_VERSION_RANGE, - rule.getMinimumLanguageVersion(), - rule.getMaximumLanguageVersion() - ); + throw err.at(ruleElement.getAttributeNode(MINIMUM_LANGUAGE_VERSION)) + .error( + XmlErrorMessages.ERR__INVALID_VERSION_RANGE, + rule.getMinimumLanguageVersion(), + rule.getMaximumLanguageVersion() + ); } } - private @NonNull RulePriority parsePriority(XmlErrorReporter err, Element node, String trim) { + private @NonNull RulePriority parsePriority(PmdXmlReporter err, Element node, String trim) { try { int i = Integer.parseInt(trim.trim()); RulePriority rp = RulePriority.valueOfNullable(i); if (rp == null) { - err.warn(node, XmlErrorMessages.WARN__INVALID_PRIORITY_VALUE, i); + err.at(node).warn(XmlErrorMessages.WARN__INVALID_PRIORITY_VALUE, i); return RulePriority.MEDIUM; } else { return rp; } } catch (NumberFormatException e) { - err.warn(node, XmlErrorMessages.WARN__INVALID_PRIORITY_VALUE, trim); + err.at(node).warn(XmlErrorMessages.WARN__INVALID_PRIORITY_VALUE, trim); return RulePriority.MEDIUM; } } - private LanguageVersion getLanguageVersion(Element ruleElement, XmlErrorReporter err, Language language, String attrName) { + private LanguageVersion getLanguageVersion(Element ruleElement, PmdXmlReporter err, Language language, String attrName) { if (ruleElement.hasAttribute(attrName)) { String attrValue = ruleElement.getAttribute(attrName); LanguageVersion version = language.getVersion(attrValue); @@ -242,24 +242,24 @@ public class RuleFactory { String message = supportedVersions.isEmpty() ? ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION : ERR__INVALID_LANG_VERSION; - throw err.fatal( - ruleElement.getAttributeNode(attrName), - message, - attrValue, - language.getTerseName(), - supportedVersions - ); + throw err.at(ruleElement.getAttributeNode(attrName)) + .error( + message, + attrValue, + language.getTerseName(), + supportedVersions + ); } return version; } return null; } - private void setLanguage(Element ruleElement, XmlErrorReporter err, Rule rule) { + private void setLanguage(Element ruleElement, PmdXmlReporter err, Rule rule) { String langId = getNonBlankAttribute(ruleElement, err, LANGUAGE); Language lang = LanguageRegistry.findLanguageByTerseName(langId); if (lang == null) { - throw err.fatal(ruleElement.getAttributeNode(LANGUAGE), "Invalid language ''{0}'', possible values are {1}", langId, supportedLanguages()); + throw err.at(ruleElement.getAttributeNode(LANGUAGE)).error("Invalid language ''{0}'', possible values are {1}", langId, supportedLanguages()); } rule.setLanguage(lang); } @@ -268,14 +268,14 @@ public class RuleFactory { return LanguageRegistry.getLanguages().stream().map(Language::getTerseName).map(StringUtil::inSingleQuotes).collect(Collectors.joining(", ")); } - private @NonNull String getNonBlankAttribute(Element ruleElement, XmlErrorReporter err, String attrName) { + private @NonNull String getNonBlankAttribute(Element ruleElement, PmdXmlReporter err, String attrName) { String clazz = ruleElement.getAttribute(attrName); if (StringUtils.isBlank(clazz)) { Attr attr = ruleElement.getAttributeNode(attrName); if (attr == null) { - throw err.fatal(ruleElement, "Missing {0} attribute", attrName); + throw err.at(ruleElement).error("Missing {0} attribute", attrName); } else { - throw err.fatal(attr, "This attribute may not be blank"); + throw err.at(attr).error("This attribute may not be blank"); } } return clazz; @@ -285,11 +285,11 @@ public class RuleFactory { * Parses the properties node and adds property definitions to the builder. Doesn't care for value overriding, that * will be handled after the rule instantiation. * - * @param rule Rule builder + * @param rule Rule builder * @param propertiesNode Node to parse * @param err Error reporter */ - private void parsePropertiesForDefinitions(Rule rule, Element propertiesNode, @NonNull XmlErrorReporter err) { + private void parsePropertiesForDefinitions(Rule rule, Element propertiesNode, @NonNull PmdXmlReporter err) { for (Element child : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesNode, err)) { if (isPropertyDefinition(child)) { rule.definePropertyDescriptor(parsePropertyDefinition(child, err)); @@ -303,21 +303,21 @@ public class RuleFactory { * @param rule The rule * @param propertiesElt The {@literal } element */ - private void setPropertyValues(Rule rule, Element propertiesElt, XmlErrorReporter err) { + private void setPropertyValues(Rule rule, Element propertiesElt, PmdXmlReporter err) { Set overridden = new HashSet<>(); XmlException exception = null; for (Element element : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesElt, err)) { String name = SchemaConstants.NAME.getAttributeOrThrow(element, err); if (!overridden.add(name)) { - err.warn(element, IGNORED__DUPLICATE_PROPERTY_SETTER, name); + err.at(element).warn(IGNORED__DUPLICATE_PROPERTY_SETTER, name); continue; } PropertyDescriptor desc = rule.getPropertyDescriptor(name); if (desc == null) { // todo just warn and ignore - throw err.error(element, ERR__PROPERTY_DOES_NOT_EXIST, name, rule.getName()); + throw err.at(element).error(ERR__PROPERTY_DOES_NOT_EXIST, name, rule.getName()); } try { setRulePropertyCapture(rule, desc, element, err); @@ -334,7 +334,7 @@ public class RuleFactory { } } - private void setRulePropertyCapture(Rule rule, PropertyDescriptor descriptor, Element propertyElt, XmlErrorReporter err) { + private void setRulePropertyCapture(Rule rule, PropertyDescriptor descriptor, Element propertyElt, PmdXmlReporter err) { T value = parsePropertyValue(propertyElt, err, descriptor.xmlMapper()); rule.setProperty(descriptor, value); } @@ -358,24 +358,24 @@ public class RuleFactory { * * @return The property descriptor */ - private static PropertyDescriptor parsePropertyDefinition(Element propertyElement, XmlErrorReporter err) { + private static PropertyDescriptor parsePropertyDefinition(Element propertyElement, PmdXmlReporter err) { String typeId = SchemaConstants.PROPERTY_TYPE.getAttributeOrThrow(propertyElement, err); PropertyTypeId factory = PropertyTypeId.lookupMnemonic(typeId); if (factory == null) { - throw err.fatal( - PROPERTY_TYPE.getAttributeNode(propertyElement), - "Unsupported property type ''{0}''", - typeId - ); + throw err.at(PROPERTY_TYPE.getAttributeNode(propertyElement)) + .error( + "Unsupported property type ''{0}''", + typeId + ); } return propertyDefCapture(propertyElement, err, factory.getBuilderUtils()); } private static PropertyDescriptor propertyDefCapture(Element propertyElement, - XmlErrorReporter err, + PmdXmlReporter err, BuilderAndMapper factory) { // TODO support constraints like numeric range @@ -391,11 +391,11 @@ public class RuleFactory { } catch (IllegalArgumentException e) { // builder threw, rethrow with XML location - throw err.error(propertyElement, e.getMessage()); + throw err.at(propertyElement).error(e); } } - private static T parsePropertyValue(Element propertyElt, XmlErrorReporter err, XmlMapper syntax) { + private static T parsePropertyValue(Element propertyElt, PmdXmlReporter err, XmlMapper syntax) { @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElt); if (defaultAttr != null) { Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElt); @@ -409,10 +409,10 @@ public class RuleFactory { try { return syntax.fromString(defaultAttr); } catch (IllegalArgumentException e) { - throw err.error(attrNode, e); + throw err.at(attrNode).error(e); } catch (UnsupportedOperationException e) { - throw err.error(attrNode, - ERR__UNSUPPORTED_VALUE_ATTRIBUTE, + throw err.at(attrNode) + .error(ERR__UNSUPPORTED_VALUE_ATTRIBUTE, String.join("\nor\n", syntax.getExamples())); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/MessageReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/MessageReporter.java index 689e4f41c4..836380c8c2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/MessageReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/MessageReporter.java @@ -5,7 +5,9 @@ package net.sourceforge.pmd.util.log; import java.text.MessageFormat; +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.event.Level; import net.sourceforge.pmd.annotation.InternalApi; @@ -26,9 +28,16 @@ public interface MessageReporter { boolean isLoggable(Level level); - void log(Level level, String message, Object... formatArgs); + default void log(Level level, String message, Object... formatArgs) { + logEx(level, message, formatArgs, null); + } - void logEx(Level level, String message, Object[] formatArgs, Throwable error); + void logEx(Level level, @Nullable String message, Object[] formatArgs, @Nullable Throwable error); + + default RuntimeException newException(Level level, @Nullable Throwable cause, String message, Object... formatArgs) { + logEx(level, message, formatArgs, cause); + return new RuntimeException(MessageFormat.format(message, formatArgs), cause); + } default void info(String message, Object... formatArgs) { log(Level.INFO, message, formatArgs); @@ -46,8 +55,19 @@ public interface MessageReporter { logEx(Level.WARN, message, formatArgs, error); } - default void error(String message, Object... formatArgs) { + default RuntimeException error(String message, Object... formatArgs) { log(Level.ERROR, message, formatArgs); + return newException(Level.ERROR, null, message, formatArgs); + } + + default RuntimeException error(Throwable cause, String contextMessage, Object... formatArgs) { + logEx(Level.ERROR, contextMessage, formatArgs, Objects.requireNonNull(cause)); + return newException(Level.ERROR, null, contextMessage, formatArgs); + } + + default RuntimeException error(Throwable error) { + logEx(Level.ERROR, null, new Object[0], Objects.requireNonNull(error)); + return newException(Level.ERROR, error, error.getMessage()); } default void errorEx(String message, Throwable error) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 314535bd40..913969ee65 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -654,17 +654,17 @@ public class RuleSetFactoryTest { @Test public void testInvertedMinimumMaximumLanguageVersions() { assertCannotParse(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS); - verifyFoundAnErrorWithMessage(containing("versionRange")); + verifyFoundAnErrorWithMessage(containing("version range")); } private void verifyFoundAnErrorWithMessage(Predicate messageTest) { Mockito.verify(mockReporter, Mockito.times(1)) - .log(Mockito.eq(Level.ERROR), Mockito.argThat(messageTest::test), Mockito.any()); + .logEx(Mockito.eq(Level.ERROR), Mockito.argThat(messageTest::test), Mockito.any(), Mockito.any()); } private void verifyFoundAWarningWithMessage(Predicate messageTest) { Mockito.verify(mockReporter, Mockito.times(1)) - .log(Mockito.eq(Level.WARN), Mockito.argThat(messageTest::test), Mockito.any()); + .logEx(Mockito.eq(Level.WARN), Mockito.argThat(messageTest::test), Mockito.any(), Mockito.any()); } private static Predicate containing(String part) { From 271b8ab06238762869e275c5e7b296ca0da51637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 13 Apr 2022 20:33:45 +0200 Subject: [PATCH 073/347] Fix tests --- .../net/sourceforge/pmd/RuleSetFactory.java | 33 ++++---- .../net/sourceforge/pmd/RuleSetWriter.java | 4 +- .../pmd/internal/util/FileCollectionUtil.java | 2 +- .../internal/util/xml/SchemaConstants.java | 1 + .../pmd/lang/document/FileCollector.java | 2 +- .../sourceforge/pmd/rules/RuleFactory.java | 1 - .../log/internal/MessageReporterBase.java | 6 ++ ...leSetFactoryDuplicatedRuleLoggingTest.java | 31 ++++---- .../sourceforge/pmd/RuleSetFactoryTest.java | 75 +++++-------------- .../pmd/RulesetFactoryTestBase.java | 58 ++++++++++++++ .../renderers/SummaryHTMLRendererTest.java | 2 +- .../pmd/testframework/RuleTstTest.java | 13 +--- 12 files changed, 124 insertions(+), 104 deletions(-) create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index dce19fb873..c76f0ab825 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -9,6 +9,7 @@ import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.EXCLUDE; import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.EXCLUDE_PATTERN; import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.INCLUDE_PATTERN; import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PRIORITY; +import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.REF; import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.RULE; import java.io.IOException; @@ -161,6 +162,7 @@ final class RuleSetFactory { "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); } + @SuppressWarnings("PMD.CloseResource") AccumulatingMessageHandler handler = getXmlMessagePrinter(); DocumentBuilder builder = createDocumentBuilder(); InputSource inputSource = new InputSource(inputStream); @@ -329,7 +331,7 @@ final class RuleSetFactory { return; // deleted rule } if (ref.endsWith("xml")) { - parseRuleSetReferenceNode(ruleSetBuilder, ruleNode, ref, rulesetReferences); + parseRuleSetReferenceNode(ruleSetBuilder, ruleNode, ref, rulesetReferences, err); } else if (StringUtils.isBlank(ref)) { parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, err); } else { @@ -351,11 +353,14 @@ final class RuleSetFactory { * The RuleSet reference. * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ - // todo error reporting - private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref, Set rulesetReferences) { + private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, + Element ruleElement, + String ref, + Set rulesetReferences, + PmdXmlReporter err) { String priority = null; NodeList childNodes = ruleElement.getChildNodes(); - Set excludedRulesCheck = new HashSet<>(); + Map excludedRulesCheck = new HashMap<>(); for (int i = 0; i < childNodes.getLength(); i++) { Node child = childNodes.item(i); if (EXCLUDE.isElementWithName(child)) { @@ -363,13 +368,13 @@ final class RuleSetFactory { String excludedRuleName = excludeElement.getAttribute("name"); excludedRuleName = compatibilityFilter.applyExclude(ref, excludedRuleName, this.warnDeprecated); if (excludedRuleName != null) { - excludedRulesCheck.add(excludedRuleName); + excludedRulesCheck.put(excludedRuleName, excludeElement); } } else if (PRIORITY.isElementWithName(child)) { priority = XmlUtil.parseTextNode(child).trim(); } } - final RuleSetReference ruleSetReference = new RuleSetReference(ref, true, excludedRulesCheck); + final RuleSetReference ruleSetReference = new RuleSetReference(ref, true, excludedRulesCheck.keySet()); // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset @@ -397,8 +402,9 @@ final class RuleSetFactory { if (!potentialRules.isEmpty() && potentialRules.size() == countDeprecated) { // all rules in the ruleset have been deprecated - the ruleset itself is considered to be deprecated rulesetDeprecated = true; - LOG.warn("The RuleSet {} has been deprecated and will be removed in PMD {}", - ref, PMDVersion.getNextMajorRelease()); + err.at(REF.getAttributeNode(ruleElement)) + .warn("The RuleSet {0} has been deprecated and will be removed in PMD {1}", + ref, PMDVersion.getNextMajorRelease()); } for (RuleReference r : potentialRules) { @@ -411,14 +417,13 @@ final class RuleSetFactory { } if (!excludedRulesCheck.isEmpty()) { - LOG.warn( - "Unable to exclude rules {} from ruleset reference {}" - + "; perhaps the rule name is misspelled or the rule doesn't exist anymore?", - excludedRulesCheck, ref); + excludedRulesCheck.forEach( + (name, elt) -> + err.at(elt).warn("Exclude pattern ''{0}'' did not match any rule in ruleset {1}", name, ref)); } if (rulesetReferences.contains(ref)) { - LOG.warn("The ruleset {} is referenced multiple times in \"{}\".", ref, ruleSetBuilder.getName()); + err.at(ruleElement).warn("The ruleset {0} is referenced multiple times in \"{1}\".", ref, ruleSetBuilder.getName()); } rulesetReferences.add(ref); } @@ -659,7 +664,7 @@ final class RuleSetFactory { delayedExceptions.add(e); } - public PmdXmlReporterImpl(MessageReporter pmdReporter, OoxmlFacade ooxml, XmlPositioner positioner) { + PmdXmlReporterImpl(MessageReporter pmdReporter, OoxmlFacade ooxml, XmlPositioner positioner) { super(ooxml, positioner); this.pmdReporter = pmdReporter; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index c9a1815483..8445046165 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -21,10 +21,10 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.CDATASection; import org.w3c.dom.DOMException; import org.w3c.dom.Document; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java index 697d16f927..ccb6b5b8f8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java @@ -128,7 +128,7 @@ public final class FileCollectionUtil { private static void addRoot(FileCollector collector, String rootLocation) throws IOException { Path path = Paths.get(rootLocation); if (!Files.exists(path)) { - collector.getReporter().error("No such file {}", path); + collector.getReporter().error("No such file {0}", path); return; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java index fb82b2ee95..1655c445f2 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java @@ -25,6 +25,7 @@ public final class SchemaConstants { public static final SchemaConstant EXCLUDE_PATTERN = new SchemaConstant("exclude-pattern"); public static final SchemaConstant INCLUDE_PATTERN = new SchemaConstant("include-pattern"); public static final SchemaConstant RULE = new SchemaConstant("rule"); + public static final SchemaConstant REF = new SchemaConstant("ref"); public static final SchemaConstant EXCLUDE = new SchemaConstant("exclude"); public static final SchemaConstant PRIORITY = new SchemaConstant("priority"); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java index 55c7fb5d24..a12e77939e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java @@ -138,7 +138,7 @@ public final class FileCollector implements AutoCloseable { */ public boolean addFile(Path file) { if (!Files.isRegularFile(file)) { - reporter.error("Not a regular file {}", file); + reporter.error("Not a regular file {0}", file); return false; } LanguageVersion languageVersion = discoverLanguage(file.toString()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index e3e1624c79..d8d43efa16 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -35,7 +35,6 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.RuleReference; -import net.sourceforge.pmd.lang.rule.internal.CommonPropertyDescriptors; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java index 64f045174d..6b07788ce6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.util.log.internal; import java.text.MessageFormat; +import java.util.Objects; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.event.Level; @@ -43,6 +44,11 @@ abstract class MessageReporterBase implements MessageReporter { @Override public void logEx(Level level, String message, Object[] formatArgs, Throwable error) { if (isLoggable(level)) { + if (error == null) { + Objects.requireNonNull(message, "cannot call this method with null message and error"); + log(level, message, formatArgs); + return; + } message = MessageFormat.format(message, formatArgs); String errorMessage = error.getMessage(); if (errorMessage == null) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java index 051768b639..44e4919941 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java @@ -6,19 +6,10 @@ package net.sourceforge.pmd; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import org.junit.Test; -import org.junit.contrib.java.lang.system.SystemErrRule; -import net.sourceforge.pmd.junit.LocaleRule; - -public class RuleSetFactoryDuplicatedRuleLoggingTest { - @org.junit.Rule - public LocaleRule localeRule = LocaleRule.en(); - - @org.junit.Rule - public final SystemErrRule systemErrRule = new SystemErrRule().mute().enableLog(); +public class RuleSetFactoryDuplicatedRuleLoggingTest extends RulesetFactoryTestBase { @Test public void duplicatedRuleReferenceShouldWarn() { @@ -28,8 +19,10 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { Rule mockRule = ruleset.getRuleByName("DummyBasicMockRule"); assertNotNull(mockRule); assertEquals(RulePriority.MEDIUM, mockRule.getPriority()); - assertTrue(systemErrRule.getLog().contains("The rule DummyBasicMockRule is referenced multiple times in \"Custom Rules\". " - + "Only the last rule configuration is used.")); + verifyFoundAWarningWithMessage(containing( + "The rule DummyBasicMockRule is referenced multiple times in \"Custom Rules\". " + + "Only the last rule configuration is used." + )); } @Test @@ -41,7 +34,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { assertNotNull(mockRule); assertEquals(RulePriority.HIGH, mockRule.getPriority()); assertNotNull(ruleset.getRuleByName("SampleXPathRule")); - assertTrue(systemErrRule.getLog().isEmpty()); + verifyNoWarnings(); } @Test @@ -53,7 +46,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { assertNotNull(mockRule); assertEquals(RulePriority.HIGH, mockRule.getPriority()); assertNotNull(ruleset.getRuleByName("SampleXPathRule")); - assertTrue(systemErrRule.getLog().isEmpty()); + verifyNoWarnings(); } @Test @@ -65,12 +58,14 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { assertNotNull(mockRule); assertEquals(RulePriority.MEDIUM_HIGH, mockRule.getPriority()); assertNotNull(ruleset.getRuleByName("SampleXPathRule")); - assertTrue(systemErrRule.getLog().contains("The rule DummyBasicMockRule is referenced multiple times in \"Custom Rules\". " + verifyFoundAWarningWithMessage(containing( + "The rule DummyBasicMockRule is referenced multiple times in \"Custom Rules\". " + "Only the last rule configuration is used.")); - assertTrue(systemErrRule.getLog().contains("The ruleset rulesets/dummy/basic.xml is referenced multiple times in \"Custom Rules\".")); + verifyFoundAWarningWithMessage(containing( + "The ruleset rulesets/dummy/basic.xml is referenced multiple times in \"Custom Rules\".")); } - private RuleSet loadRuleSet(String ruleSetFilename) { - return new RuleSetLoader().loadFromResource("net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/" + ruleSetFilename); + protected RuleSet loadRuleSet(String ruleSetFilename) { + return loadRuleSetInDir("net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest", ruleSetFilename); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 913969ee65..f5d0e67b76 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -16,45 +16,20 @@ import java.io.InputStream; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import java.util.function.Predicate; -import org.apache.commons.lang3.StringUtils; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; -import org.junit.contrib.java.lang.system.SystemErrRule; import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; -import net.sourceforge.pmd.junit.LocaleRule; import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.MockRule; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.util.ResourceLoader; -import net.sourceforge.pmd.util.log.MessageReporter; -public class RuleSetFactoryTest { +public class RuleSetFactoryTest extends RulesetFactoryTestBase { - private static final Logger LOG = LoggerFactory.getLogger(RuleSetFactoryTest.class); - - @org.junit.Rule - public LocaleRule localeRule = LocaleRule.en(); - - @org.junit.Rule - public final SystemErrRule systemErrRule = new SystemErrRule().muteForSuccessfulTests().enableLog(); - - private MessageReporter mockReporter; - - - @Before - public void setup() { - // mockReporter = Mockito.spy(new SimpleMessageReporter(LOG)); - mockReporter = Mockito.mock(MessageReporter.class); - } @Test public void testRuleSetFileName() { @@ -222,7 +197,7 @@ public class RuleSetFactoryTest { assertNotNull(rule); assertNull(rs.getRuleByName("OldName")); - assertTrue(systemErrRule.getLog().isEmpty()); + verifyNoWarnings(); } /** @@ -295,7 +270,7 @@ public class RuleSetFactoryTest { assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); - assertTrue(systemErrRule.getLog().isEmpty()); + verifyNoWarnings(); } /** @@ -324,7 +299,7 @@ public class RuleSetFactoryTest { assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); - assertTrue(systemErrRule.getLog().isEmpty()); + verifyNoWarnings(); } /** @@ -349,12 +324,13 @@ public class RuleSetFactoryTest { assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); - assertEquals(0, - StringUtils.countMatches(systemErrRule.getLog(), - "WARN net.sourceforge.pmd.RuleSetFactory - Discontinue using Rule rulesets/dummy/basic.xml/DeprecatedRule as it is scheduled for removal from PMD.")); - assertEquals(1, - StringUtils.countMatches(systemErrRule.getLog(), - "WARN net.sourceforge.pmd.RuleSetFactory - Unable to exclude rules [NonExistingRule] from ruleset reference rulesets/dummy/basic.xml; perhaps the rule name is misspelled or the rule doesn't exist anymore?")); + verifyFoundWarningWithMessage( + Mockito.never(), + containing("Discontinue using Rule rulesets/dummy/basic.xml/DeprecatedRule") + ); + verifyFoundAWarningWithMessage(containing( + "Exclude pattern 'NonExistingRule' did not match any rule in ruleset" + )); } /** @@ -371,9 +347,9 @@ public class RuleSetFactoryTest { assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); - assertEquals(1, - StringUtils.countMatches(systemErrRule.getLog(), - "WARN net.sourceforge.pmd.RuleSetFactory - The RuleSet rulesets/dummy/deprecated.xml has been deprecated and will be removed in PMD")); + verifyFoundAWarningWithMessage(containing( + "The RuleSet rulesets/dummy/deprecated.xml has been deprecated and will be removed in PMD" + )); } /** @@ -390,9 +366,10 @@ public class RuleSetFactoryTest { assertEquals(1, rs.getRules().size()); assertNotNull(rs.getRuleByName("DummyBasic2MockRule")); - assertEquals(0, - StringUtils.countMatches(systemErrRule.getLog(), - "WARN net.sourceforge.pmd.RuleSetFactory - Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic2.xml/DummyBasicMockRule. PMD")); + verifyFoundWarningWithMessage( + Mockito.never(), + containing("Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic2.xml/DummyBasicMockRule") + ); } @Test @@ -657,20 +634,6 @@ public class RuleSetFactoryTest { verifyFoundAnErrorWithMessage(containing("version range")); } - private void verifyFoundAnErrorWithMessage(Predicate messageTest) { - Mockito.verify(mockReporter, Mockito.times(1)) - .logEx(Mockito.eq(Level.ERROR), Mockito.argThat(messageTest::test), Mockito.any(), Mockito.any()); - } - - private void verifyFoundAWarningWithMessage(Predicate messageTest) { - Mockito.verify(mockReporter, Mockito.times(1)) - .logEx(Mockito.eq(Level.WARN), Mockito.argThat(messageTest::test), Mockito.any(), Mockito.any()); - } - - private static Predicate containing(String part) { - return it -> it.contains(part); - } - @Test public void testDirectDeprecatedRule() { Rule r = loadFirstRule(DIRECT_DEPRECATED_RULE); @@ -776,7 +739,7 @@ public class RuleSetFactoryTest { + "\n"); assertEquals(0, ruleset.getRules().size()); - assertTrue(systemErrRule.getLog().isEmpty()); + verifyNoWarnings(); } /** diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java new file mode 100644 index 0000000000..b84dbdf4b2 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java @@ -0,0 +1,58 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import java.util.function.Predicate; + +import org.junit.Before; +import org.mockito.Mockito; +import org.mockito.verification.VerificationMode; +import org.slf4j.event.Level; + +import net.sourceforge.pmd.junit.LocaleRule; +import net.sourceforge.pmd.util.log.MessageReporter; + +public class RulesetFactoryTestBase { + + @org.junit.Rule + public LocaleRule localeRule = LocaleRule.en(); + + protected MessageReporter mockReporter; + + @Before + public void setup() { + mockReporter = Mockito.mock(MessageReporter.class); + } + + protected void verifyNoWarnings() { + Mockito.verifyZeroInteractions(mockReporter); + } + + protected static Predicate containing(String part) { + return it -> it.contains(part); + } + + protected void verifyFoundAWarningWithMessage(Predicate messageTest) { + verifyFoundWarningWithMessage(Mockito.times(1), messageTest); + } + + protected void verifyFoundWarningWithMessage(VerificationMode mode, Predicate messageTest) { + Mockito.verify(mockReporter, mode) + .logEx(Mockito.eq(Level.WARN), Mockito.argThat(messageTest::test), Mockito.any(), Mockito.any()); + } + + protected void verifyFoundAnErrorWithMessage(Predicate messageTest) { + Mockito.verify(mockReporter, Mockito.times(1)) + .logEx(Mockito.eq(Level.ERROR), Mockito.argThat(messageTest::test), Mockito.any(), Mockito.any()); + } + + + protected RuleSet loadRuleSetInDir(String resourceDir, String ruleSetFilename) { + RuleSetLoader loader = new RuleSetLoader(); + loader.setReporter(mockReporter); + return loader.loadFromResource(resourceDir + "/" + ruleSetFilename); + } + +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java index 95565ca590..a5063e2b9f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java @@ -7,8 +7,8 @@ package net.sourceforge.pmd.renderers; import static org.junit.Assert.assertEquals; import java.util.Collections; -import java.util.function.Consumer; import java.util.Optional; +import java.util.function.Consumer; import org.junit.Test; diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java index 9f54633ced..9b1fb424a8 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java @@ -5,8 +5,7 @@ package net.sourceforge.pmd.testframework; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -20,13 +19,14 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.AbstractRule; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.test.lang.DummyLanguageModule.DummyRootNode; public class RuleTstTest { private LanguageVersion dummyLanguage = LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion(); - private Rule rule = mock(Rule.class); + private Rule rule = spy(AbstractRule.class); private RuleTst ruleTester = new RuleTst() { }; @@ -42,13 +42,6 @@ public class RuleTstTest { verify(rule).start(any(RuleContext.class)); verify(rule).end(any(RuleContext.class)); - verify(rule).getLanguage(); - verify(rule, times(2)).getTargetSelector(); - verify(rule).getMinimumLanguageVersion(); - verify(rule).getMaximumLanguageVersion(); - verify(rule).apply(any(Node.class), any(RuleContext.class)); - verify(rule, times(4)).getName(); - verify(rule).getPropertiesByPropertyDescriptor(); } @Test From 36b750f682991b484f65d3ca8e6c85a020958855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 13 Apr 2022 21:20:25 +0200 Subject: [PATCH 074/347] Fix tests Move runAndReportStats to PmdAnalysis --- .../main/java/net/sourceforge/pmd/PMD.java | 34 ++----------------- .../java/net/sourceforge/pmd/PmdAnalysis.java | 28 +++++++++++++++ .../net/sourceforge/pmd/RuleSetLoader.java | 4 +-- .../internal/ErrorsAsWarningsReporter.java | 4 +-- .../log/internal/MessageReporterBase.java | 28 +++++++++------ .../pmd/util/log/internal/NoopReporter.java | 2 +- .../log/internal/SimpleMessageReporter.java | 4 +-- .../java/net/sourceforge/pmd/cli/CLITest.java | 2 +- 8 files changed, 56 insertions(+), 50 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index 9c1b261c88..09a2d13301 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -31,7 +31,6 @@ import net.sourceforge.pmd.cli.internal.CliMessages; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.reporting.ReportStats; -import net.sourceforge.pmd.reporting.ReportStatsListener; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.log.MessageReporter; import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; @@ -71,34 +70,6 @@ public final class PMD { } - private static ReportStats runAndReturnStats(PmdAnalysis pmd) { - if (pmd.getRulesets().isEmpty()) { - return ReportStats.empty(); - } - - @SuppressWarnings("PMD.CloseResource") - ReportStatsListener listener = new ReportStatsListener(); - - pmd.addListener(listener); - - try { - pmd.performAnalysis(); - } catch (Exception e) { - pmd.getReporter().errorEx("Exception during processing", e); - ReportStats stats = listener.getResult(); - printErrorDetected(1 + stats.getNumErrors()); - return stats; // should have been closed - } - ReportStats stats = listener.getResult(); - - if (stats.getNumErrors() > 0) { - printErrorDetected(stats.getNumErrors()); - } - - return stats; - } - - static void encourageToUseIncrementalAnalysis(final PMDConfiguration configuration) { if (!configuration.isIgnoreIncrementalAnalysis() && configuration.getAnalysisCache() instanceof NoopAnalysisCache @@ -192,7 +163,7 @@ public final class PMD { return runPmd(parseResult.toConfiguration()); } - private static void printErrorDetected(int errors) { + static void printErrorDetected(int errors) { String msg = CliMessages.errorDetectedMessage(errors, "PMD"); log.error(msg); } @@ -240,8 +211,7 @@ public final class PMD { return StatusCode.ERROR; } try { - ReportStats stats; - stats = PMD.runAndReturnStats(pmd); + ReportStats stats = pmd.runAndReturnStats(); if (pmdReporter.numErrors() > 0) { // processing errors are ignored return StatusCode.ERROR; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java index 1b74fa5ab8..7604d378a1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java @@ -32,6 +32,8 @@ import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.processor.AbstractPMDProcessor; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; +import net.sourceforge.pmd.reporting.ReportStats; +import net.sourceforge.pmd.reporting.ReportStatsListener; import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.DataSource; @@ -400,4 +402,30 @@ public final class PmdAnalysis implements AutoCloseable { } } + ReportStats runAndReturnStats() { + if (getRulesets().isEmpty()) { + return ReportStats.empty(); + } + + @SuppressWarnings("PMD.CloseResource") + ReportStatsListener listener = new ReportStatsListener(); + + addListener(listener); + + try { + performAnalysis(); + } catch (Exception e) { + getReporter().errorEx("Exception during processing", e); + ReportStats stats = listener.getResult(); + PMD.printErrorDetected(1 + stats.getNumErrors()); + return stats; // should have been closed + } + ReportStats stats = listener.getResult(); + + if (stats.getNumErrors() > 0) { + PMD.printErrorDetected(stats.getNumErrors()); + } + + return stats; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java index f75997e3ee..4bafafa73c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -218,7 +218,7 @@ public final class RuleSetLoader { } } if (!anyRules) { - reporter.warn("No rules found. Maybe you misspelled a rule name? ({})", + reporter.warn("No rules found. Maybe you misspelled a rule name? ({0})", StringUtils.join(rulesetPaths, ',')); } return ruleSets; @@ -232,7 +232,7 @@ public final class RuleSetLoader { } } if (ruleset.getRules().isEmpty()) { - reporter.warn("No rules found in ruleset {}", path); + reporter.warn("No rules found in ruleset {0}", path); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/ErrorsAsWarningsReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/ErrorsAsWarningsReporter.java index 80270d9163..71b201e2cc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/ErrorsAsWarningsReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/ErrorsAsWarningsReporter.java @@ -32,10 +32,10 @@ public final class ErrorsAsWarningsReporter extends MessageReporterBase { } @Override - protected void logImpl(Level level, String message, Object[] formatArgs) { + protected void logImpl(Level level, String message) { if (level == Level.ERROR) { level = Level.WARN; } - backend.log(level, message, formatArgs); + backend.log(level, message); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java index 6b07788ce6..9602535dca 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/MessageReporterBase.java @@ -4,13 +4,15 @@ package net.sourceforge.pmd.util.log.internal; +import static net.sourceforge.pmd.util.StringUtil.quoteMessageFormat; + import java.text.MessageFormat; import java.util.Objects; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import org.slf4j.event.Level; -import net.sourceforge.pmd.util.StringUtil; import net.sourceforge.pmd.util.log.MessageReporter; /** @@ -50,33 +52,39 @@ abstract class MessageReporterBase implements MessageReporter { return; } message = MessageFormat.format(message, formatArgs); - String errorMessage = error.getMessage(); - if (errorMessage == null) { - errorMessage = error.getClass().getSimpleName(); - } - errorMessage = StringUtil.quoteMessageFormat(errorMessage); - log(level, message + ": " + errorMessage); + String errorMessage = getErrorMessage(error); + logImpl(level, message + ": " + errorMessage); if (isLoggable(Level.DEBUG)) { - String stackTrace = StringUtil.quoteMessageFormat(ExceptionUtils.getStackTrace(error)); + String stackTrace = quoteMessageFormat(ExceptionUtils.getStackTrace(error)); log(Level.DEBUG, stackTrace); } } } + @NonNull + private String getErrorMessage(Throwable error) { + String errorMessage = error.getMessage(); + if (errorMessage == null) { + errorMessage = error.getClass().getSimpleName(); + } + errorMessage = errorMessage; + return errorMessage; + } + @Override public final void log(Level level, String message, Object... formatArgs) { if (level == Level.ERROR) { this.numErrors++; } if (isLoggable(level)) { - logImpl(level, message, formatArgs); + logImpl(level, MessageFormat.format(message, formatArgs)); } } /** * Perform logging assuming {@link #isLoggable(Level)} is true. */ - protected abstract void logImpl(Level level, String message, Object[] formatArgs); + protected abstract void logImpl(Level level, String message); @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/NoopReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/NoopReporter.java index 98eb4a72a3..2ddfbf6d64 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/NoopReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/NoopReporter.java @@ -25,7 +25,7 @@ public final class NoopReporter extends MessageReporterBase implements MessageRe } @Override - protected void logImpl(Level level, String message, Object[] formatArgs) { + protected void logImpl(Level level, String message) { // noop } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/SimpleMessageReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/SimpleMessageReporter.java index 672e40df98..4201c19f9c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/SimpleMessageReporter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/internal/SimpleMessageReporter.java @@ -30,7 +30,7 @@ public class SimpleMessageReporter extends MessageReporterBase implements Messag } @Override - protected void logImpl(Level level, String message, Object[] formatArgs) { - backend.atLevel(level).log(message, formatArgs); + protected void logImpl(Level level, String message) { + backend.atLevel(level).log(message); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java index 016f208fd6..c8a11b8a40 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java @@ -122,7 +122,7 @@ public class CLITest extends BaseCLITest { @Test public void testWrongRulename() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml/ThisRuleDoesNotExist", }; - String log = runTest(StatusCode.OK, args); + String log = runTest(StatusCode.ERROR, args); assertThat(log, containsString("No rules found. Maybe you misspelled a rule name?" + " (category/java/design.xml/ThisRuleDoesNotExist)")); } From 0dddefa4b83141b4f3feacb274a3ca1e55dff482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 17 Jul 2022 16:54:18 +0200 Subject: [PATCH 075/347] Fix merge --- .../net/sourceforge/pmd/RuleSetWriter.java | 4 +- .../pmd/internal/util/xml/PmdXmlReporter.java | 19 --- .../pmd/internal/util/xml/SchemaConstant.java | 111 --------------- .../internal/util/xml/SchemaConstants.java | 37 ----- .../internal/util/xml/XmlErrorMessages.java | 35 ----- .../pmd/internal/util/xml/XmlUtil.java | 130 ------------------ .../properties/xml/ConstraintDecorator.java | 4 +- .../pmd/properties/xml/MapperSet.java | 10 +- .../pmd/properties/xml/OptionalSyntax.java | 2 +- .../pmd/properties/xml/SeqSyntax.java | 6 +- .../pmd/properties/xml/ValueSyntax.java | 2 +- .../pmd/properties/xml/XmlMapper.java | 2 +- .../pmd/properties/xml/XmlSyntaxUtils.java | 4 +- .../sourceforge/pmd/rules/RuleFactory.java | 28 ++-- .../pmd/util/internal/xml/XmlUtil.java | 18 +++ .../properties/PropertyDescriptorTest.java | 2 +- .../constraints/NumericConstraintsTest.java | 4 +- .../documentation/CommentRequiredRule.java | 1 - .../bestpractices/xml/MissingOverride.xml | 18 ++- .../AvoidBranchingStatementAsLastInLoop.xml | 6 +- 20 files changed, 65 insertions(+), 378 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java delete mode 100755 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java delete mode 100755 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 1ae7f558ac..ba5345c01d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -30,15 +30,15 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; -import net.sourceforge.pmd.internal.util.xml.SchemaConstants; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.PropertyTypeId; -import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.properties.xml.XmlMapper; +import net.sourceforge.pmd.util.IOUtil; +import net.sourceforge.pmd.util.internal.xml.SchemaConstants; /** * This class represents a way to serialize a RuleSet to an XML configuration diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java deleted file mode 100644 index bd0b1dc979..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/PmdXmlReporter.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal.util.xml; - -import net.sourceforge.pmd.util.log.MessageReporter; - -import com.github.oowekyala.ooxml.messages.XmlException; -import com.github.oowekyala.ooxml.messages.XmlMessageReporter; - -/** - * @author Clรฉment Fournier - */ -public interface PmdXmlReporter extends XmlMessageReporter { - - void addExceptionToThrowLater(XmlException e); - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java deleted file mode 100755 index 4d5bd8ef43..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstant.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal.util.xml; - -import static net.sourceforge.pmd.util.CollectionUtil.setOf; - -import java.util.List; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - - -/** - * Constants of the ruleset schema. - */ -public class SchemaConstant { - - private final String name; - - - public SchemaConstant(String name) { - this.name = name; - } - - - public boolean getAsBooleanAttr(Element e, boolean defaultValue) { - String attr = e.getAttribute(name); - return e.hasAttribute(name) ? Boolean.parseBoolean(attr) : defaultValue; - } - - public @NonNull String getAttributeOrThrow(Element element, PmdXmlReporter err) { - String attribute = element.getAttribute(name); - if (!element.hasAttribute(name)) { - throw err.at(element).error(XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); - } - - return attribute; - } - - public @NonNull String getNonBlankAttributeOrThrow(Element element, PmdXmlReporter err) { - String attribute = element.getAttribute(name); - if (!element.hasAttribute(name)) { - throw err.at(element).error(XmlErrorMessages.ERR__MISSING_REQUIRED_ATTRIBUTE, name); - } else if (StringUtils.isBlank(attribute)) { - throw err.at(element).error(XmlErrorMessages.ERR__BLANK_REQUIRED_ATTRIBUTE, name); - } - return attribute; - } - - public @Nullable String getAttributeOpt(Element element) { - String attr = element.getAttribute(name); - return attr.isEmpty() ? null : attr; - } - - public @Nullable Attr getAttributeNode(Element element) { - return element.getAttributeNode(name); - } - - public boolean hasAttribute(Element element) { - return element.hasAttribute(name); - } - - public List getChildrenIn(Element elt) { - return XmlUtil.getElementChildrenNamed(elt, name) - .collect(Collectors.toList()); - } - - public List getElementChildrenNamedReportOthers(Element elt, PmdXmlReporter err) { - return XmlUtil.getElementChildrenNamedReportOthers(elt, setOf(name), err) - .collect(Collectors.toList()); - } - - public Element getSingleChildIn(Element elt, PmdXmlReporter err) { - return XmlUtil.getSingleChildIn(elt, true, err, setOf(name)); - } - - public Element getOptChildIn(Element elt, PmdXmlReporter err) { - return XmlUtil.getSingleChildIn(elt, false, err, setOf(name)); - } - - public void setOn(Element element, String value) { - element.setAttribute(name, value); - } - - /** - * Returns the String name of this attribute. - * - * @return The attribute's name - */ - public String xmlName() { - return name; - } - - - @Override - public String toString() { - return xmlName(); - } - - - public boolean isElementWithName(Node node) { - return node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(name); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java deleted file mode 100755 index 1655c445f2..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/SchemaConstants.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal.util.xml; - - -/** - * Constants of the ruleset schema. - */ -public final class SchemaConstants { - - public static final SchemaConstant PROPERTY_TYPE = new SchemaConstant("type"); - public static final SchemaConstant NAME = new SchemaConstant("name"); - public static final SchemaConstant DESCRIPTION = new SchemaConstant("description"); - public static final SchemaConstant PROPERTY_VALUE = new SchemaConstant("value"); - - public static final SchemaConstant PROPERTY_ELT = new SchemaConstant("property"); - - public static final SchemaConstant PROPERTIES = new SchemaConstant("properties"); - public static final SchemaConstant DEPRECATED = new SchemaConstant("deprecated"); - - - // ruleset - public static final SchemaConstant EXCLUDE_PATTERN = new SchemaConstant("exclude-pattern"); - public static final SchemaConstant INCLUDE_PATTERN = new SchemaConstant("include-pattern"); - public static final SchemaConstant RULE = new SchemaConstant("rule"); - public static final SchemaConstant REF = new SchemaConstant("ref"); - - public static final SchemaConstant EXCLUDE = new SchemaConstant("exclude"); - public static final SchemaConstant PRIORITY = new SchemaConstant("priority"); - - - private SchemaConstants() { - // utility class - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java deleted file mode 100644 index a701c70fd2..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlErrorMessages.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal.util.xml; - -public final class XmlErrorMessages { - - private static final String THIS_WILL_BE_IGNORED = ", this will be ignored"; - - public static final String ERR__UNEXPECTED_ELEMENT = "Unexpected element ''{0}'', expecting {1}"; - public static final String ERR__UNEXPECTED_ELEMENT_IN = "Unexpected element ''{0}'' in {1}, expecting {1}"; - public static final String ERR__MISSING_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is missing"; - public static final String ERR__BLANK_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is blank"; - public static final String ERR__MISSING_REQUIRED_ELEMENT = "Required child element named {0} is missing"; - - public static final String IGNORED__UNEXPECTED_ELEMENT = ERR__UNEXPECTED_ELEMENT + THIS_WILL_BE_IGNORED; - public static final String IGNORED__DUPLICATE_CHILD_ELEMENT = "Duplicated child with name ''{0}''" + THIS_WILL_BE_IGNORED; - public static final String IGNORED__DUPLICATE_PROPERTY_SETTER = "Duplicate property tag with name ''{0}''" + THIS_WILL_BE_IGNORED; - - public static final String ERR__UNSUPPORTED_VALUE_ATTRIBUTE = "This property does not support the attribute syntax.\nUse a nested element, e.g. {1}"; - public static final String ERR__PROPERTY_DOES_NOT_EXIST = "Cannot set non-existent property ''{0}'' on rule {1}"; - public static final String ERR__CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied: {0}"; - public static final String ERR__LIST_CONSTRAINT_NOT_SATISFIED = "Property constraint(s) not satisfied on items"; - public static final String ERR__INVALID_VERSION_RANGE = "Invalid language version range, minimum version ''{0}'' is greater than maximum version ''{1}''"; - public static final String ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION = "Invalid language version ''{0}'' for language ''{1}'', the language has no named versions"; - public static final String ERR__INVALID_LANG_VERSION = "Invalid language version ''{0}'' for language ''{1}'', supported versions are {2}"; - - public static final String WARN__DEPRECATED_USE_OF_ATTRIBUTE = "The use of the ''{0}'' attribute is deprecated. Use a nested element, e.g. {1}"; - public static final String WARN__INVALID_PRIORITY_VALUE = "Not a valid priority ''{}'', expected a number in [1,5]"; - - private XmlErrorMessages() { - // utility class - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java deleted file mode 100644 index cf09d3de45..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/xml/XmlUtil.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal.util.xml; - -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__MISSING_REQUIRED_ELEMENT; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__DUPLICATE_CHILD_ELEMENT; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__UNEXPECTED_ELEMENT; - -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.checkerframework.checker.nullness.qual.Nullable; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import net.sourceforge.pmd.properties.xml.XmlMapper; - -import com.github.oowekyala.ooxml.DomUtils; - -public final class XmlUtil { - - private XmlUtil() { - - } - - public static Stream getElementChildren(Element parent) { - return DomUtils.asList(parent.getChildNodes()).stream() - .filter(it -> it.getNodeType() == Node.ELEMENT_NODE) - .map(Element.class::cast); - } - - public static Stream getElementChildrenNamed(Element parent, Set names) { - return getElementChildren(parent).filter(e -> names.contains(e.getTagName())); - } - - public static Stream getElementChildrenNamedReportOthers(Element parent, Set names, PmdXmlReporter err) { - return getElementChildren(parent) - .map(it -> { - if (names.contains(it.getTagName())) { - return it; - } else { - err.at(it).warn(IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), formatPossibleNames(names)); - return null; - } - }).filter(Objects::nonNull); - } - - public static Stream getElementChildrenNamed(Element parent, String name) { - return getElementChildren(parent).filter(e -> name.equals(e.getTagName())); - } - - public static T expectElement(PmdXmlReporter err, Element elt, XmlMapper syntax) { - - if (!syntax.getReadElementNames().contains(elt.getTagName())) { - err.at(elt).warn("Wrong name, expected " + formatPossibleNames(syntax.getReadElementNames())); - } else { - return syntax.fromXml(elt, err); - } - - return null; - } - - - public static List getChildrenExpectSingleName(Element elt, String name, PmdXmlReporter err) { - return XmlUtil.getElementChildren(elt).peek(it -> { - if (!it.getTagName().equals(name)) { - err.at(it).warn(IGNORED__UNEXPECTED_ELEMENT, it.getTagName(), name); - } - }).collect(Collectors.toList()); - } - - public static Element getSingleChildIn(Element elt, boolean throwOnMissing, PmdXmlReporter err, Set names) { - List children = getElementChildrenNamed(elt, names).collect(Collectors.toList()); - if (children.size() == 1) { - return children.get(0); - } else if (children.isEmpty()) { - if (throwOnMissing) { - throw err.at(elt).error(ERR__MISSING_REQUIRED_ELEMENT, formatPossibleNames(names)); - } else { - return null; - } - } else { - for (int i = 1; i < children.size(); i++) { - Element child = children.get(i); - err.at(child).warn(IGNORED__DUPLICATE_CHILD_ELEMENT, child.getTagName()); - } - return children.get(0); - } - } - - @Nullable - public static String formatPossibleNames(Set names) { - if (names.isEmpty()) { - return null; - } else if (names.size() == 1) { - return "'" + names.iterator().next() + "'"; - } else { - return "one of " + names.stream().map(it -> "'" + it + "'").collect(Collectors.joining(", ")); - } - } - - /** - * Parse a String from a textually type node. - * - * @param node The node. - * - * @return The String. - */ - public static String parseTextNode(Node node) { - final int nodeCount = node.getChildNodes().getLength(); - if (nodeCount == 0) { - return ""; - } - - StringBuilder buffer = new StringBuilder(); - - for (int i = 0; i < nodeCount; i++) { - Node childNode = node.getChildNodes().item(i); - if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) { - buffer.append(childNode.getNodeValue()); - } - } - return buffer.toString(); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index b9539f3e1b..d434445648 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -10,10 +10,10 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; -import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; +import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; +import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; /** * Decorates an XmlMapper with some {@link PropertyConstraint}s. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java index d0c81defa8..7ef9709ac5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java @@ -15,11 +15,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; -import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; -import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; -import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; +import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; +import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; +import net.sourceforge.pmd.util.internal.xml.XmlUtil; /** * A set of syntaxes for read and write. One special syntax is designated @@ -124,7 +124,9 @@ final class MapperSet extends XmlMapper { public T fromXml(Element element, PmdXmlReporter err) { XmlMapper syntax = readIndex.get(element.getTagName()); if (syntax == null) { - throw err.at(element).error(XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, element.getTagName(), XmlUtil.formatPossibleNames(readIndex.keySet())); + throw err.at(element).error(XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, + element.getTagName(), + XmlUtil.formatPossibleNames(XmlUtil.toConstants(readIndex.keySet()))); } else { return syntax.fromXml(element, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java index 6c89e78532..467b5c55b8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java @@ -13,9 +13,9 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; +import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; /** * Serialize an optional value. If the value is itself an {@code Optional}, diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java index 43401e114c..ac71b54771 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java @@ -12,11 +12,11 @@ import java.util.stream.Collectors; import org.w3c.dom.Element; -import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; -import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; -import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; +import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; +import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; +import net.sourceforge.pmd.util.internal.xml.XmlUtil; /** * Serialize to and from a simple string. Examples: diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index c8e8d7292d..0f73cc5c0b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -15,9 +15,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.PredicateUtil; -import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; +import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; /** * Serialize to and from a simple string. Examples: diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java index bca972b184..7515860e77 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java @@ -13,9 +13,9 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; import net.sourceforge.pmd.util.log.MessageReporter; import com.github.oowekyala.ooxml.messages.XmlException; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index 74fc94a69d..d889d9081d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -19,9 +19,9 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; -import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; +import net.sourceforge.pmd.util.internal.xml.XmlUtil; /** * This is internal API and shouldn't be used directly by clients. @@ -227,7 +227,7 @@ public final class XmlSyntaxUtils { mappings::get, PropertyConstraint.fromPredicate( mappings::containsKey, - "Should be " + XmlUtil.formatPossibleNames(mappings.keySet()) + "Should be " + XmlUtil.formatPossibleNames(XmlUtil.toConstants(mappings.keySet())) ) ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index ff95c9881e..33a6e62116 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -4,17 +4,18 @@ package net.sourceforge.pmd.rules; -import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PROPERTY_TYPE; -import static net.sourceforge.pmd.internal.util.xml.SchemaConstants.PROPERTY_VALUE; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE; -import static net.sourceforge.pmd.internal.util.xml.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER; -import static net.sourceforge.pmd.internal.util.xml.XmlUtil.getSingleChildIn; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.MAXIMUM_LANGUAGE_VERSION; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.MINIMUM_LANGUAGE_VERSION; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.NAME; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_TYPE; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_VALUE; +import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION; +import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION; +import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST; +import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE; +import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER; +import static net.sourceforge.pmd.util.internal.xml.XmlUtil.getSingleChildIn; + import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @@ -28,10 +29,6 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSetReference; import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.internal.util.xml.PmdXmlReporter; -import net.sourceforge.pmd.internal.util.xml.SchemaConstants; -import net.sourceforge.pmd.internal.util.xml.XmlErrorMessages; -import net.sourceforge.pmd.internal.util.xml.XmlUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -379,7 +376,7 @@ public class RuleFactory { } private static T parsePropertyValue(Element propertyElt, PmdXmlReporter err, XmlMapper syntax) { - @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOpt(propertyElt); + @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOrNull(propertyElt); if (defaultAttr != null) { Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElt); @@ -400,7 +397,10 @@ public class RuleFactory { } } else { - Element child = getSingleChildIn(propertyElt, true, err, syntax.getReadElementNames()); + Element child = getSingleChildIn(propertyElt, + true, + err, + XmlUtil.toConstants(syntax.getReadElementNames())); // this will report the correct error if any return syntax.fromXml(child, err); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java index 5d66faab05..107b07bcb2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java @@ -19,6 +19,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; import org.w3c.dom.Node; +import net.sourceforge.pmd.properties.xml.XmlMapper; +import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.StringUtil; import com.github.oowekyala.ooxml.DomUtils; @@ -101,6 +103,10 @@ public final class XmlUtil { } } + public static Set toConstants(Set names) { + return CollectionUtil.map(Collectors.toSet(), names, SchemaConstant::new); + } + public static @Nullable String formatPossibleNames(Set names) { if (names.isEmpty()) { return null; @@ -137,4 +143,16 @@ public final class XmlUtil { } return buffer.toString(); } + + public static T expectElement(PmdXmlReporter err, Element elt, XmlMapper syntax) { + + if (!syntax.getReadElementNames().contains(elt.getTagName())) { + err.at(elt).warn("Wrong name, expected " + formatPossibleNames(toConstants(syntax.getReadElementNames()))); + } else { + return syntax.fromXml(elt, err); + } + + return null; + } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index c8830ef70d..7a433402ee 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -305,7 +305,7 @@ class PropertyDescriptorTest { IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> descriptor.valueFrom("InvalidEnumValue")); - assertThat(thrown.getMessage(), containsString("Value was not in the set [TEST_A, TEST_B, TEST_C]")); + assertThat(thrown.getMessage(), containsString("'InvalidEnumValue' should be one of 'TEST_A', 'TEST_B', 'TEST_C'")); } @Test diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java index 3dc48b665e..070c3f8711 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java @@ -4,7 +4,9 @@ package net.sourceforge.pmd.properties.constraints; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java index 3e4fad51b0..bcaccdfa76 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.documentation; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml index bdbcac52a0..6f142a4f35 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml @@ -294,22 +294,20 @@ public class SubclassWithGenericMethod extends AbstractClass { Consider varargs parameter 1 - The method 'setProperty(MultiValuePropertyDescriptor<V>, V[])' is missing an @Override annotation. + , V[])' is missing an @Override annotation.]]> {} +interface R { + void setProperty(M m, V... vs); +} -/** - * Base class for Rule implementations which delegate to another Rule instance. - */ -public abstract class AbstractDelegateRule implements Rule { +public abstract class AbstractDelegateRule implements R { // missing - public void setProperty(MultiValuePropertyDescriptor propertyDescriptor, V... values) { - Rule.super.setProperty(propertyDescriptor, values); + public void setProperty(M propertyDescriptor, V... values) { } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml index cb83bafd0a..1dd70fd897 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidBranchingStatementAsLastInLoop.xml @@ -138,7 +138,7 @@ public class Foo { violations: break:for/do/while - for|do|while + for,do,while 4 @@ -149,7 +149,7 @@ public class Foo { violations: continue:for/do/while - for|do|while + for,do,while 4 11,20,29,38 @@ -160,7 +160,7 @@ public class Foo { violations: return:for/do/while - for|do|while + for,do,while 4 5,14,23,32 From 29a711335f9351a39a50e18ba7676ee6bb4d234e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jul 2022 13:27:34 +0200 Subject: [PATCH 076/347] Reintroduce separate default delimiter Let's change that later --- pmd-core/pmd-core-checkstyle-suppressions.xml | 1 - .../pmd/properties/PropertyBuilder.java | 11 ++++++++ .../pmd/properties/PropertyFactory.java | 14 ++++++---- .../ConstraintViolatedException.java | 18 ++++++++++++ .../pmd/properties/xml/ValueSyntax.java | 4 ++- .../sourceforge/pmd/rules/RuleFactory.java | 28 +++++++++++++++---- .../util/internal/xml/SchemaConstants.java | 1 + .../sourceforge/pmd/RuleSetFactoryTest.java | 2 +- .../properties/PropertyDescriptorTest.java | 8 ++---- .../renderers/CodeClimateRendererTest.java | 2 +- .../bestpractices/GuardLogStatementRule.java | 3 +- .../AvoidDuplicateLiteralsRule.java | 1 + .../rule/errorprone/CloseResourceRule.java | 4 +-- .../lang/java/metrics/impl/xml/CycloTest.xml | 2 +- .../xml/AvoidUsingHardCodedIP.xml | 2 +- .../codestyle/xml/FieldNamingConventions.xml | 2 +- .../rule/documentation/xml/CommentContent.xml | 2 +- .../AvoidBranchingStatementAsLastInLoop.xml | 6 ++-- .../xml/AvoidInstantiatingObjectsInLoops.xml | 6 ++-- .../resources/category/pom/errorprone.xml | 3 +- 20 files changed, 86 insertions(+), 34 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java diff --git a/pmd-core/pmd-core-checkstyle-suppressions.xml b/pmd-core/pmd-core-checkstyle-suppressions.xml index eb502dc7ba..c1647b6530 100644 --- a/pmd-core/pmd-core-checkstyle-suppressions.xml +++ b/pmd-core/pmd-core-checkstyle-suppressions.xml @@ -8,5 +8,4 @@ - \ No newline at end of file diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index df6eda13e2..2407584c61 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper; @@ -57,6 +58,10 @@ public abstract class PropertyBuilder, T> { private final String name; private String description; private T defaultValue; + + /** + * Non-null if declared in XML. + */ protected @Nullable PropertyTypeId typeId; protected boolean isXPathAvailable = false; @@ -466,6 +471,12 @@ public abstract class PropertyBuilder, T> { return this; } + + @InternalApi + public char getMultiValueDelimiter() { + return multiValueDelimiter; + } + /** * Specify that this property may not be parsed from a string. * This is the case for lists of patterns, for instance. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 9832ad2bb5..03406b55c4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -88,8 +88,12 @@ import net.sourceforge.pmd.util.CollectionUtil; public final class PropertyFactory { - /** Default delimiter for all multi-valued properties. */ - public static final char DEFAULT_DELIMITER = ','; + /** Default delimiter for all properties. */ + public static final char DEFAULT_DELIMITER = '|'; + + + /** Default delimiter for numeric properties. */ + public static final char DEFAULT_NUMERIC_DELIMITER = ','; private PropertyFactory() { @@ -128,7 +132,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> intListProperty(String name) { - return intProperty(name).toList(); + return intProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); } @@ -165,7 +169,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> longIntListProperty(String name) { - return longIntProperty(name).toList(); + return longIntProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); } @@ -197,7 +201,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericCollectionPropertyBuilder> doubleListProperty(String name) { - return doubleProperty(name).toList(); + return doubleProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java new file mode 100644 index 0000000000..40a94b59ce --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java @@ -0,0 +1,18 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties.constraints; + +/** + * Thrown when a property constraint is violated. Detected while parsing + * values from XML. + * + * @author Clรฉment Fournier + */ +public class ConstraintViolatedException extends IllegalArgumentException { + + public ConstraintViolatedException(String message) { + super(message); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java index 0f73cc5c0b..d152a44320 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java @@ -15,6 +15,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.PredicateUtil; +import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; @@ -106,9 +107,10 @@ class ValueSyntax extends StableXmlMapper { return new ValueSyntax<>( toString, s -> { + // this is the crucial place where constraints are applied. String error = checker.validate(s); if (error != null) { - throw new IllegalArgumentException(error); + throw new ConstraintViolatedException(error); } return fromString.apply(s); }, diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 33a6e62116..aaa4142cd9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -33,6 +33,8 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.RuleReference; +import net.sourceforge.pmd.properties.PropertyBuilder; +import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; @@ -362,12 +364,13 @@ public class RuleFactory { String name = SchemaConstants.NAME.getNonBlankAttributeOrThrow(propertyElement, err); String description = SchemaConstants.DESCRIPTION.getNonBlankAttributeOrThrow(propertyElement, err); - try { - return factory.newBuilder(name) - .desc(description) - .defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper())) - .build(); + PropertyBuilder builder = factory.newBuilder(name) + .desc(description) + .defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper())); + setPropDelimiter(propertyElement, err, builder); + + return builder.build(); } catch (IllegalArgumentException e) { // builder threw, rethrow with XML location @@ -375,6 +378,21 @@ public class RuleFactory { } } + private static void setPropDelimiter(Element propertyElement, PmdXmlReporter err, PropertyBuilder builder) { + if (builder instanceof PropertyBuilder.GenericCollectionPropertyBuilder) { + String customDelimiter = SchemaConstants.DELIMITER.getAttributeOrNull(propertyElement); + if (customDelimiter != null) { + if (customDelimiter.length() == 1) { + ((GenericCollectionPropertyBuilder) builder).delim(customDelimiter.charAt(0)); + } else { + err.at(SchemaConstants.DELIMITER.getAttributeNode(propertyElement)) + .error("Delimiter is not a single character, it will be defaulted to ''{0}''", + ((GenericCollectionPropertyBuilder) builder).getMultiValueDelimiter()); + } + } + } + } + private static T parsePropertyValue(Element propertyElt, PmdXmlReporter err, XmlMapper syntax) { @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOrNull(propertyElt); if (defaultAttr != null) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java index d6e7c37c94..2313d0afa9 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java @@ -34,6 +34,7 @@ public final class SchemaConstants { public static final SchemaConstant EXTERNAL_INFO_URL = new SchemaConstant("externalInfoUrl"); public static final SchemaConstant EXAMPLE = new SchemaConstant("example"); public static final SchemaConstant SINCE = new SchemaConstant("since"); + public static final SchemaConstant DELIMITER = new SchemaConstant("delimiter"); private SchemaConstants() { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 80ceee0fcb..1bc73cc21e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -183,7 +183,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase { + "class=\"net.sourceforge.pmd.lang.rule.XPathRule\" language=\"dummy\">\n" + " Please move your class to the right folder(rest \nfolder)\n" + " 2\n \n \n "); Object propValue = r.getProperty(r.getPropertyDescriptor("packageRegEx")); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index 7a433402ee..c73eb0fe47 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -39,8 +39,6 @@ import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; /** - * Mostly TODO, I'd rather implement tests on the final version of the framework. - * * @author Clรฉment Fournier * @since 7.0.0 */ @@ -238,8 +236,8 @@ class PropertyDescriptorTest { assertEquals("stringListProp", listDescriptor.name()); assertEquals("hello", listDescriptor.description()); assertEquals(Arrays.asList("v1", "v2"), listDescriptor.defaultValue()); - assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("foo,bar")); - assertEquals(Arrays.asList("foo | bar"), listDescriptor.valueFrom(" foo | bar ")); + assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("foo|bar")); + assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom(" foo | bar ")); } private enum SampleEnum { A, B, C } @@ -270,7 +268,7 @@ class PropertyDescriptorTest { assertEquals("enumListProp", listDescriptor.name()); assertEquals("hello", listDescriptor.description()); assertEquals(Arrays.asList(SampleEnum.A, SampleEnum.B), listDescriptor.defaultValue()); - assertEquals(Arrays.asList(SampleEnum.B, SampleEnum.C), listDescriptor.valueFrom("TEST_B,TEST_C")); + assertEquals(Arrays.asList(SampleEnum.B, SampleEnum.C), listDescriptor.valueFrom("TEST_B|TEST_C")); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java index 64f8ed897f..89ef1588c8 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java @@ -48,7 +48,7 @@ class CodeClimateRendererTest extends AbstractRendererTest { + "violationSuppressRegex | | Suppress violations with messages matching a regular expression\\n" + "violationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\\n" + "stringProperty | the string value\\nsecond line with 'quotes' | simple string property\\n" - + "multiString | default1,default2 | multi string property\\n" + + "multiString | default1|default2 | multi string property\\n" // todo doesn't the delimiter need escaping? + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index b351be0afa..c102432ec3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -67,13 +67,14 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { .desc("LogLevels to guard") .defaultValues("trace", "debug", "info", "warn", "error", "log", "finest", "finer", "fine", "info", "warning", "severe") + .delim(',') .build(); private static final PropertyDescriptor> GUARD_METHODS = stringListProperty("guardsMethods") .desc("Method use to guard the log statement") .defaultValues("isTraceEnabled", "isDebugEnabled", "isInfoEnabled", "isWarnEnabled", "isErrorEnabled", "isLoggable") - .build(); + .delim(',').build(); private final Map guardStmtByLogLevel = new HashMap<>(12); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java index 8583300a8b..9f02f85e4a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java @@ -46,6 +46,7 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRulechainRule { + "Components of this list should not be surrounded by double quotes.") .map(Collectors.toSet()) .defaultValue(Collections.emptySet()) + .delim(',') .build(); private Map> literals = new HashMap<>(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java index 9de5dceca8..c67cde6a40 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java @@ -81,13 +81,13 @@ public class CloseResourceRule extends AbstractJavaRule { stringListProperty("closeTargets") .desc("Methods which may close this resource") .emptyDefaultValue() - .build(); + .delim(',').build(); private static final PropertyDescriptor> TYPES_DESCRIPTOR = stringListProperty("types") .desc("Affected types") .defaultValues("java.lang.AutoCloseable", "java.sql.Connection", "java.sql.Statement", "java.sql.ResultSet") - .build(); + .delim(',').build(); private static final PropertyDescriptor USE_CLOSE_AS_DEFAULT_TARGET = booleanProperty("closeAsDefaultTarget") diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml index c60ffeca93..a589c70dfb 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml @@ -136,7 +136,7 @@ public class Complicated { Full example - considerAssert + ignoreBooleanPaths - ignoreBooleanPaths,considerAssert + ignoreBooleanPaths|considerAssert 8 'Complicated#exception()' has value 4. diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml index 361b575064..39afc069c1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml @@ -150,7 +150,7 @@ public class Foo { Comprehensive, check for IPv6 and IPv4 mapped IPv6 - IPv6,IPv4 mapped IPv6 + IPv6|IPv4 mapped IPv6 15 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml index 7b38a0e0cc..1ee74d2ba5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml @@ -207,7 +207,7 @@ public class Foo implements Serializable { More exclusions can be configured - m$mangled,serialVersionUID + m$mangled|serialVersionUID 0 Includes bad words - idiot,jerk + idiot|jerk 2 violations: break:for/do/while - for,do,while + for|do|while 4 @@ -149,7 +149,7 @@ public class Foo { violations: continue:for/do/while - for,do,while + for|do|while 4 11,20,29,38 @@ -160,7 +160,7 @@ public class Foo { violations: return:for/do/while - for,do,while + for|do|while 4 5,14,23,32 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml index 6b975dc823..c750a3afd9 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AvoidInstantiatingObjectsInLoops.xml @@ -179,7 +179,7 @@ public class PMDDemo { 1 14 getFilteredMessages() { + private SortedSet getFilteredMessages(List messages) { final SortedSet result = new TreeSet<>(messages); for (Message element : messages) { Message event = new Message(element); for (Function filter : filters) { - if (!filter.accept(event)) { + if (!filter.apply(event)) { result.remove(element); break; } diff --git a/pmd-xml/src/main/resources/category/pom/errorprone.xml b/pmd-xml/src/main/resources/category/pom/errorprone.xml index 816e8c72a9..cdbb924d27 100644 --- a/pmd-xml/src/main/resources/category/pom/errorprone.xml +++ b/pmd-xml/src/main/resources/category/pom/errorprone.xml @@ -30,9 +30,8 @@ The following types are considered valid: pom, jar, maven-plugin, ejb, war, ear, ]]> - From a73af66aae848a477293aadcbaebd5873b21a99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jul 2022 14:59:47 +0200 Subject: [PATCH 077/347] throw ConstraintViolatedException --- .../net/sourceforge/pmd/properties/PropertyDescriptor.java | 3 ++- .../properties/constraints/ConstraintViolatedException.java | 4 ++++ .../sourceforge/pmd/properties/xml/ConstraintDecorator.java | 3 ++- .../net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 5a1429c55b..191067b075 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -10,6 +10,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.RuleSetWriter; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; import net.sourceforge.pmd.properties.xml.XmlMapper; import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; @@ -60,7 +61,7 @@ public final class PropertyDescriptor { XmlSyntaxUtils.checkConstraintsThrow( defaultValue, parser.getConstraints(), - s -> new IllegalArgumentException("Constraint violated " + s) + ConstraintViolatedException::new ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java index 40a94b59ce..635bd49888 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java @@ -15,4 +15,8 @@ public class ConstraintViolatedException extends IllegalArgumentException { public ConstraintViolatedException(String message) { super(message); } + + public ConstraintViolatedException(Throwable cause) { + super(cause); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java index d434445648..0e6b23c8fc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java @@ -10,6 +10,7 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; +import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; @@ -45,7 +46,7 @@ class ConstraintDecorator extends XmlMapper { XmlSyntaxUtils.checkConstraintsThrow( t, constraints, - s -> err.at(element).error(XmlErrorMessages.ERR__CONSTRAINT_NOT_SATISFIED, s) + s -> new ConstraintViolatedException(err.at(element).error(XmlErrorMessages.ERR__CONSTRAINT_NOT_SATISFIED, s)) ); return t; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java index d889d9081d..024385a9c2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java @@ -20,6 +20,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.internal.xml.XmlUtil; @@ -101,7 +102,7 @@ public final class XmlSyntaxUtils { public static void checkConstraintsThrow(T t, List> constraints, - Function exceptionMaker) { + Function exceptionMaker) { String failures = checkConstraintsJoin(t, constraints); if (failures != null) { From 093d5d36fb4b9ab0011512f166eecbf1ee5a8720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jul 2022 15:00:46 +0200 Subject: [PATCH 078/347] Flatten properties package --- .../lang/apex/rule/design/AvoidDeeplyNestedIfStmtsRule.java | 2 +- .../pmd/lang/apex/rule/design/CognitiveComplexityRule.java | 2 +- .../pmd/lang/apex/rule/design/CyclomaticComplexityRule.java | 2 +- .../lang/apex/rule/design/StdCyclomaticComplexityRule.java | 2 +- .../pmd/lang/apex/rule/design/TooManyFieldsRule.java | 2 +- .../lang/apex/rule/internal/AbstractCounterCheckRule.java | 2 +- .../src/main/java/net/sourceforge/pmd/RuleSetWriter.java | 2 +- .../pmd/properties/{xml => }/ConstraintDecorator.java | 4 +--- .../{constraints => }/ConstraintViolatedException.java | 2 +- .../net/sourceforge/pmd/properties/{xml => }/MapperSet.java | 3 +-- .../properties/{constraints => }/NumericConstraints.java | 4 ++-- .../pmd/properties/{xml => }/OptionalSyntax.java | 3 +-- .../net/sourceforge/pmd/properties/PropertyBuilder.java | 5 +---- .../properties/{constraints => }/PropertyConstraint.java | 5 ++--- .../net/sourceforge/pmd/properties/PropertyDescriptor.java | 3 --- .../net/sourceforge/pmd/properties/PropertyFactory.java | 6 +----- .../java/net/sourceforge/pmd/properties/PropertyTypeId.java | 3 --- .../net/sourceforge/pmd/properties/{xml => }/SeqSyntax.java | 5 ++--- .../sourceforge/pmd/properties/{xml => }/ValueSyntax.java | 6 ++---- .../net/sourceforge/pmd/properties/{xml => }/XmlMapper.java | 4 +--- .../pmd/properties/{xml => }/XmlSyntaxUtils.java | 5 +---- .../main/java/net/sourceforge/pmd/rules/RuleFactory.java | 2 +- .../java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java | 2 +- .../src/test/java/net/sourceforge/pmd/AbstractRuleTest.java | 2 +- .../test/java/net/sourceforge/pmd/lang/rule/MockRule.java | 2 +- .../{constraints => }/NumericConstraintsTest.java | 4 ++-- .../sourceforge/pmd/properties/PropertyDescriptorTest.java | 4 +--- .../bestpractices/JUnitTestContainsTooManyAssertsRule.java | 2 +- .../lang/java/rule/design/AvoidDeeplyNestedIfStmtsRule.java | 2 +- .../pmd/lang/java/rule/design/CognitiveComplexityRule.java | 2 +- .../lang/java/rule/design/CouplingBetweenObjectsRule.java | 2 +- .../pmd/lang/java/rule/design/CyclomaticComplexityRule.java | 2 +- .../pmd/lang/java/rule/design/LawOfDemeterRule.java | 2 +- .../pmd/lang/java/rule/design/NPathComplexityRule.java | 2 +- .../pmd/lang/java/rule/design/NcssCountRule.java | 2 +- .../pmd/lang/java/rule/design/SwitchDensityRule.java | 2 +- .../pmd/lang/java/rule/documentation/CommentSizeRule.java | 2 +- .../java/rule/errorprone/AvoidDuplicateLiteralsRule.java | 2 +- .../java/rule/internal/AbstractJavaCounterCheckRule.java | 2 +- .../rule/performance/ConsecutiveLiteralAppendsRule.java | 2 +- .../pmd/lang/plsql/rule/codestyle/CodeFormatRule.java | 2 +- .../pmd/lang/plsql/rule/codestyle/LineLengthRule.java | 2 +- .../lang/plsql/rule/design/AbstractCounterCheckRule.java | 2 +- .../lang/plsql/rule/design/CyclomaticComplexityRule.java | 2 +- .../pmd/lang/plsql/rule/design/TooManyFieldsRule.java | 2 +- .../lang/vm/rule/design/AvoidDeeplyNestedIfStmtsRule.java | 2 +- .../lang/vm/rule/design/ExcessiveTemplateLengthRule.java | 2 +- 47 files changed, 50 insertions(+), 78 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{xml => }/ConstraintDecorator.java (93%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{constraints => }/ConstraintViolatedException.java (90%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{xml => }/MapperSet.java (97%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{constraints => }/NumericConstraints.java (96%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{xml => }/OptionalSyntax.java (96%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{constraints => }/PropertyConstraint.java (97%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{xml => }/SeqSyntax.java (93%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{xml => }/ValueSyntax.java (94%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{xml => }/XmlMapper.java (96%) rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{xml => }/XmlSyntaxUtils.java (97%) rename pmd-core/src/test/java/net/sourceforge/pmd/properties/{constraints => }/NumericConstraintsTest.java (97%) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/AvoidDeeplyNestedIfStmtsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/AvoidDeeplyNestedIfStmtsRule.java index e91ce1e745..ab3ebfb189 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/AvoidDeeplyNestedIfStmtsRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/AvoidDeeplyNestedIfStmtsRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.apex.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import net.sourceforge.pmd.lang.apex.ast.ASTIfBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CognitiveComplexityRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CognitiveComplexityRule.java index a310fb4527..c1b970d1af 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CognitiveComplexityRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CognitiveComplexityRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.apex.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.ArrayDeque; import java.util.Deque; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CyclomaticComplexityRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CyclomaticComplexityRule.java index fcf21afac5..9247aeef71 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CyclomaticComplexityRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/CyclomaticComplexityRule.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.lang.apex.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.ArrayDeque; import java.util.Deque; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/StdCyclomaticComplexityRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/StdCyclomaticComplexityRule.java index 8caf04fbc1..f0c0152ed0 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/StdCyclomaticComplexityRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/StdCyclomaticComplexityRule.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.lang.apex.rule.design; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange; +import static net.sourceforge.pmd.properties.NumericConstraints.inRange; import java.util.ArrayDeque; import java.util.Deque; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/TooManyFieldsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/TooManyFieldsRule.java index 3d85cf2eb3..cbaf66fac0 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/TooManyFieldsRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/design/TooManyFieldsRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.apex.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.List; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/AbstractCounterCheckRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/AbstractCounterCheckRule.java index 8e4ff77025..9c5a077360 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/AbstractCounterCheckRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/internal/AbstractCounterCheckRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.apex.rule.internal; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import org.checkerframework.checker.nullness.qual.NonNull; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index ba5345c01d..685fdf0768 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -36,7 +36,7 @@ import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.PropertyTypeId; -import net.sourceforge.pmd.properties.xml.XmlMapper; +import net.sourceforge.pmd.properties.XmlMapper; import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.internal.xml.SchemaConstants; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java similarity index 93% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java index 0e6b23c8fc..b7a9a5304d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ConstraintDecorator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties; import java.util.List; import java.util.Set; @@ -10,8 +10,6 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintViolatedException.java similarity index 90% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintViolatedException.java index 635bd49888..0c7f759e5f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/ConstraintViolatedException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintViolatedException.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.constraints; +package net.sourceforge.pmd.properties; /** * Thrown when a property constraint is violated. Detected while parsing diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/MapperSet.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/MapperSet.java index 7ef9709ac5..28bcfc1d43 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/MapperSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/MapperSet.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties; import java.util.Collection; import java.util.LinkedHashMap; @@ -15,7 +15,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/NumericConstraints.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericConstraints.java similarity index 96% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/NumericConstraints.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericConstraints.java index cdfc86199e..015d2c4396 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/NumericConstraints.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericConstraints.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.constraints; +package net.sourceforge.pmd.properties; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/OptionalSyntax.java similarity index 96% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/OptionalSyntax.java index 467b5c55b8..2a8c3dee9b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/OptionalSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/OptionalSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties; import java.util.HashSet; import java.util.List; @@ -13,7 +13,6 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 2407584c61..0ca35aa562 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -20,9 +20,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.XmlMapper; -import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; // @formatter:off /** @@ -157,7 +154,7 @@ public abstract class PropertyBuilder, T> { * * @return The same builder * - * @see net.sourceforge.pmd.properties.constraints.NumericConstraints + * @see NumericConstraints */ @SuppressWarnings("unchecked") public abstract B require(PropertyConstraint constraint); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyConstraint.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyConstraint.java index 13a8ae59ce..0e547f6afe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/constraints/PropertyConstraint.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyConstraint.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.constraints; +package net.sourceforge.pmd.properties; import java.util.ArrayList; import java.util.List; @@ -13,7 +13,6 @@ import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.Experimental; -import net.sourceforge.pmd.properties.PropertyBuilder; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 191067b075..6ad4ff7723 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -10,9 +10,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.RuleSetWriter; import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; -import net.sourceforge.pmd.properties.xml.XmlMapper; -import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 03406b55c4..e02c8e2dee 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.properties; import static java.util.Arrays.asList; -import static net.sourceforge.pmd.properties.xml.XmlSyntaxUtils.enumerationParser; +import static net.sourceforge.pmd.properties.XmlSyntaxUtils.enumerationParser; import java.util.Arrays; import java.util.List; @@ -20,10 +20,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.GenericPropertyBuilder; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; -import net.sourceforge.pmd.properties.constraints.NumericConstraints; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.XmlMapper; -import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; import net.sourceforge.pmd.util.CollectionUtil; //@formatter:off diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index db0cd9f39e..a30a862014 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -9,9 +9,6 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import net.sourceforge.pmd.properties.xml.XmlMapper; -import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; - /** * Enumerates the properties that can be built from the XML. Defining a property in diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java similarity index 93% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java index ac71b54771..4704e82b9e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/SeqSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties; import java.util.Collections; import java.util.List; @@ -12,8 +12,7 @@ import java.util.stream.Collectors; import org.w3c.dom.Element; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; +import net.sourceforge.pmd.properties.XmlMapper.StableXmlMapper; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; import net.sourceforge.pmd.util.internal.xml.XmlUtil; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java similarity index 94% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java index d152a44320..129c95e5fc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.util.CollectionUtil.listOf; @@ -15,9 +15,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.PredicateUtil; -import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.XmlMapper.StableXmlMapper; +import net.sourceforge.pmd.properties.XmlMapper.StableXmlMapper; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java similarity index 96% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java index 7515860e77..c6c35c20a4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlMapper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties; import static net.sourceforge.pmd.util.CollectionUtil.setOf; @@ -13,8 +13,6 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.w3c.dom.Element; -import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; import net.sourceforge.pmd.util.log.MessageReporter; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java index 024385a9c2..8b26da9d59 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/xml/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.xml; +package net.sourceforge.pmd.properties; import java.util.ArrayList; @@ -19,9 +19,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; -import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.properties.constraints.ConstraintViolatedException; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; import net.sourceforge.pmd.util.internal.xml.XmlUtil; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index aaa4142cd9..b412d73bc8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -38,7 +38,7 @@ import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyB import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper; -import net.sourceforge.pmd.properties.xml.XmlMapper; +import net.sourceforge.pmd.properties.XmlMapper; import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.StringUtil; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java index 107b07bcb2..a8596d8588 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java @@ -19,7 +19,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.w3c.dom.Element; import org.w3c.dom.Node; -import net.sourceforge.pmd.properties.xml.XmlMapper; +import net.sourceforge.pmd.properties.XmlMapper; import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.StringUtil; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java index acb88be6ef..0cfa1e572b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange; +import static net.sourceforge.pmd.properties.NumericConstraints.inRange; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java index 9ce811406c..d55e5f2294 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.rule; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange; +import static net.sourceforge.pmd.properties.NumericConstraints.inRange; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/NumericConstraintsTest.java similarity index 97% rename from pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java rename to pmd-core/src/test/java/net/sourceforge/pmd/properties/NumericConstraintsTest.java index 070c3f8711..4c848e537e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/constraints/NumericConstraintsTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/NumericConstraintsTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.properties.constraints; +package net.sourceforge.pmd.properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index c73eb0fe47..f6ec9631af 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.properties; import static java.util.Collections.emptyList; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange; +import static net.sourceforge.pmd.properties.NumericConstraints.inRange; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; @@ -34,8 +34,6 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.FooRule; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.properties.constraints.PropertyConstraint; -import net.sourceforge.pmd.properties.xml.XmlSyntaxUtils; /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java index 33d578d98a..104db6415c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java @@ -11,7 +11,7 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.properties.constraints.NumericConstraints; +import net.sourceforge.pmd.properties.NumericConstraints; public class JUnitTestContainsTooManyAssertsRule extends AbstractJavaRulechainRule { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidDeeplyNestedIfStmtsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidDeeplyNestedIfStmtsRule.java index 0bc350780a..d5d2173884 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidDeeplyNestedIfStmtsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidDeeplyNestedIfStmtsRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CognitiveComplexityRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CognitiveComplexityRule.java index b15190d0be..fdefa560e6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CognitiveComplexityRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CognitiveComplexityRule.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.lang.java.rule.design; import static net.sourceforge.pmd.lang.java.metrics.JavaMetrics.COGNITIVE_COMPLEXITY; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java index 3ce63e495c..8bd9c128c6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CouplingBetweenObjectsRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.HashSet; import java.util.Set; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java index d9aae30eee..4b218d528e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.HashMap; import java.util.List; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LawOfDemeterRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LawOfDemeterRule.java index d6aa57e247..c7e811ab70 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LawOfDemeterRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LawOfDemeterRule.java @@ -12,7 +12,7 @@ import static net.sourceforge.pmd.lang.java.ast.internal.JavaAstUtils.isRefToFie import static net.sourceforge.pmd.lang.java.ast.internal.JavaAstUtils.isThisOrSuper; import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.isGetterCall; import static net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil.isNullChecked; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.LinkedHashMap; import java.util.Map; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java index 7fa535e437..565c393e75 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NPathComplexityRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.math.BigInteger; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java index 93d9c09b49..cc0304c21e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.HashMap; import java.util.List; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java index 21f8d2bcb1..aa481261a9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import net.sourceforge.pmd.lang.java.ast.ASTStatement; import net.sourceforge.pmd.lang.java.ast.ASTSwitchBranch; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentSizeRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentSizeRule.java index 6edff2a963..98954cb47e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentSizeRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentSizeRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.documentation; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import static net.sourceforge.pmd.util.CollectionUtil.setOf; import java.util.ArrayList; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java index 9f02f85e4a..910493cba8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; import static net.sourceforge.pmd.properties.PropertyFactory.intProperty; import static net.sourceforge.pmd.properties.PropertyFactory.stringProperty; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.Collections; import java.util.HashMap; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AbstractJavaCounterCheckRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AbstractJavaCounterCheckRule.java index ebdce6bd07..fac683f58c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AbstractJavaCounterCheckRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AbstractJavaCounterCheckRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.internal; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.metrics.JavaMetrics; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/ConsecutiveLiteralAppendsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/ConsecutiveLiteralAppendsRule.java index 479f2d91b5..c727b87b29 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/ConsecutiveLiteralAppendsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/ConsecutiveLiteralAppendsRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange; +import static net.sourceforge.pmd.properties.NumericConstraints.inRange; import java.util.HashSet; import java.util.Set; diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeFormatRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeFormatRule.java index b1176d7759..69b3a06823 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeFormatRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/CodeFormatRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.plsql.rule.codestyle; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.inRange; +import static net.sourceforge.pmd.properties.NumericConstraints.inRange; import java.util.List; diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/LineLengthRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/LineLengthRule.java index b5b6120257..ef721af33b 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/LineLengthRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codestyle/LineLengthRule.java @@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.plsql.ast.ASTInput; import net.sourceforge.pmd.lang.plsql.rule.AbstractPLSQLRule; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; -import net.sourceforge.pmd.properties.constraints.NumericConstraints; +import net.sourceforge.pmd.properties.NumericConstraints; public class LineLengthRule extends AbstractPLSQLRule { diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/AbstractCounterCheckRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/AbstractCounterCheckRule.java index 2309f3ad62..08ebfe3afd 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/AbstractCounterCheckRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/AbstractCounterCheckRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.plsql.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.lang.reflect.Modifier; diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java index fc8db839cf..eff82905fd 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/CyclomaticComplexityRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.plsql.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.ArrayDeque; import java.util.Deque; diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/TooManyFieldsRule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/TooManyFieldsRule.java index 30f7f1b0a9..2d336ea34f 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/TooManyFieldsRule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/design/TooManyFieldsRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.plsql.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import java.util.HashMap; import java.util.List; diff --git a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/AvoidDeeplyNestedIfStmtsRule.java b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/AvoidDeeplyNestedIfStmtsRule.java index c3377e4f48..b81045d639 100644 --- a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/AvoidDeeplyNestedIfStmtsRule.java +++ b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/AvoidDeeplyNestedIfStmtsRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.vm.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import net.sourceforge.pmd.lang.vm.ast.ASTElseIfStatement; import net.sourceforge.pmd.lang.vm.ast.ASTIfStatement; diff --git a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/ExcessiveTemplateLengthRule.java b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/ExcessiveTemplateLengthRule.java index a4998515db..1a2b59f9e1 100644 --- a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/ExcessiveTemplateLengthRule.java +++ b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/design/ExcessiveTemplateLengthRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.vm.rule.design; -import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive; +import static net.sourceforge.pmd.properties.NumericConstraints.positive; import net.sourceforge.pmd.lang.rule.internal.CommonPropertyDescriptors; import net.sourceforge.pmd.lang.vm.ast.ASTTemplate; From e4e76880b982a97476856aaabf2541a0828d6cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jul 2022 20:36:09 +0200 Subject: [PATCH 079/347] add testes --- .../pmd/properties/PropertyBuilder.java | 2 +- .../sourceforge/pmd/properties/SeqSyntax.java | 44 ++++--- .../pmd/RulesetFactoryTestBase.java | 6 +- .../pmd/properties/PropertySyntaxTest.java | 117 ++++++++++++++++++ 4 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 0ca35aa562..8603ad698c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -254,7 +254,7 @@ public abstract class PropertyBuilder, T> { * converted to collection validators. The default value cannot * have previously been set. The returned builder will support * conversion to and from a delimited string if this property does. - * Otherwise it will only support the {@code } syntax. + * Otherwise, it will only support the {@code } syntax. * *

Example usage: *

{@code
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java
index 4704e82b9e..5845bf6279 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java
@@ -6,17 +6,18 @@ package net.sourceforge.pmd.properties;
 
 import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
 import org.w3c.dom.Element;
 
 import net.sourceforge.pmd.properties.XmlMapper.StableXmlMapper;
+import net.sourceforge.pmd.util.CollectionUtil;
 import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter;
-import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages;
 import net.sourceforge.pmd.util.internal.xml.XmlUtil;
 
+import com.github.oowekyala.ooxml.DomUtils;
+
 /**
  * Serialize to and from a simple string. Examples:
  *
@@ -46,25 +47,30 @@ final class SeqSyntax> extends StableXmlMapper {
 
     @Override
     public C fromXml(Element element, PmdXmlReporter err) {
-        RuntimeException aggregateEx = err.at(element).error(XmlErrorMessages.ERR__LIST_CONSTRAINT_NOT_SATISFIED);
+        return collectFromXml(element, err, collector);
+    }
 
-        C result = XmlUtil.getElementChildren(element)
-                          .map(child -> {
-                              try {
-                                  return XmlUtil.expectElement(err, child, itemSyntax);
-                              } catch (Exception e) {
-                                  aggregateEx.addSuppressed(e);
-                                  return null;
-                              }
-                          })
-                          .filter(Objects::nonNull)
-                          .collect(collector);
-
-        if (aggregateEx.getSuppressed().length > 0) {
-            throw aggregateEx;
-        } else {
-            return result;
+    // capture the A type var.
+    private  C collectFromXml(Element element, PmdXmlReporter err, Collector collector) {
+        RuntimeException error = null;
+        A acc = collector.supplier().get();
+        for (Element child : DomUtils.children(element)) {
+            try {
+                T elt = XmlUtil.expectElement(err, child, itemSyntax);
+                collector.accumulator().accept(acc, elt);
+            } catch (RuntimeException e) {
+                if (error == null) {
+                    error = err.at(child).error(e);
+                } else {
+                    error.addSuppressed(e);
+                }
+            }
         }
+
+        if (error != null) {
+            throw error;
+        }
+        return CollectionUtil.finish(collector, acc);
     }
 
     @Override
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java
index bad9754ed2..5167df3e10 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java
@@ -227,17 +227,17 @@ public class RulesetFactoryTestBase {
         ));
     }
 
-    private static @NonNull String tag(String tagName, String... body) {
+    protected static @NonNull String tag(String tagName, String... body) {
         return "<" + tagName + ">\n"
             + body(body)
             + "";
     }
 
-    private static @NonNull String emptyTag(String tagName, Map attrs) {
+    protected static @NonNull String emptyTag(String tagName, Map attrs) {
         return "<" + tagName + " " + attrs(attrs) + " />";
     }
 
-    private static @NonNull String tagOneLine(String tagName, String text) {
+    protected static @NonNull String tagOneLine(String tagName, String text) {
         return "<" + tagName + ">" + text + "";
     }
 }
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
new file mode 100644
index 0000000000..326c4bdde3
--- /dev/null
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
@@ -0,0 +1,117 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.properties;
+
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+import net.sourceforge.pmd.RulesetFactoryTestBase;
+import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter;
+
+import com.github.oowekyala.ooxml.messages.OoxmlFacade;
+
+/**
+ * @author Clรฉment Fournier
+ */
+public class PropertySyntaxTest extends RulesetFactoryTestBase {
+
+    protected static @NonNull String property(String name, String... contents) {
+        return "> mapper = PropertyFactory.stringProperty("eude")
+                                                        .desc("eu")
+                                                        .toList()
+                                                        .emptyDefaultValue()
+                                                        .delim('-')
+                                                        .build()
+                                                        .xmlMapper();
+        assertParsesAs(mapper,
+                       tag("seq",
+                           valueTag("ad"),
+                           valueTag("u")
+                       ),
+                       listOf("ad", "u"));
+
+    }
+
+    private  void assertParsesAs(XmlMapper mapper, String xmlCode, T expected) {
+        Element xml = parseXml(xmlCode);
+        T parsed = mapper.fromXml(xml, mock(PmdXmlReporter.class));
+        assertEquals(expected, parsed);
+    }
+
+}

From 19da46d5ceed85887928741f41cc8fd8a2bfe48c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Wed, 27 Jul 2022 18:01:51 +0200
Subject: [PATCH 080/347] remove regex property list

---
 .../pmd/properties/PropertyBuilder.java           | 12 +-----------
 .../pmd/properties/PropertyFactory.java           | 15 ---------------
 2 files changed, 1 insertion(+), 26 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index 8603ad698c..4dc17c1140 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -415,7 +415,6 @@ public abstract class PropertyBuilder, T> {
         private final Collector collector;
         private final List> collectionConstraints = new ArrayList<>();
         private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER;
-        private boolean allowsStringSyntaxIfPossible = true;
 
 
         /**
@@ -474,15 +473,6 @@ public abstract class PropertyBuilder, T> {
             return multiValueDelimiter;
         }
 
-        /**
-         * Specify that this property may not be parsed from a string.
-         * This is the case for lists of patterns, for instance.
-         */
-        GenericCollectionPropertyBuilder onlyAllowSeqSyntax() {
-            this.allowsStringSyntaxIfPossible = false;
-            return this;
-        }
-
 
         /**
          * Specify default values. To specify an empty
@@ -525,7 +515,7 @@ public abstract class PropertyBuilder, T> {
 
         @Override
         public PropertyDescriptor build() {
-            XmlMapper syntax = itemParser.supportsStringMapping() && allowsStringSyntaxIfPossible
+            XmlMapper syntax = itemParser.supportsStringMapping()
                                   ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, multiValueDelimiter)
                                   : XmlSyntaxUtils.onlySeq(itemParser, collector);
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
index e02c8e2dee..3b3a744111 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
@@ -12,7 +12,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.function.Function;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import org.checkerframework.checker.nullness.qual.NonNull;
@@ -216,20 +215,6 @@ public final class PropertyFactory {
         return new RegexPropertyBuilder(name);
     }
 
-    /**
-     * Returns a builder for a property having as value a list of regex patterns.
-     * The format of the individual items is the same as for {@linkplain #regexProperty(String) regexProperty}.
-     * This property may only be written with the structured {@code } syntax
-     * in an XML ruleset.
-     *
-     * @param name Name of the property to build
-     *
-     * @return A new builder
-     */
-    public static GenericCollectionPropertyBuilder> regexListProperty(String name) {
-        return regexProperty(name).toList().onlyAllowSeqSyntax();
-    }
-
 
     /**
      * Returns a builder for a string property. The property descriptor

From 561732b539d1ce790209884438bda4c488a99be5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Wed, 27 Jul 2022 19:11:40 +0200
Subject: [PATCH 081/347] [doc] update doc tests

---
 .../sourceforge/pmd/properties/XmlMapper.java |  44 ++-
 .../pmd/properties/XmlSyntaxUtils.java        |   2 +-
 .../net/sourceforge/pmd/util/StringUtil.java  |  19 ++
 .../pmd/properties/PropertySyntaxTest.java    |  18 ++
 .../pmd/docs/RuleDocGenerator.java            |  42 ++-
 pmd-doc/src/test/resources/expected/sample.md | 280 ++++++++++++------
 6 files changed, 297 insertions(+), 108 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java
index c6c35c20a4..2789aec057 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java
@@ -6,13 +6,26 @@ package net.sourceforge.pmd.properties;
 
 import static net.sourceforge.pmd.util.CollectionUtil.setOf;
 
+import java.io.ByteArrayOutputStream;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
 
 import org.checkerframework.checker.nullness.qual.NonNull;
+import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import net.sourceforge.pmd.lang.document.Chars;
+import net.sourceforge.pmd.util.StringUtil;
 import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter;
 import net.sourceforge.pmd.util.log.MessageReporter;
 
@@ -86,8 +99,7 @@ public abstract class XmlMapper {
      * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()}
      * @throws IllegalArgumentException      if something goes wrong (but should be reported on the error reporter)
      */
-    @NonNull
-    public String toString(T value) {
+    public @NonNull String toString(T value) {
         throw new UnsupportedOperationException("Check #supportsStringMapping()");
     }
 
@@ -116,6 +128,34 @@ public abstract class XmlMapper {
      */
     protected abstract List examplesImpl(String curIndent, String baseIndent);
 
+    public String xmlToString(T value) {
+        try {
+            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+            documentBuilderFactory.setNamespaceAware(true);
+            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+            Document document = documentBuilder.newDocument();
+            Element container = document.createElement(getWriteElementName(value));
+            toXml(container, value);
+            document.appendChild(container);
+
+            TransformerFactory transformerFactory = TransformerFactory.newInstance();
+            Transformer transformer = transformerFactory.newTransformer();
+            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+            // This is as close to pretty printing as we'll get using standard
+            // Java APIs.
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+            ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
+            transformer.transform(new DOMSource(document), new StreamResult(resultBuffer));
+            String result = resultBuffer.toString();
+            result = result.replace("", "");
+            result = StringUtil.trimIndent(Chars.wrap(result)).toString();
+            return result.trim();
+        } catch (ParserConfigurationException | TransformerException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Override
     public String toString() {
         return getExamples().get(0);
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java
index 8b26da9d59..a42049986c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java
@@ -57,7 +57,7 @@ public final class XmlSyntaxUtils {
 
 
     private static  XmlMapper> numberList(ValueSyntax valueSyntax) {
-        return seqAndDelimited(valueSyntax, Collectors.toList(), true, PropertyFactory.DEFAULT_DELIMITER);
+        return seqAndDelimited(valueSyntax, Collectors.toList(), true, PropertyFactory.DEFAULT_NUMERIC_DELIMITER);
     }
 
     private static  XmlMapper> otherList(ValueSyntax valueSyntax) {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java
index 4b7d25d4fa..f97fddd78d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java
@@ -367,6 +367,25 @@ public final class StringUtil {
     }
 
 
+    /**
+     * Replace common indentation in the lines of the given string.
+     */
+    public static StringBuilder replaceIndent(Chars string, String newIndent) {
+        List lines = string.lineStream().collect(CollectionUtil.toMutableList());
+        trimIndentInPlace(lines);
+        // this is joinCharsIntoStringBuilder inlined and with the appender modified
+        return CollectionUtil.joinOn(
+            new StringBuilder(),
+            lines,
+            (buf, line) -> {
+                buf.append(newIndent); // here
+                line.appendChars(buf);
+            },
+            "\n"
+        );
+    }
+
+
     private static int countLeadingWhitespace(CharSequence s) {
         int count = 0;
         while (count < s.length() && Character.isWhitespace(s.charAt(count))) {
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
index 326c4bdde3..c0a3215802 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
@@ -108,6 +108,24 @@ public class PropertySyntaxTest extends RulesetFactoryTestBase {
 
     }
 
+    @Test
+    void testXmlToString() {
+        // given an xml elt and a pdescriptor
+        // ensure fromXml runs properly
+        XmlMapper> mapper = PropertyFactory.stringProperty("eude")
+                                                        .desc("eu")
+                                                        .toList()
+                                                        .emptyDefaultValue()
+                                                        .delim(',')
+                                                        .build()
+                                                        .xmlMapper();
+        assertEquals(
+            "ad,u",
+            mapper.xmlToString(listOf("ad", "u"))
+        );
+
+    }
+
     private  void assertParsesAs(XmlMapper mapper, String xmlCode, T expected) {
         Element xml = parseXml(xmlCode);
         T parsed = mapper.fromXml(xml, mock(PmdXmlReporter.class));
diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
index 5255253685..4cfa162c09 100644
--- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
+++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
@@ -25,7 +25,6 @@ import java.util.Objects;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
@@ -38,10 +37,12 @@ import net.sourceforge.pmd.RuleSet;
 import net.sourceforge.pmd.RuleSetLoadException;
 import net.sourceforge.pmd.RuleSetLoader;
 import net.sourceforge.pmd.lang.Language;
+import net.sourceforge.pmd.lang.document.Chars;
 import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.lang.rule.XPathRule;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.util.IOUtil;
+import net.sourceforge.pmd.util.StringUtil;
 
 public class RuleDocGenerator {
     private static final Logger LOG = LoggerFactory.getLogger(RuleDocGenerator.class);
@@ -55,7 +56,7 @@ public class RuleDocGenerator {
     private static final String RULESET_INDEX_PERMALINK_PATTERN = "pmd_rules_${language.tersename}_${ruleset.name}.html";
 
     private static final String DEPRECATION_LABEL_SMALL = "Deprecated ";
-    private static final String DEPRECATION_LABEL = "Deprecated ";
+    private static final String DEPRECATION_LABEL = "Deprecated";
     private static final String DEPRECATED_RULE_PROPERTY_MARKER = "deprecated!";
 
     private static final String GITHUB_SOURCE_LINK = "https://github.com/pmd/pmd/blob/master/";
@@ -440,12 +441,10 @@ public class RuleDocGenerator {
 
                             String defaultValue = determineDefaultValueAsString(propertyDescriptor, rule, true);
 
-                            // TODO document property syntax
-
                             lines.add("|"
                                     + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(propertyDescriptor.name()))
                                     + "|"
-                                    + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(defaultValue))
+                                    + EscapeUtils.escapeMarkdown(defaultValue)
                                     + "|"
                                     + EscapeUtils.escapeMarkdown((isDeprecated ? DEPRECATION_LABEL_SMALL : "") + StringEscapeUtils.escapeHtml4(description))
                                     + "|"
@@ -471,9 +470,12 @@ public class RuleDocGenerator {
                         lines.add("    ");
                         for (PropertyDescriptor propertyDescriptor : properties) {
                             if (!isDeprecated(propertyDescriptor)) {
-                                String defaultValue = determineDefaultValueAsString(propertyDescriptor, rule, false);
-                                lines.add("        ");
+                                lines.add("        ");
+                                String defaultValue = determineDefaultValueAsXml(propertyDescriptor, rule);
+
+                                defaultValue = StringUtil.replaceIndent(Chars.wrap(defaultValue), "            ").toString();
+                                Collections.addAll(lines, defaultValue.split("\\R"));
+                                lines.add("        ");
                             }
                         }
                         lines.add("    ");
@@ -508,16 +510,30 @@ public class RuleDocGenerator {
         T realDefaultValue = rule.getProperty(propertyDescriptor);
 
         if (realDefaultValue != null) {
-            defaultValue = propertyDescriptor.asDelimitedString(realDefaultValue);
-            if (pad && realDefaultValue instanceof Collection) {
-                // surround the delimiter with spaces, so that the browser can wrap
-                // the value nicely
-                defaultValue = defaultValue.replaceAll(",", " , ");
+            if (propertyDescriptor.xmlMapper().supportsStringMapping()) {
+                defaultValue = propertyDescriptor.xmlMapper().toString(realDefaultValue);
+                if (pad && realDefaultValue instanceof Collection) {
+                    // surround the delimiter with spaces, so that the browser can wrap
+                    // the value nicely
+                    defaultValue = defaultValue.replaceAll(",", " , ");
+                }
+            } else {
+                defaultValue = propertyDescriptor.xmlMapper().xmlToString(realDefaultValue);
             }
         }
+        defaultValue = StringEscapeUtils.escapeHtml4(defaultValue);
         return defaultValue;
     }
 
+    private  String determineDefaultValueAsXml(PropertyDescriptor propertyDescriptor, Rule rule) {
+        T realDefaultValue = rule.getProperty(propertyDescriptor);
+        if (realDefaultValue != null) {
+            return propertyDescriptor.xmlMapper().xmlToString(realDefaultValue);
+        } else {
+            return "";
+        }
+    }
+
     private static String stripIndentation(String description) {
         if (description == null || description.isEmpty()) {
             return "";
diff --git a/pmd-doc/src/test/resources/expected/sample.md b/pmd-doc/src/test/resources/expected/sample.md
index f41394ddf5..7e628390e4 100644
--- a/pmd-doc/src/test/resources/expected/sample.md
+++ b/pmd-doc/src/test/resources/expected/sample.md
@@ -11,7 +11,7 @@ language: Java
 
 ## DeprecatedSample
 
-Deprecated 
+Deprecated
 
 **Since:** PMD 1.0
 
@@ -93,22 +93,40 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-        
-        
-        
-        
-        
-        
-        
-        
+        
+            the value
+        
+        
+            Value1,Value2
+        
+        
+            \/\*\s+(default|package)\s+\*\/
+        
+        
+            [a-z]*
+        
+        
+            \s+
+        
+        
+            _dd_
+        
+        
+            [0-9]{1,3}
+        
+        
+            \b
+        
+        
+            \n
+        
     
 
 ```
 
 ## MovedRule
 
-Deprecated 
+Deprecated
 
 The rule has been moved to another ruleset. Use instead: [JumbledIncrementer](pmd_rules_java_sample2.html#jumbledincrementer)
 
@@ -206,7 +224,7 @@ public class Foo {
 
 ## RenamedRule1
 
-Deprecated 
+Deprecated
 
 This rule has been renamed. Use instead: [JumbledIncrementer](#jumbledincrementer)
 
@@ -264,22 +282,40 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-        
-        
-        
-        
-        
-        
-        
-        
+        
+            the value
+        
+        
+            Value1,Value2
+        
+        
+            \/\*\s+(default|package)\s+\*\/
+        
+        
+            [a-z]*
+        
+        
+            \s+
+        
+        
+            _dd_
+        
+        
+            [0-9]{1,3}
+        
+        
+            \b
+        
+        
+            \n
+        
     
 
 ```
 
 ## RenamedRule2
 
-Deprecated 
+Deprecated
 
 This rule has been renamed. Use instead: [JumbledIncrementer](#jumbledincrementer)
 
@@ -315,18 +351,18 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 
 **This rule has the following properties:**
 
-|Name|Default Value|Description|Multivalued|
-|----|-------------|-----------|-----------|
-|sampleAdditionalProperty|the value|This is a additional property for tests|no|
-|sampleMultiStringProperty|Value1 \| Value2|Test property with multiple strings|yes. Delimiter is '\|'.|
-|sampleDeprecatedProperty|test|Deprecated  This is a sample deprecated property for tests|no|
-|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|no|
-|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|no|
-|sampleRegexProperty3|\\s+|The property is of type regex|no|
-|sampleRegexProperty4|\_dd\_|The property is of type regex|no|
-|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|no|
-|sampleRegexProperty6|\\b|The property is of type regex|no|
-|sampleRegexProperty7|\\n|The property is of type regex|no|
+|Name|Default Value|Description|
+|----|-------------|-----------|
+|sampleAdditionalProperty|the value|This is a additional property for tests|
+|sampleMultiStringProperty|Value1 , Value2|Test property with multiple strings|
+|sampleDeprecatedProperty|test|Deprecated  This is a sample deprecated property for tests|
+|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|
+|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|
+|sampleRegexProperty3|\\s+|The property is of type regex|
+|sampleRegexProperty4|\_dd\_|The property is of type regex|
+|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|
+|sampleRegexProperty6|\\b|The property is of type regex|
+|sampleRegexProperty7|\\n|The property is of type regex|
 
 **Use this rule with the default properties by just referencing it:**
 ``` xml
@@ -337,26 +373,44 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-        
-        
-        
-        
-        
-        
-        
-        
+        
+            the value
+        
+        
+            Value1,Value2
+        
+        
+            \/\*\s+(default|package)\s+\*\/
+        
+        
+            [a-z]*
+        
+        
+            \s+
+        
+        
+            _dd_
+        
+        
+            [0-9]{1,3}
+        
+        
+            \b
+        
+        
+            \n
+        
     
 
 ```
 
 ## RenamedRule3
 
-Deprecated 
+Deprecated
 
 This rule has been renamed. Use instead: [JumbledIncrementer](#jumbledincrementer)
 
-Deprecated 
+Deprecated
 
 **Since:** PMD 1.0
 
@@ -390,18 +444,18 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 
 **This rule has the following properties:**
 
-|Name|Default Value|Description|Multivalued|
-|----|-------------|-----------|-----------|
-|sampleAdditionalProperty|the value|This is a additional property for tests|no|
-|sampleMultiStringProperty|Value1 \| Value2|Test property with multiple strings|yes. Delimiter is '\|'.|
-|sampleDeprecatedProperty|test|Deprecated  This is a sample deprecated property for tests|no|
-|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|no|
-|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|no|
-|sampleRegexProperty3|\\s+|The property is of type regex|no|
-|sampleRegexProperty4|\_dd\_|The property is of type regex|no|
-|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|no|
-|sampleRegexProperty6|\\b|The property is of type regex|no|
-|sampleRegexProperty7|\\n|The property is of type regex|no|
+|Name|Default Value|Description|
+|----|-------------|-----------|
+|sampleAdditionalProperty|the value|This is a additional property for tests|
+|sampleMultiStringProperty|Value1 , Value2|Test property with multiple strings|
+|sampleDeprecatedProperty|test|Deprecated  This is a sample deprecated property for tests|
+|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|
+|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|
+|sampleRegexProperty3|\\s+|The property is of type regex|
+|sampleRegexProperty4|\_dd\_|The property is of type regex|
+|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|
+|sampleRegexProperty6|\\b|The property is of type regex|
+|sampleRegexProperty7|\\n|The property is of type regex|
 
 **Use this rule with the default properties by just referencing it:**
 ``` xml
@@ -412,26 +466,44 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-        
-        
-        
-        
-        
-        
-        
-        
+        
+            the value
+        
+        
+            Value1,Value2
+        
+        
+            \/\*\s+(default|package)\s+\*\/
+        
+        
+            [a-z]*
+        
+        
+            \s+
+        
+        
+            _dd_
+        
+        
+            [0-9]{1,3}
+        
+        
+            \b
+        
+        
+            \n
+        
     
 
 ```
 
 ## RenamedRule4
 
-Deprecated 
+Deprecated
 
 This rule has been renamed. Use instead: [JumbledIncrementer](#jumbledincrementer)
 
-Deprecated 
+Deprecated
 
 **Since:** PMD 1.0
 
@@ -465,18 +537,18 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 
 **This rule has the following properties:**
 
-|Name|Default Value|Description|Multivalued|
-|----|-------------|-----------|-----------|
-|sampleAdditionalProperty|the value|This is a additional property for tests|no|
-|sampleMultiStringProperty|Value1 \| Value2|Test property with multiple strings|yes. Delimiter is '\|'.|
-|sampleDeprecatedProperty|test|Deprecated  This is a sample deprecated property for tests|no|
-|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|no|
-|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|no|
-|sampleRegexProperty3|\\s+|The property is of type regex|no|
-|sampleRegexProperty4|\_dd\_|The property is of type regex|no|
-|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|no|
-|sampleRegexProperty6|\\b|The property is of type regex|no|
-|sampleRegexProperty7|\\n|The property is of type regex|no|
+|Name|Default Value|Description|
+|----|-------------|-----------|
+|sampleAdditionalProperty|the value|This is a additional property for tests|
+|sampleMultiStringProperty|Value1 , Value2|Test property with multiple strings|
+|sampleDeprecatedProperty|test|Deprecated  This is a sample deprecated property for tests|
+|sampleRegexProperty1|\\/\\\*\\s+(default\|package)\\s+\\\*\\/|The property is of type regex|
+|sampleRegexProperty2|\[a-z\]\*|The property is of type regex|
+|sampleRegexProperty3|\\s+|The property is of type regex|
+|sampleRegexProperty4|\_dd\_|The property is of type regex|
+|sampleRegexProperty5|\[0-9\]{1,3}|The property is of type regex|
+|sampleRegexProperty6|\\b|The property is of type regex|
+|sampleRegexProperty7|\\n|The property is of type regex|
 
 **Use this rule with the default properties by just referencing it:**
 ``` xml
@@ -487,15 +559,33 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-        
-        
-        
-        
-        
-        
-        
-        
+        
+            the value
+        
+        
+            Value1,Value2
+        
+        
+            \/\*\s+(default|package)\s+\*\/
+        
+        
+            [a-z]*
+        
+        
+            \s+
+        
+        
+            _dd_
+        
+        
+            [0-9]{1,3}
+        
+        
+            \b
+        
+        
+            \n
+        
     
 
 ```
@@ -577,9 +667,15 @@ if (0 > 1 && 0 < 1) {
 ``` xml
 
     
-        
-        
-        
+        
+            \/\*\s+(default|package)\s+\*\/
+        
+        
+            <script>alert('XSS');</script>
+        
+        
+            this is escaped: |
+        
     
 
 ```

From 84cba78e645ac0c586f6a64c532efefeb0d1be7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Wed, 27 Jul 2022 21:16:00 +0200
Subject: [PATCH 082/347] wip: tear down new property syntax

---
 .../main/java/net/sourceforge/pmd/Rule.java   |   4 +-
 .../net/sourceforge/pmd/RuleSetWriter.java    |   8 +-
 .../pmd/properties/ConstraintDecorator.java   |  79 ++------
 .../sourceforge/pmd/properties/MapperSet.java | 144 --------------
 .../pmd/properties/OptionalSyntax.java        |  94 ---------
 .../pmd/properties/PropertyBuilder.java       |  31 +--
 .../pmd/properties/PropertyDescriptor.java    |  14 +-
 .../pmd/properties/PropertyFactory.java       |  16 +-
 ...taxUtils.java => PropertyParsingUtil.java} |  76 +++----
 .../pmd/properties/PropertySerializer.java    |  57 ++++++
 .../pmd/properties/PropertyTypeId.java        |  38 ++--
 .../sourceforge/pmd/properties/SeqSyntax.java |  93 ---------
 .../pmd/properties/ValueSyntax.java           |  32 +--
 .../sourceforge/pmd/properties/XmlMapper.java | 185 ------------------
 .../pmd/renderers/HTMLRenderer.java           |   2 +-
 .../pmd/renderers/RendererFactory.java        |   2 +-
 .../sourceforge/pmd/rules/RuleFactory.java    |  56 +++---
 .../pmd/util/internal/xml/SchemaConstant.java |   4 +-
 .../util/internal/xml/XmlErrorMessages.java   |   3 +-
 .../pmd/util/internal/xml/XmlUtil.java        |  12 --
 .../pmd/util/treeexport/TreeExportCli.java    |   2 +-
 .../properties/PropertyDescriptorTest.java    |   2 +-
 .../pmd/properties/PropertySyntaxTest.java    | 117 +----------
 .../pmd/docs/RuleDocGenerator.java            |  34 +---
 24 files changed, 211 insertions(+), 894 deletions(-)
 delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/MapperSet.java
 delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/OptionalSyntax.java
 rename pmd-core/src/main/java/net/sourceforge/pmd/properties/{XmlSyntaxUtils.java => PropertyParsingUtil.java} (68%)
 create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySerializer.java
 delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java
 delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
index 65bc5150ce..51c3ec3b43 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
@@ -36,7 +36,7 @@ public interface Rule extends PropertySource {
     PropertyDescriptor> VIOLATION_SUPPRESS_REGEX_DESCRIPTOR =
         PropertyFactory.regexProperty("violationSuppressRegex")
                        .desc("Suppress violations with messages matching a regular expression")
-                       .toOptional()
+                       .toOptional("")
                        .defaultValue(Optional.empty())
                        .build();
 
@@ -48,7 +48,7 @@ public interface Rule extends PropertySource {
     PropertyDescriptor> VIOLATION_SUPPRESS_XPATH_DESCRIPTOR =
         PropertyFactory.stringProperty("violationSuppressXPath")
                        .desc("Suppress violations on nodes which match a given relative XPath expression.")
-                       .toOptional()
+                       .toOptional("")
                        .defaultValue(Optional.empty())
                        .build();
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
index 685fdf0768..4f5e54e6c2 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
@@ -36,7 +36,7 @@ import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.PropertySource;
 import net.sourceforge.pmd.properties.PropertyTypeId;
-import net.sourceforge.pmd.properties.XmlMapper;
+import net.sourceforge.pmd.properties.PropertySerializer;
 import net.sourceforge.pmd.util.IOUtil;
 import net.sourceforge.pmd.util.internal.xml.SchemaConstants;
 
@@ -303,10 +303,10 @@ public class RuleSetWriter {
         Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property");
         SchemaConstants.NAME.setOn(element, propertyDescriptor.name());
 
-        XmlMapper xmlStrategy = propertyDescriptor.xmlMapper();
+        PropertySerializer xmlStrategy = propertyDescriptor.serializer();
 
-        Element valueElt = createPropertyValueElement(xmlStrategy.getWriteElementName(value));
-        xmlStrategy.toXml(valueElt, value);
+        Element valueElt = createPropertyValueElement(SchemaConstants.PROPERTY_VALUE.xmlName());
+        valueElt.setTextContent(xmlStrategy.toString(value));
         element.appendChild(valueElt);
 
         return element;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
index b7a9a5304d..03ea82731f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
@@ -5,102 +5,59 @@
 package net.sourceforge.pmd.properties;
 
 import java.util.List;
-import java.util.Set;
 
 import org.checkerframework.checker.nullness.qual.NonNull;
-import org.w3c.dom.Element;
 
 import net.sourceforge.pmd.util.CollectionUtil;
-import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter;
-import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages;
 
 /**
  * Decorates an XmlMapper with some {@link PropertyConstraint}s.
  * Those are checked when the value is parsed. This is used to
  * report errors on the most specific failing element.
- *
- * 

Note that this is the only XmlMapper that *applies* constraints - * in {@link #fromXml(Element, PmdXmlReporter)}. A {@link SeqSyntax} - * or {@link OptionalSyntax} may return some constraints in {@link #getConstraints()} - * that are derived from the constraints of the item, yet not check them - * on elements (they will be applied on each element by the {@link XmlMapper} - * they wrap). */ -class ConstraintDecorator extends XmlMapper { +class ConstraintDecorator extends PropertySerializer { - private final XmlMapper xmlMapper; + private final PropertySerializer propertySerializer; private final List> constraints; - ConstraintDecorator(XmlMapper mapper, List> constraints) { - this.xmlMapper = mapper; + ConstraintDecorator(PropertySerializer mapper, List> constraints) { + this.propertySerializer = mapper; this.constraints = constraints; } - @Override - public T fromXml(Element element, PmdXmlReporter err) { - T t = xmlMapper.fromXml(element, err); - - XmlSyntaxUtils.checkConstraintsThrow( - t, - constraints, - s -> new ConstraintViolatedException(err.at(element).error(XmlErrorMessages.ERR__CONSTRAINT_NOT_SATISFIED, s)) - ); - - return t; - } - @Override public List> getConstraints() { return constraints; } @Override - public XmlMapper withConstraint(PropertyConstraint t) { - return new ConstraintDecorator<>(this.xmlMapper, CollectionUtil.plus(this.constraints, t)); - } - - @Override - public void toXml(Element container, T value) { - xmlMapper.toXml(container, value); - } - - - @Override - public String getWriteElementName(T value) { - return xmlMapper.getWriteElementName(value); - } - - - @Override - public Set getReadElementNames() { - return xmlMapper.getReadElementNames(); - } - - - @Override - protected List examplesImpl(String curIndent, String baseIndent) { - return xmlMapper.examplesImpl(curIndent, baseIndent); - } - - @Override - public boolean supportsStringMapping() { - return xmlMapper.supportsStringMapping(); + public PropertySerializer withConstraint(PropertyConstraint t) { + return new ConstraintDecorator<>(this.propertySerializer, CollectionUtil.plus(this.constraints, t)); } @Override public T fromString(@NonNull String attributeData) { - return xmlMapper.fromString(attributeData); + T t = propertySerializer.fromString(attributeData); + + // perform constraint validation + PropertyParsingUtil.checkConstraintsThrow( + t, + constraints, + ConstraintViolatedException::new + ); + + return t; } @Override public @NonNull String toString(T value) { - return xmlMapper.toString(value); + return propertySerializer.toString(value); } @Override public String toString() { - return xmlMapper.toString(); + return propertySerializer.toString(); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/MapperSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/MapperSet.java deleted file mode 100644 index 28bcfc1d43..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/MapperSet.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.w3c.dom.Element; - -import net.sourceforge.pmd.util.CollectionUtil; -import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; -import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; -import net.sourceforge.pmd.util.internal.xml.XmlUtil; - -/** - * A set of syntaxes for read and write. One special syntax is designated - * as the one used to write elements, the others are used to read. - */ -final class MapperSet extends XmlMapper { - - private final XmlMapper forWrite; - private final Map> readIndex; - - /** - * @param newSyntax Newer syntax (eg seq) - * @param compat Value syntax (eg delimited string for sequence) - */ - MapperSet(XmlMapper newSyntax, ValueSyntax compat, boolean preferNew) { - // the set here prunes duplicates - this(preferNew ? newSyntax : compat, CollectionUtil.setOf(newSyntax, compat)); - } - - /** - * TODO This constructor is a bit too general for now. The other constructor - * is the only one that's public. The problem with publishing this constructor, - * is that there may be a SyntaxSet somewhere in the 'forRead' set, and some - * overlapping values may be unlocked - * - * @param forWrite Designated strategy for writing - * @param forRead Set of supported syntaxes, must have pairwise different read names - */ - private MapperSet(XmlMapper forWrite, Collection> forRead) { - super(); - this.forWrite = forWrite; - - if (forRead.isEmpty()) { - throw new IllegalArgumentException("Empty set of reads strategies!"); - } - - Map> map = new LinkedHashMap<>(); - for (XmlMapper syntax : forRead) { - for (String name : syntax.getReadElementNames()) { - - map.merge(name, syntax, (a, b) -> { - // merge function - throw new IllegalArgumentException( - "Duplicate name '" + name + "', for syntaxes " + a + " and " + b - ); - }); - } - } - this.readIndex = map; - } - - @Override - public Set getReadElementNames() { - return readIndex.keySet(); - } - - @Override - public String getWriteElementName(T value) { - return forWrite.getWriteElementName(value); - } - - @Override - public @Nullable T fromString(@NonNull String string) { - - for (XmlMapper syntax : supportedReadStrategies()) { - if (syntax.supportsStringMapping()) { - return syntax.fromString(string); - } - } - - throw new UnsupportedOperationException(); - } - - @Override - public @NonNull String toString(T value) { - - for (XmlMapper syntax : supportedReadStrategies()) { - if (syntax.supportsStringMapping()) { - return syntax.toString(value); - } - } - - throw new UnsupportedOperationException(); - } - - @Override - public List> getConstraints() { - return readIndex.values().iterator().next().getConstraints(); - } - - private Collection> supportedReadStrategies() { - return readIndex.values(); - } - - @Override - public boolean supportsStringMapping() { - return supportedReadStrategies().stream().anyMatch(XmlMapper::supportsStringMapping) - && forWrite.supportsStringMapping(); - } - - @Override - public T fromXml(Element element, PmdXmlReporter err) { - XmlMapper syntax = readIndex.get(element.getTagName()); - if (syntax == null) { - throw err.at(element).error(XmlErrorMessages.ERR__UNEXPECTED_ELEMENT, - element.getTagName(), - XmlUtil.formatPossibleNames(XmlUtil.toConstants(readIndex.keySet()))); - } else { - return syntax.fromXml(element, err); - } - } - - @Override - public void toXml(Element container, T value) { - forWrite.toXml(container, value); - } - - - @Override - protected List examplesImpl(String curIndent, String baseIndent) { - return readIndex.values().stream().flatMap(it -> it.examplesImpl(curIndent, baseIndent).stream()).collect(Collectors.toList()); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/OptionalSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/OptionalSyntax.java deleted file mode 100644 index 2a8c3dee9b..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/OptionalSyntax.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.properties; - -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import org.checkerframework.checker.nullness.qual.NonNull; -import org.w3c.dom.Element; - -import net.sourceforge.pmd.util.CollectionUtil; -import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; - -/** - * Serialize an optional value. If the value is itself an {@code Optional}, - * then mentioning {@code } will yield a toplevel empty optional. So - * having a non-empty optional with an empty optional inside is disallowed. - */ -final class OptionalSyntax extends XmlMapper> { - - private static final String EMPTY_NAME = "none"; - private final XmlMapper itemSyntax; - - OptionalSyntax(XmlMapper itemSyntax) { - this.itemSyntax = itemSyntax; - } - - // TODO this scheme for string mapping is lossy, and is there just - // for compatibility with CodeClimateRenderer - - @Override - public boolean supportsStringMapping() { - return itemSyntax.supportsStringMapping(); - } - - @Override - public @NonNull String toString(Optional value) { - return value.map(itemSyntax::toString).orElse(""); - } - - @Override - public Optional fromString(@NonNull String attributeData) { - return attributeData.isEmpty() ? Optional.empty() - : Optional.ofNullable(itemSyntax.fromString(attributeData)); - } - - @Override - public List>> getConstraints() { - return itemSyntax.getConstraints().stream() - .map(PropertyConstraint::toOptionalConstraint) - .collect(Collectors.toList()); - } - - @Override - public void toXml(Element container, Optional value) { - if (value.isPresent()) { - itemSyntax.toXml(container, value.get()); - } else { - Element none = container.getOwnerDocument().createElement(EMPTY_NAME); - container.appendChild(none); - } - } - - @Override - public Optional fromXml(Element element, PmdXmlReporter err) { - if (EMPTY_NAME.equals(element.getTagName())) { - return Optional.empty(); - } else { - return Optional.ofNullable(itemSyntax.fromXml(element, err)); - } - } - - @Override - public String getWriteElementName(Optional value) { - return value.map(itemSyntax::getWriteElementName).orElse(EMPTY_NAME); - } - - @Override - public Set getReadElementNames() { - HashSet strings = new HashSet<>(itemSyntax.getReadElementNames()); - strings.add(EMPTY_NAME); - return strings; - } - - @Override - protected List examplesImpl(String curIndent, String baseIndent) { - return CollectionUtil.plus(itemSyntax.examplesImpl(curIndent, baseIndent), curIndent + ""); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java index 4dc17c1140..9a003f2581 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java @@ -211,17 +211,17 @@ public abstract class PropertyBuilder, T> { // This would allow specifying eg lists of numbers as 1,2,3, for which the syntax would look clumsy abstract static class BaseSinglePropertyBuilder, T> extends PropertyBuilder { - private XmlMapper parser; + private PropertySerializer parser; // Class is not final but a package-private constructor restricts inheritance - BaseSinglePropertyBuilder(String name, XmlMapper parser) { + BaseSinglePropertyBuilder(String name, PropertySerializer parser) { super(name); this.parser = parser; } - protected XmlMapper getParser() { + protected PropertySerializer getParser() { return parser; } @@ -296,15 +296,18 @@ public abstract class PropertyBuilder, T> { * Returns a new builder that can be used to build a property * handling {@code Optional}. The validators already added * are used on the validator property. If the default value was - * previously set, it is converted to an optional with {@link Optional#ofNullable(Object)}. + * previously set, it is converted to an optional with {@link Optional#of(Object)}. + * + * @param missingValue The string representation of the empty optional. * * @return A new property builder for an optional. */ - public GenericPropertyBuilder> toOptional() { - GenericPropertyBuilder> result = new GenericPropertyBuilder<>(this.getName(), XmlSyntaxUtils.toOptional(getParser())); + public GenericPropertyBuilder> toOptional(String missingValue) { + GenericPropertyBuilder> result = + new GenericPropertyBuilder<>(this.getName(), PropertyParsingUtil.toOptional(getParser(), missingValue)); if (isDefaultValueSet()) { - result.defaultValue(Optional.ofNullable(getDefaultValue())); + result.defaultValue(Optional.of(getDefaultValue())); } if (isDescriptionSet()) { @@ -338,7 +341,7 @@ public abstract class PropertyBuilder, T> { // Note: This type is used to fix the first type parameter for classes that don't need more API. public static class GenericPropertyBuilder extends BaseSinglePropertyBuilder, T> { - GenericPropertyBuilder(String name, XmlMapper parser) { + GenericPropertyBuilder(String name, PropertySerializer parser) { super(name, parser); } } @@ -354,7 +357,7 @@ public abstract class PropertyBuilder, T> { public static final class RegexPropertyBuilder extends BaseSinglePropertyBuilder { RegexPropertyBuilder(String name) { - super(name, XmlSyntaxUtils.REGEX); + super(name, PropertyParsingUtil.REGEX); } @@ -411,7 +414,7 @@ public abstract class PropertyBuilder, T> { */ public static final class GenericCollectionPropertyBuilder> extends PropertyBuilder, C> { - private XmlMapper itemParser; + private PropertySerializer itemParser; private final Collector collector; private final List> collectionConstraints = new ArrayList<>(); private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER; @@ -421,7 +424,7 @@ public abstract class PropertyBuilder, T> { * Builds a new builder for a collection type. Package-private. */ GenericCollectionPropertyBuilder(String name, - XmlMapper itemParser, + PropertySerializer itemParser, Collector collector) { super(name); this.itemParser = itemParser; @@ -515,11 +518,9 @@ public abstract class PropertyBuilder, T> { @Override public PropertyDescriptor build() { - XmlMapper syntax = itemParser.supportsStringMapping() - ? XmlSyntaxUtils.seqAndDelimited(itemParser, collector, false, multiValueDelimiter) - : XmlSyntaxUtils.onlySeq(itemParser, collector); + PropertySerializer syntax = PropertyParsingUtil.delimitedString(itemParser, collector, multiValueDelimiter); - syntax = XmlSyntaxUtils.withAllConstraints(syntax, collectionConstraints); + syntax = PropertyParsingUtil.withAllConstraints(syntax, collectionConstraints); return new PropertyDescriptor<>( getName(), diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java index 6ad4ff7723..8b8b4511a6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java @@ -34,7 +34,7 @@ import net.sourceforge.pmd.annotation.InternalApi; public final class PropertyDescriptor { - private final XmlMapper parser; + private final PropertySerializer parser; private final PropertyTypeId typeId; private final String name; private final String description; @@ -44,7 +44,7 @@ public final class PropertyDescriptor { PropertyDescriptor(String name, String description, T defaultValue, - XmlMapper parser, + PropertySerializer parser, @Nullable PropertyTypeId typeId, boolean isXPathAvailable) { @@ -55,7 +55,7 @@ public final class PropertyDescriptor { this.typeId = typeId; this.isXPathAvailable = isXPathAvailable; - XmlSyntaxUtils.checkConstraintsThrow( + PropertyParsingUtil.checkConstraintsThrow( defaultValue, parser.getConstraints(), ConstraintViolatedException::new @@ -100,7 +100,7 @@ public final class PropertyDescriptor { * Returns the strategy used to read and write this property to XML. * May support strings too. */ - public XmlMapper xmlMapper() { + public PropertySerializer serializer() { return parser; } @@ -116,7 +116,7 @@ public final class PropertyDescriptor { */ @Deprecated public String errorFor(T value) { - return XmlSyntaxUtils.checkConstraintsJoin(value, parser.getConstraints()); + return PropertyParsingUtil.checkConstraintsJoin(value, parser.getConstraints()); } @@ -148,7 +148,7 @@ public final class PropertyDescriptor { */ @Deprecated public T valueFrom(String propertyString) throws IllegalArgumentException { - return xmlMapper().fromString(propertyString); + return serializer().fromString(propertyString); } @@ -165,7 +165,7 @@ public final class PropertyDescriptor { */ @Deprecated public String asDelimitedString(T value) { - return xmlMapper().toString(value); + return serializer().toString(value); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java index 3b3a744111..c842518edd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.properties; import static java.util.Arrays.asList; -import static net.sourceforge.pmd.properties.XmlSyntaxUtils.enumerationParser; +import static net.sourceforge.pmd.properties.PropertyParsingUtil.enumerationParser; import java.util.Arrays; import java.util.List; @@ -114,7 +114,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder intProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.INTEGER); + return new GenericPropertyBuilder<>(name, PropertyParsingUtil.INTEGER); } @@ -151,7 +151,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder longIntProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.LONG); + return new GenericPropertyBuilder<>(name, PropertyParsingUtil.LONG); } @@ -183,7 +183,7 @@ public final class PropertyFactory { * @see NumericConstraints */ public static GenericPropertyBuilder doubleProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.DOUBLE); + return new GenericPropertyBuilder<>(name, PropertyParsingUtil.DOUBLE); } @@ -229,7 +229,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder stringProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.STRING); + return new GenericPropertyBuilder<>(name, PropertyParsingUtil.STRING); } /** @@ -259,7 +259,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder charProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.CHARACTER); + return new GenericPropertyBuilder<>(name, PropertyParsingUtil.CHARACTER); } @@ -286,7 +286,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder booleanProperty(String name) { - return new GenericPropertyBuilder<>(name, XmlSyntaxUtils.BOOLEAN); + return new GenericPropertyBuilder<>(name, PropertyParsingUtil.BOOLEAN); } // We can add more useful factories with Java 8. @@ -311,7 +311,7 @@ public final class PropertyFactory { * @return A new builder */ public static GenericPropertyBuilder enumProperty(String name, Map nameToValue) { - XmlMapper parser = enumerationParser( + PropertySerializer parser = enumerationParser( nameToValue, t -> Objects.requireNonNull(CollectionUtil.getKeyOfValue(nameToValue, t)) ); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java similarity index 68% rename from pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java rename to pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java index a42049986c..ce37988557 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlSyntaxUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java @@ -17,15 +17,13 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.util.internal.xml.XmlUtil; /** * This is internal API and shouldn't be used directly by clients. */ -@InternalApi -public final class XmlSyntaxUtils { +final class PropertyParsingUtil { public static final ValueSyntax STRING = ValueSyntax.withDefaultToString(String::trim); public static final ValueSyntax CHARACTER = @@ -44,32 +42,38 @@ public final class XmlSyntaxUtils { public static final ValueSyntax DOUBLE = ValueSyntax.withDefaultToString(preTrim(Double::valueOf)); - public static final XmlMapper> INTEGER_LIST = numberList(INTEGER); - public static final XmlMapper> DOUBLE_LIST = numberList(DOUBLE); - public static final XmlMapper> LONG_LIST = numberList(LONG); + public static final PropertySerializer> INTEGER_LIST = numberList(INTEGER); + public static final PropertySerializer> DOUBLE_LIST = numberList(DOUBLE); + public static final PropertySerializer> LONG_LIST = numberList(LONG); - public static final XmlMapper> CHAR_LIST = otherList(CHARACTER); - public static final XmlMapper> STRING_LIST = otherList(STRING); + public static final PropertySerializer> CHAR_LIST = otherList(CHARACTER); + public static final PropertySerializer> STRING_LIST = otherList(STRING); - private XmlSyntaxUtils() { + private PropertyParsingUtil() { } - private static XmlMapper> numberList(ValueSyntax valueSyntax) { - return seqAndDelimited(valueSyntax, Collectors.toList(), true, PropertyFactory.DEFAULT_NUMERIC_DELIMITER); + private static PropertySerializer> numberList(ValueSyntax valueSyntax) { + return delimitedString(valueSyntax, Collectors.toList(), PropertyFactory.DEFAULT_NUMERIC_DELIMITER); } - private static XmlMapper> otherList(ValueSyntax valueSyntax) { - return seqAndDelimited(valueSyntax, Collectors.toList(), /* prefer old syntax for now */ true, PropertyFactory.DEFAULT_DELIMITER); + private static PropertySerializer> otherList(ValueSyntax valueSyntax) { + return delimitedString(valueSyntax, Collectors.toList(), /* prefer old syntax for now */ PropertyFactory.DEFAULT_DELIMITER); } private static Function preTrim(Function parser) { return parser.compose(String::trim); } - public static XmlMapper> toOptional(XmlMapper itemSyntax) { - return new OptionalSyntax<>(itemSyntax); + public static PropertySerializer> toOptional(PropertySerializer itemSyntax, String missingValue) { + return ValueSyntax.create( + opt -> opt.map(itemSyntax::toString).orElse(missingValue), + str -> { + if (str.equals(missingValue)) return Optional.empty(); + return Optional.of(itemSyntax.fromString(str)); + } + ); } @@ -88,8 +92,7 @@ public final class XmlSyntaxUtils { return failures; } - @Nullable - public static String checkConstraintsJoin(T t, List> constraints) { + public static @Nullable String checkConstraintsJoin(T t, List> constraints) { List failures = checkConstraints(t, constraints); if (!failures.isEmpty()) { return String.join(", ", failures); @@ -107,8 +110,8 @@ public final class XmlSyntaxUtils { } } - public static XmlMapper withAllConstraints(XmlMapper mapper, List> constraints) { - XmlMapper result = mapper; + public static PropertySerializer withAllConstraints(PropertySerializer mapper, List> constraints) { + PropertySerializer result = mapper; for (PropertyConstraint constraint : constraints) { result = result.withConstraint(constraint); } @@ -122,44 +125,19 @@ public final class XmlSyntaxUtils { * * @param itemSyntax Serializer for the items, must support string mapping * @param collector Collector to create the collection from strings - * @param preferOldSyntax If true, the property will be written with {@code }, - * otherwise with {@code }. * @param delimiter Delimiter for the {@code } syntax * @param Type of items * @param Type of collection to handle * * @throws IllegalArgumentException If the item syntax doesn't support string mapping */ - public static > XmlMapper seqAndDelimited(XmlMapper itemSyntax, - Collector collector, - boolean preferOldSyntax, - char delimiter) { - if (!itemSyntax.supportsStringMapping()) { - throw new IllegalArgumentException("Item syntax does not support string mapping " + itemSyntax); - } - return new MapperSet<>( - new SeqSyntax<>(itemSyntax, collector), - delimitedString(itemSyntax::toString, itemSyntax::fromString, delimiter, collector), - preferOldSyntax - ); - } - - public static > XmlMapper onlySeq(XmlMapper itemSyntax, - Collector collector) { - return new SeqSyntax<>(itemSyntax, collector); - } - - - private static > ValueSyntax delimitedString( - Function toString, - Function fromString, - char delimiter, - Collector collector - ) { + public static > PropertySerializer delimitedString(PropertySerializer itemSyntax, + Collector collector, + char delimiter) { String delim = "" + delimiter; return ValueSyntax.create( - coll -> IteratorUtil.toStream(coll.iterator()).map(toString).collect(Collectors.joining(delim)), - string -> parseListWithEscapes(string, delimiter, fromString).stream().collect(collector) + coll -> IteratorUtil.toStream(coll.iterator()).map(itemSyntax::toString).collect(Collectors.joining(delim)), + string -> parseListWithEscapes(string, delimiter, itemSyntax::fromString).stream().collect(collector) ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySerializer.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySerializer.java new file mode 100644 index 0000000000..6eb7dca88e --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySerializer.java @@ -0,0 +1,57 @@ +/* + * 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.List; + +import org.checkerframework.checker.nullness.qual.NonNull; + + +/** + * Strategy to serialize a value to and from strings. + */ +public abstract class PropertySerializer { + + PropertySerializer() { + // package private, we want to control available mappers to + // put them into the ruleset schema + } + + + /** + * Returns the constraints that this mapper applies to values + * after parsing them. This may be used for documentation, or + * to check a constraint on a value that was not parsed from + * XML. + * + * @implNote See {@link ConstraintDecorator} + */ + public abstract List> getConstraints(); + + /** + * Returns a new XML mapper that will check parsed values with + * the given constraint. + */ + public PropertySerializer withConstraint(PropertyConstraint t) { + return new ConstraintDecorator<>(this, Collections.singletonList(t)); + } + + /** + * Read the value from a string, if it is supported. + * + * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) + */ + public abstract T fromString(@NonNull String attributeData); + + /** + * Format the value to a string. + * + * @throws IllegalArgumentException if something goes wrong (but should be reported on the error reporter) + */ + public abstract @NonNull String toString(T value); + + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java index a30a862014..88b9befd86 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -30,26 +30,26 @@ public enum PropertyTypeId { // property types around XML Schema Datatypes (XSD) 1.0 or 1.1 instead of Java datatypes (save for // e.g. the Class type), including the mnemonics (eg. xs:integer instead of Integer) - BOOLEAN("Boolean", XmlSyntaxUtils.BOOLEAN, PropertyFactory::booleanProperty), - STRING("String", XmlSyntaxUtils.STRING, PropertyFactory::stringProperty), - STRING_LIST("List[String]", XmlSyntaxUtils.STRING_LIST, PropertyFactory::stringListProperty), - CHARACTER("Character", XmlSyntaxUtils.CHARACTER, PropertyFactory::charProperty), - CHARACTER_LIST("List[Character]", XmlSyntaxUtils.CHAR_LIST, PropertyFactory::charListProperty), + BOOLEAN("Boolean", PropertyParsingUtil.BOOLEAN, PropertyFactory::booleanProperty), + STRING("String", PropertyParsingUtil.STRING, PropertyFactory::stringProperty), + STRING_LIST("List[String]", PropertyParsingUtil.STRING_LIST, PropertyFactory::stringListProperty), + CHARACTER("Character", PropertyParsingUtil.CHARACTER, PropertyFactory::charProperty), + CHARACTER_LIST("List[Character]", PropertyParsingUtil.CHAR_LIST, PropertyFactory::charListProperty), - REGEX("Regex", XmlSyntaxUtils.REGEX, PropertyFactory::regexProperty), + REGEX("Regex", PropertyParsingUtil.REGEX, PropertyFactory::regexProperty), - INTEGER("Integer", XmlSyntaxUtils.INTEGER, PropertyFactory::intProperty), - INTEGER_LIST("List[Integer]", XmlSyntaxUtils.INTEGER_LIST, PropertyFactory::intListProperty), - LONG("Long", XmlSyntaxUtils.LONG, PropertyFactory::longIntProperty), - LONG_LIST("List[Long]", XmlSyntaxUtils.LONG_LIST, PropertyFactory::longIntListProperty), - DOUBLE("Double", XmlSyntaxUtils.DOUBLE, PropertyFactory::doubleProperty), - DOUBLE_LIST("List[Double]", XmlSyntaxUtils.DOUBLE_LIST, PropertyFactory::doubleListProperty), + INTEGER("Integer", PropertyParsingUtil.INTEGER, PropertyFactory::intProperty), + INTEGER_LIST("List[Integer]", PropertyParsingUtil.INTEGER_LIST, PropertyFactory::intListProperty), + LONG("Long", PropertyParsingUtil.LONG, PropertyFactory::longIntProperty), + LONG_LIST("List[Long]", PropertyParsingUtil.LONG_LIST, PropertyFactory::longIntListProperty), + DOUBLE("Double", PropertyParsingUtil.DOUBLE, PropertyFactory::doubleProperty), + DOUBLE_LIST("List[Double]", PropertyParsingUtil.DOUBLE_LIST, PropertyFactory::doubleListProperty), ; // SUPPRESS CHECKSTYLE enum trailing semi is awesome private static final Map CONSTANTS_BY_MNEMONIC; private final String stringId; - private final XmlMapper xmlMapper; + private final PropertySerializer propertySerializer; private final Function> factory; @@ -62,15 +62,15 @@ public enum PropertyTypeId { } - PropertyTypeId(String id, XmlMapper syntax, Function> factory) { + PropertyTypeId(String id, PropertySerializer syntax, Function> factory) { this.stringId = id; - this.xmlMapper = syntax; + this.propertySerializer = syntax; this.factory = factory; } /** * An factory for new properties, whose default value must be deserialized - * using an {@link XmlMapper}. This is provided so that the mapper and + * using an {@link PropertySerializer}. This is provided so that the mapper and * the factory may be related through the same type parameter, so that * capture works well. * @@ -78,7 +78,7 @@ public enum PropertyTypeId { */ public interface BuilderAndMapper { - XmlMapper getXmlMapper(); + PropertySerializer getXmlMapper(); PropertyBuilder newBuilder(String name); } @@ -91,8 +91,8 @@ public enum PropertyTypeId { public BuilderAndMapper getBuilderUtils() { return new BuilderAndMapper() { @Override - public XmlMapper getXmlMapper() { - return xmlMapper; + public PropertySerializer getXmlMapper() { + return propertySerializer; } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java deleted file mode 100644 index 5845bf6279..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/SeqSyntax.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.List; -import java.util.stream.Collector; -import java.util.stream.Collectors; - -import org.w3c.dom.Element; - -import net.sourceforge.pmd.properties.XmlMapper.StableXmlMapper; -import net.sourceforge.pmd.util.CollectionUtil; -import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; -import net.sourceforge.pmd.util.internal.xml.XmlUtil; - -import com.github.oowekyala.ooxml.DomUtils; - -/** - * Serialize to and from a simple string. Examples: - * - *

{@code
- *  1
- * }
- */ -final class SeqSyntax> extends StableXmlMapper { - - private final XmlMapper itemSyntax; - private final Collector collector; - - SeqSyntax(XmlMapper itemSyntax, Collector collector) { - super("seq"); - this.itemSyntax = itemSyntax; - this.collector = collector; - } - - @Override - public void toXml(Element container, C value) { - for (T v : value) { - Element item = container.getOwnerDocument().createElement(itemSyntax.getWriteElementName(v)); - itemSyntax.toXml(item, v); - container.appendChild(item); - } - } - - @Override - public C fromXml(Element element, PmdXmlReporter err) { - return collectFromXml(element, err, collector); - } - - // capture the A type var. - private
C collectFromXml(Element element, PmdXmlReporter err, Collector collector) { - RuntimeException error = null; - A acc = collector.supplier().get(); - for (Element child : DomUtils.children(element)) { - try { - T elt = XmlUtil.expectElement(err, child, itemSyntax); - collector.accumulator().accept(acc, elt); - } catch (RuntimeException e) { - if (error == null) { - error = err.at(child).error(e); - } else { - error.addSuppressed(e); - } - } - } - - if (error != null) { - throw error; - } - return CollectionUtil.finish(collector, acc); - } - - @Override - public List> getConstraints() { - return itemSyntax.getConstraints().stream() - .map(PropertyConstraint::toCollectionConstraint) - .collect(Collectors.toList()); - } - - @Override - protected List examplesImpl(String curIndent, String baseIndent) { - String newIndent = curIndent + baseIndent; - return Collections.singletonList( - curIndent + "\n" - + newIndent + String.join("\n", itemSyntax.examplesImpl(newIndent, baseIndent)) + "\n" - + newIndent + "..." - + curIndent + "" - ); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java index 129c95e5fc..fe13e09fdd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java @@ -12,11 +12,8 @@ import java.util.Objects; import java.util.function.Function; import org.checkerframework.checker.nullness.qual.NonNull; -import org.w3c.dom.Element; import net.sourceforge.pmd.internal.util.PredicateUtil; -import net.sourceforge.pmd.properties.XmlMapper.StableXmlMapper; -import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; /** * Serialize to and from a simple string. Examples: @@ -30,9 +27,8 @@ import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; *
This class is special because it enables compatibility with the
  * pre 7.0.0 XML syntax.
  */
-class ValueSyntax extends StableXmlMapper {
+class ValueSyntax extends PropertySerializer {
 
-    private static final String VALUE_NAME = "value";
     private final Function toString;
     private final Function<@NonNull String, ? extends T> fromString;
 
@@ -42,17 +38,11 @@ class ValueSyntax extends StableXmlMapper {
     ValueSyntax(Function toString,
                 Function<@NonNull String, ? extends T> fromString,
                 List> docConstraints) {
-        super(VALUE_NAME);
         this.toString = toString;
         this.fromString = fromString;
         this.docConstraints = docConstraints;
     }
 
-    @Override
-    public boolean supportsStringMapping() {
-        return true;
-    }
-
     @Override
     public List> getConstraints() {
         return docConstraints;
@@ -68,26 +58,6 @@ class ValueSyntax extends StableXmlMapper {
         return toString.apply(data);
     }
 
-    @Override
-    public void toXml(Element container, T value) {
-        // TODO CDATA/ xml escape
-        container.setTextContent(toString.apply(value));
-    }
-
-    @Override
-    public T fromXml(Element element, PmdXmlReporter err) {
-        try {
-            return fromString.apply(element.getTextContent());
-        } catch (IllegalArgumentException e) {
-            throw err.at(element).error(e);
-        }
-    }
-
-    @Override
-    protected List examplesImpl(String curIndent, String baseIndent) {
-        return Collections.singletonList(curIndent + "data");
-    }
-
     /**
      * Creates a value syntax that cannot parse just any string, but
      * which only applies the fromString parser if a precondition holds.
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java
deleted file mode 100644
index 2789aec057..0000000000
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/XmlMapper.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.properties;
-
-import static net.sourceforge.pmd.util.CollectionUtil.setOf;
-
-import java.io.ByteArrayOutputStream;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import net.sourceforge.pmd.lang.document.Chars;
-import net.sourceforge.pmd.util.StringUtil;
-import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter;
-import net.sourceforge.pmd.util.log.MessageReporter;
-
-import com.github.oowekyala.ooxml.messages.XmlException;
-
-
-/**
- * Strategy to serialize a value to and from XML. Some strategies support
- * mapping to and from a string, without XML structure. They can be identified
- * with {@link #supportsStringMapping()}. All the standard properties
- * provided by {@link PropertyFactory} do.
- */
-public abstract class XmlMapper {
-
-    XmlMapper() {
-        // package private, we want to control available mappers to
-        // put them into the ruleset schema
-    }
-
-    /**
-     * Extract the value from an XML element. If an error occurs, throws
-     * an {@link XmlException} with {@link MessageReporter#error(Throwable)}
-     * on the most specific node (the type of exception is unspecified).
-     * This will check property constraints if any.
-     */
-    public abstract T fromXml(Element element, PmdXmlReporter err);
-
-
-    /** Write the value into the given XML element. */
-    public abstract void toXml(Element container, T value);
-
-    /**
-     * Returns true if this syntax knows how to map values of type {@code T}
-     * from and to a simple string, without XML.
-     */
-    public boolean supportsStringMapping() {
-        return false;
-    }
-
-    /**
-     * Returns the constraints that this mapper applies to values
-     * after parsing them. This may be used for documentation, or
-     * to check a constraint on a value that was not parsed from
-     * XML.
-     *
-     * @implNote See {@link ConstraintDecorator}
-     */
-    public abstract List> getConstraints();
-
-    /**
-     * Returns a new XML mapper that will check parsed values with
-     * the given constraint.
-     */
-    public XmlMapper withConstraint(PropertyConstraint t) {
-        return new ConstraintDecorator<>(this, Collections.singletonList(t));
-    }
-
-    /**
-     * Read the value from a string, if it is supported.
-     *
-     * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()}
-     * @throws IllegalArgumentException      if something goes wrong (but should be reported on the error reporter)
-     */
-    public T fromString(@NonNull String attributeData) {
-        throw new UnsupportedOperationException("Check #supportsStringMapping()");
-    }
-
-    /**
-     * Format the value to a string.
-     *
-     * @throws UnsupportedOperationException if unsupported, see {@link #supportsStringMapping()}
-     * @throws IllegalArgumentException      if something goes wrong (but should be reported on the error reporter)
-     */
-    public @NonNull String toString(T value) {
-        throw new UnsupportedOperationException("Check #supportsStringMapping()");
-    }
-
-
-    /** Get the name that should be used for the element to represent [value]. */
-    public abstract String getWriteElementName(T value);
-
-
-    /** Get all names that can be read using this syntax. */
-    public abstract Set getReadElementNames();
-
-
-    /**
-     * Returns some examples for what XML output this strategy produces.
-     * For example, {@code 1}.
-     */
-    public final List getExamples() {
-        return examplesImpl("", "    ");
-    }
-
-    /**
-     * Builds examples (impl).
-     *
-     * @param curIndent  Indentation of the current level
-     * @param baseIndent Base indentation string, adding one indent level concats this with the [curIndent]
-     */
-    protected abstract List examplesImpl(String curIndent, String baseIndent);
-
-    public String xmlToString(T value) {
-        try {
-            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
-            documentBuilderFactory.setNamespaceAware(true);
-            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
-            Document document = documentBuilder.newDocument();
-            Element container = document.createElement(getWriteElementName(value));
-            toXml(container, value);
-            document.appendChild(container);
-
-            TransformerFactory transformerFactory = TransformerFactory.newInstance();
-            Transformer transformer = transformerFactory.newTransformer();
-            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
-            // This is as close to pretty printing as we'll get using standard
-            // Java APIs.
-            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-            ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
-            transformer.transform(new DOMSource(document), new StreamResult(resultBuffer));
-            String result = resultBuffer.toString();
-            result = result.replace("", "");
-            result = StringUtil.trimIndent(Chars.wrap(result)).toString();
-            return result.trim();
-        } catch (ParserConfigurationException | TransformerException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getExamples().get(0);
-    }
-
-    /**
-     * A mapper that has a single name for read and write.
-     */
-    abstract static class StableXmlMapper extends XmlMapper {
-
-        private final String eltName;
-
-        /* package */ StableXmlMapper(String eltName) {
-            this.eltName = eltName;
-        }
-
-        @Override
-        public String getWriteElementName(T value) {
-            return eltName;
-        }
-
-        @Override
-        public Set getReadElementNames() {
-            return setOf(eltName);
-        }
-    }
-}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java
index a770c646db..d49d467232 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/HTMLRenderer.java
@@ -34,7 +34,7 @@ public class HTMLRenderer extends AbstractIncrementingRenderer {
     public static final PropertyDescriptor> LINE_PREFIX =
         PropertyFactory.stringProperty("linePrefix")
                        .desc("Prefix for line number anchor in the source file.")
-                       .toOptional()
+                       .toOptional("")
                        .defaultValue(Optional.empty())
                        .build();
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java
index 7634722d15..af9de2e0b9 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/RendererFactory.java
@@ -80,7 +80,7 @@ public final class RendererFactory {
                     if (value != null) {
                         @SuppressWarnings("unchecked")
                         PropertyDescriptor prop2 = (PropertyDescriptor) prop;
-                        Object valueFrom = prop2.xmlMapper().fromString(value);
+                        Object valueFrom = prop2.serializer().fromString(value);
                         renderer.setProperty(prop2, valueFrom);
                     }
                 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index b412d73bc8..544bf057e3 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -11,10 +11,10 @@ import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_TYP
 import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_VALUE;
 import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION;
 import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION;
+import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__MISSING_REQUIRED_ELEMENT;
 import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__PROPERTY_DOES_NOT_EXIST;
-import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__UNSUPPORTED_VALUE_ATTRIBUTE;
 import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.IGNORED__DUPLICATE_PROPERTY_SETTER;
-import static net.sourceforge.pmd.util.internal.xml.XmlUtil.getSingleChildIn;
+import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.IGNORED__PROPERTY_CHILD_HAS_PRECEDENCE;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -24,6 +24,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
 import org.checkerframework.checker.nullness.qual.Nullable;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 import net.sourceforge.pmd.Rule;
 import net.sourceforge.pmd.RulePriority;
@@ -36,9 +37,9 @@ import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.properties.PropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
+import net.sourceforge.pmd.properties.PropertySerializer;
 import net.sourceforge.pmd.properties.PropertyTypeId;
 import net.sourceforge.pmd.properties.PropertyTypeId.BuilderAndMapper;
-import net.sourceforge.pmd.properties.XmlMapper;
 import net.sourceforge.pmd.util.ResourceLoader;
 import net.sourceforge.pmd.util.StringUtil;
 import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter;
@@ -317,7 +318,7 @@ public class RuleFactory {
     }
 
     private  void setRulePropertyCapture(Rule rule, PropertyDescriptor descriptor, Element propertyElt, PmdXmlReporter err) {
-        T value = parsePropertyValue(propertyElt, err, descriptor.xmlMapper());
+        T value = parsePropertyValue(propertyElt, err, descriptor.serializer());
         rule.setProperty(descriptor, value);
     }
 
@@ -393,34 +394,29 @@ public class RuleFactory {
         }
     }
 
-    private static  T parsePropertyValue(Element propertyElt, PmdXmlReporter err, XmlMapper syntax) {
-        @Nullable String defaultAttr = PROPERTY_VALUE.getAttributeOrNull(propertyElt);
-        if (defaultAttr != null) {
-            Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElt);
-
-            // the attribute syntax could be deprecated.
-            //   err.warn(attrNode,
-            //            WARN__DEPRECATED_USE_OF_ATTRIBUTE,
-            //            PROPERTY_VALUE.xmlName(),
-            //            String.join("\nor\n", syntax.getExamples()));
-
-            try {
-                return syntax.fromString(defaultAttr);
-            } catch (IllegalArgumentException e) {
-                throw err.at(attrNode).error(e);
-            } catch (UnsupportedOperationException e) {
-                throw err.at(attrNode)
-                         .error(ERR__UNSUPPORTED_VALUE_ATTRIBUTE,
-                                String.join("\nor\n", syntax.getExamples()));
+    private static  T parsePropertyValue(Element propertyElt, PmdXmlReporter err, PropertySerializer syntax) {
+        String valueAttr = PROPERTY_VALUE.getAttributeOrNull(propertyElt);
+        Element valueChild = PROPERTY_VALUE.getOptChildIn(propertyElt, err);
+        Attr attrNode = PROPERTY_VALUE.getAttributeNode(propertyElt);
+        Node node;
+        String valueStr;
+        if (valueChild != null) {
+            if (valueAttr != null) {
+                err.at(attrNode).warn(IGNORED__PROPERTY_CHILD_HAS_PRECEDENCE);
             }
-
+            valueStr = valueChild.getTextContent();
+            node = valueChild;
+        } else if (valueAttr != null) {
+            valueStr = valueAttr;
+            node = attrNode;
         } else {
-            Element child = getSingleChildIn(propertyElt,
-                                             true,
-                                             err,
-                                             XmlUtil.toConstants(syntax.getReadElementNames()));
-            // this will report the correct error if any
-            return syntax.fromXml(child, err);
+            throw err.at(propertyElt).error(ERR__MISSING_REQUIRED_ELEMENT, PROPERTY_VALUE.xmlName());
+        }
+
+        try {
+            return syntax.fromString(valueStr);
+        } catch (IllegalArgumentException e) {
+            throw err.at(node).error(e);
         }
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstant.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstant.java
index 0b47420e7e..eeb6d6cbdf 100755
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstant.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstant.java
@@ -85,11 +85,11 @@ public class SchemaConstant {
                       .collect(Collectors.toList());
     }
 
-    public Element getSingleChildIn(Element elt, PmdXmlReporter err) {
+    public @NonNull Element getSingleChildIn(Element elt, PmdXmlReporter err) {
         return XmlUtil.getSingleChildIn(elt, true, err, setOf(this));
     }
 
-    public Element getOptChildIn(Element elt, PmdXmlReporter err) {
+    public @Nullable Element getOptChildIn(Element elt, PmdXmlReporter err) {
         return XmlUtil.getSingleChildIn(elt, false, err, setOf(this));
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java
index d397020dd8..558c5c5172 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java
@@ -17,7 +17,7 @@ public final class XmlErrorMessages {
     public static final String ERR__UNEXPECTED_ATTRIBUTE_IN = "Unexpected attribute ''{0}'' in {1}";
     public static final String ERR__MISSING_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is missing";
     public static final String ERR__BLANK_REQUIRED_ATTRIBUTE = "Required attribute ''{0}'' is blank";
-    public static final String ERR__MISSING_REQUIRED_ELEMENT = "Required child element named {0} is missing";
+    public static final String ERR__MISSING_REQUIRED_ELEMENT = "Required child element named ''{0}'' is missing";
 
     /** {0}: unexpected element name; {1}: parent node name; {2}: allowed elements in this context */
     public static final String IGNORED__UNEXPECTED_ELEMENT = ERR__UNEXPECTED_ELEMENT + THIS_WILL_BE_IGNORED;
@@ -26,6 +26,7 @@ public final class XmlErrorMessages {
     public static final String IGNORED__UNEXPECTED_ATTRIBUTE_IN = ERR__UNEXPECTED_ATTRIBUTE_IN + THIS_WILL_BE_IGNORED;
     public static final String IGNORED__DUPLICATE_CHILD_ELEMENT = "Duplicated child with name ''{0}''" + THIS_WILL_BE_IGNORED;
     public static final String IGNORED__DUPLICATE_PROPERTY_SETTER = "Duplicate property tag with name ''{0}''" + THIS_WILL_BE_IGNORED;
+    public static final String IGNORED__PROPERTY_CHILD_HAS_PRECEDENCE = "Both a ''value'' attribute and a child element are present, the attribute will be ignored";
 
     public static final String ERR__UNSUPPORTED_VALUE_ATTRIBUTE = "This property does not support the attribute syntax.\nUse a nested element, e.g. {1}";
     public static final String ERR__PROPERTY_DOES_NOT_EXIST = "Cannot set non-existent property ''{0}'' on rule {1}";
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java
index a8596d8588..695f203e6d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlUtil.java
@@ -19,7 +19,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
-import net.sourceforge.pmd.properties.XmlMapper;
 import net.sourceforge.pmd.util.CollectionUtil;
 import net.sourceforge.pmd.util.StringUtil;
 
@@ -144,15 +143,4 @@ public final class XmlUtil {
         return buffer.toString();
     }
 
-    public static  T expectElement(PmdXmlReporter err, Element elt, XmlMapper syntax) {
-
-        if (!syntax.getReadElementNames().contains(elt.getTagName())) {
-            err.at(elt).warn("Wrong name, expected " + formatPossibleNames(toConstants(syntax.getReadElementNames())));
-        } else {
-            return syntax.fromXml(elt, err);
-        }
-
-        return null;
-    }
-
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java
index a292ef9449..0345e2fc0d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java
@@ -217,7 +217,7 @@ public class TreeExportCli {
     }
 
     private static  void setProperty(PropertyDescriptor descriptor, PropertySource bundle, String value) {
-        bundle.setProperty(descriptor, descriptor.xmlMapper().fromString(value));
+        bundle.setProperty(descriptor, descriptor.serializer().fromString(value));
     }
 
 
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
index f6ec9631af..4557d40c42 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
@@ -340,7 +340,7 @@ class PropertyDescriptorTest {
 
 
     private static List parseEscaped(String s, char d) {
-        return XmlSyntaxUtils.parseListWithEscapes(s, d, Function.identity());
+        return PropertyParsingUtil.parseListWithEscapes(s, d, Function.identity());
     }
 
     @Test
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
index c0a3215802..333f29661f 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
@@ -6,130 +6,33 @@ package net.sourceforge.pmd.properties;
 
 import static net.sourceforge.pmd.util.CollectionUtil.listOf;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.mock;
 
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.List;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.checkerframework.checker.nullness.qual.NonNull;
 import org.junit.jupiter.api.Test;
-import org.w3c.dom.Element;
-import org.xml.sax.InputSource;
 
 import net.sourceforge.pmd.RulesetFactoryTestBase;
-import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter;
-
-import com.github.oowekyala.ooxml.messages.OoxmlFacade;
 
 /**
  * @author Clรฉment Fournier
  */
 public class PropertySyntaxTest extends RulesetFactoryTestBase {
 
-    protected static @NonNull String property(String name, String... contents) {
-        return "> mapper = PropertyFactory.stringProperty("eude")
-                                                        .desc("eu")
-                                                        .toList()
-                                                        .emptyDefaultValue()
-                                                        .delim('-')
-                                                        .build()
-                                                        .xmlMapper();
-        assertParsesAs(mapper,
-                       tag("seq",
-                           valueTag("ad"),
-                           valueTag("u")
-                       ),
-                       listOf("ad", "u"));
-
-    }
-
-    @Test
-    void testXmlToString() {
-        // given an xml elt and a pdescriptor
-        // ensure fromXml runs properly
-        XmlMapper> mapper = PropertyFactory.stringProperty("eude")
-                                                        .desc("eu")
-                                                        .toList()
-                                                        .emptyDefaultValue()
-                                                        .delim(',')
-                                                        .build()
-                                                        .xmlMapper();
-        assertEquals(
-            "ad,u",
-            mapper.xmlToString(listOf("ad", "u"))
-        );
-
-    }
-
-    private  void assertParsesAs(XmlMapper mapper, String xmlCode, T expected) {
-        Element xml = parseXml(xmlCode);
-        T parsed = mapper.fromXml(xml, mock(PmdXmlReporter.class));
-        assertEquals(expected, parsed);
+    private  void assertValueRoundTrip(PropertySerializer mapper, String input, T expected) {
+        T parsed = mapper.fromString(input);
+        assertEquals(expected, parsed, "fromString");
+        String str = mapper.toString(parsed);
+        assertEquals(input, str, "toString");
     }
 
 }
diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
index 4cfa162c09..be56cf16d7 100644
--- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
+++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
@@ -37,12 +37,10 @@ import net.sourceforge.pmd.RuleSet;
 import net.sourceforge.pmd.RuleSetLoadException;
 import net.sourceforge.pmd.RuleSetLoader;
 import net.sourceforge.pmd.lang.Language;
-import net.sourceforge.pmd.lang.document.Chars;
 import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.lang.rule.XPathRule;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.util.IOUtil;
-import net.sourceforge.pmd.util.StringUtil;
 
 public class RuleDocGenerator {
     private static final Logger LOG = LoggerFactory.getLogger(RuleDocGenerator.class);
@@ -470,12 +468,9 @@ public class RuleDocGenerator {
                         lines.add("    ");
                         for (PropertyDescriptor propertyDescriptor : properties) {
                             if (!isDeprecated(propertyDescriptor)) {
-                                lines.add("        ");
-                                String defaultValue = determineDefaultValueAsXml(propertyDescriptor, rule);
-
-                                defaultValue = StringUtil.replaceIndent(Chars.wrap(defaultValue), "            ").toString();
-                                Collections.addAll(lines, defaultValue.split("\\R"));
-                                lines.add("        ");
+                                String defaultValue = determineDefaultValueAsString(propertyDescriptor, rule, false);
+                                lines.add("        ");
                             }
                         }
                         lines.add("    ");
@@ -510,30 +505,17 @@ public class RuleDocGenerator {
         T realDefaultValue = rule.getProperty(propertyDescriptor);
 
         if (realDefaultValue != null) {
-            if (propertyDescriptor.xmlMapper().supportsStringMapping()) {
-                defaultValue = propertyDescriptor.xmlMapper().toString(realDefaultValue);
-                if (pad && realDefaultValue instanceof Collection) {
-                    // surround the delimiter with spaces, so that the browser can wrap
-                    // the value nicely
-                    defaultValue = defaultValue.replaceAll(",", " , ");
-                }
-            } else {
-                defaultValue = propertyDescriptor.xmlMapper().xmlToString(realDefaultValue);
+            defaultValue = propertyDescriptor.serializer().toString(realDefaultValue);
+            if (pad && realDefaultValue instanceof Collection) {
+                // surround the delimiter with spaces, so that the browser can wrap
+                // the value nicely
+                defaultValue = defaultValue.replaceAll(",", " , ");
             }
         }
         defaultValue = StringEscapeUtils.escapeHtml4(defaultValue);
         return defaultValue;
     }
 
-    private  String determineDefaultValueAsXml(PropertyDescriptor propertyDescriptor, Rule rule) {
-        T realDefaultValue = rule.getProperty(propertyDescriptor);
-        if (realDefaultValue != null) {
-            return propertyDescriptor.xmlMapper().xmlToString(realDefaultValue);
-        } else {
-            return "";
-        }
-    }
-
     private static String stripIndentation(String description) {
         if (description == null || description.isEmpty()) {
             return "";

From 9688cf7d929df18ba49d7854442565fe41fdf05e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Wed, 27 Jul 2022 21:55:27 +0200
Subject: [PATCH 083/347] Remove errorFor

---
 .../pmd/properties/AbstractPropertySource.java | 18 ------------------
 .../pmd/properties/PropertyBuilder.java        |  7 ++++++-
 .../pmd/properties/PropertyDescriptor.java     | 16 ----------------
 .../pmd/properties/PropertySource.java         |  7 ++++++-
 4 files changed, 12 insertions(+), 36 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java
index 6c3c9227e4..f12153f0ef 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java
@@ -188,22 +188,4 @@ public abstract class AbstractPropertySource implements PropertySource {
         return Collections.unmodifiableMap(propertiesByPropertyDescriptor);
     }
 
-
-    // todo Java 8 move up to interface
-    @Override
-    public String dysfunctionReason() {
-        for (PropertyDescriptor descriptor : getOverriddenPropertyDescriptors()) {
-            String error = errorForPropCapture(descriptor);
-            if (error != null) {
-                return error;
-            }
-        }
-        return null;
-    }
-
-
-    private  String errorForPropCapture(PropertyDescriptor descriptor) {
-        return descriptor.errorFor(getProperty(descriptor));
-    }
-
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index 9a003f2581..30dc94507e 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -19,6 +19,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
 import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.internal.util.AssertionUtil;
 import net.sourceforge.pmd.internal.util.IteratorUtil;
 
 // @formatter:off
@@ -303,8 +304,12 @@ public abstract class PropertyBuilder, T> {
          * @return A new property builder for an optional.
          */
         public GenericPropertyBuilder> toOptional(String missingValue) {
+            AssertionUtil.requireParamNotNull("missingValue", missingValue);
+
+            PropertySerializer> serializer =
+                PropertyParsingUtil.toOptional(getParser(), missingValue);
             GenericPropertyBuilder> result =
-                new GenericPropertyBuilder<>(this.getName(), PropertyParsingUtil.toOptional(getParser(), missingValue));
+                new GenericPropertyBuilder<>(this.getName(), serializer);
 
             if (isDefaultValueSet()) {
                 result.defaultValue(Optional.of(getDefaultValue()));
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
index 8b8b4511a6..3f4559a829 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
@@ -104,22 +104,6 @@ public final class PropertyDescriptor {
         return parser;
     }
 
-
-    /**
-     * TODO this needs to go away. Property constraints are now checked at
-     * the time the ruleset is parsed, to report errors on the specific
-     * XML nodes. Other than that, constraints should be checked when
-     * calling {@link PropertySource#setProperty(PropertyDescriptor, Object)}
-     * for fail-fast behaviour.
-     *
-     * @deprecated PMD 7.0.0 will change the return type to {@code Optional}
-     */
-    @Deprecated
-    public String errorFor(T value) {
-        return PropertyParsingUtil.checkConstraintsJoin(value, parser.getConstraints());
-    }
-
-
     /**
      * Returns the type ID which was used to define this property. Returns
      * null if this property was defined in Java code and not in XML. This
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java
index 66915a62db..9d5ad1413a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java
@@ -145,6 +145,11 @@ public interface PropertySource {
      * between values. Returns null if the receiver is ok.
      *
      * @return String
+     *
+     * @deprecated PMD 7 will introduce another mechanism to report dysfunctional rules better.
      */
-    String dysfunctionReason();
+    @Deprecated
+    default String dysfunctionReason() {
+        return null;
+    }
 }

From 9295f817da194adb426392fdc02ad3b1a379fde6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Wed, 27 Jul 2022 22:21:34 +0200
Subject: [PATCH 084/347] Fix tests

---
 .../pmd/properties/ConstraintDecorator.java   |  6 +-
 .../pmd/properties/PropertyBuilder.java       |  3 +-
 .../properties/PropertyDescriptorTest.java    | 59 ++-----------------
 3 files changed, 12 insertions(+), 56 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
index 03ea82731f..45b376e980 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
@@ -57,7 +57,9 @@ class ConstraintDecorator extends PropertySerializer {
 
     @Override
     public String toString() {
-        return propertySerializer.toString();
+        return "ConstraintDecorator{" +
+            "propertySerializer=" + propertySerializer +
+            ", constraints=" + constraints +
+            '}';
     }
-
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index 30dc94507e..353f3bc46c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -21,6 +21,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
 import net.sourceforge.pmd.annotation.InternalApi;
 import net.sourceforge.pmd.internal.util.AssertionUtil;
 import net.sourceforge.pmd.internal.util.IteratorUtil;
+import net.sourceforge.pmd.util.CollectionUtil;
 
 // @formatter:off
 /**
@@ -524,7 +525,7 @@ public abstract class PropertyBuilder, T> {
         @Override
         public PropertyDescriptor build() {
             PropertySerializer syntax = PropertyParsingUtil.delimitedString(itemParser, collector, multiValueDelimiter);
-
+            syntax = PropertyParsingUtil.withAllConstraints(syntax, CollectionUtil.map(itemParser.getConstraints(), PropertyConstraint::toCollectionConstraint));
             syntax = PropertyParsingUtil.withAllConstraints(syntax, collectionConstraints);
 
             return new PropertyDescriptor<>(
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
index 4557d40c42..7dc33c0257 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
@@ -8,15 +8,11 @@ import static java.util.Collections.emptyList;
 import static net.sourceforge.pmd.properties.NumericConstraints.inRange;
 import static net.sourceforge.pmd.util.CollectionUtil.listOf;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasItem;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -31,10 +27,6 @@ import org.hamcrest.Matchers;
 import org.hamcrest.core.SubstringMatcher;
 import org.junit.jupiter.api.Test;
 
-import net.sourceforge.pmd.FooRule;
-import net.sourceforge.pmd.Rule;
-import net.sourceforge.pmd.RuleSet;
-
 
 /**
  * @author Clรฉment Fournier
@@ -42,59 +34,20 @@ import net.sourceforge.pmd.RuleSet;
  */
 class PropertyDescriptorTest {
 
-    @Test
-    void testConstraintViolationCausesDysfunctionalRule() {
-        PropertyDescriptor intProperty = PropertyFactory.intProperty("fooProp")
-                                                                 .desc("hello")
-                                                                 .defaultValue(4)
-                                                                 .require(inRange(1, 10))
-                                                                 .build();
 
-        FooRule rule = new FooRule();
-        rule.definePropertyDescriptor(intProperty);
-        rule.setProperty(intProperty, 1000);
-        RuleSet ruleSet = RuleSet.forSingleRule(rule);
-
-        List dysfunctional = new ArrayList<>();
-        ruleSet.removeDysfunctionalRules(dysfunctional);
-
-        assertEquals(1, dysfunctional.size());
-        assertThat(dysfunctional, hasItem(rule));
-    }
-
-
-    @Test
-    void testConstraintViolationCausesDysfunctionalRuleMulti() {
-        PropertyDescriptor> descriptor = PropertyFactory.doubleListProperty("fooProp")
-                                                                     .desc("hello")
-                                                                     .defaultValues(2., 11.) // 11. is in range
-                                                                     .requireEach(inRange(1d, 20d))
-                                                                     .build();
-
-        FooRule rule = new FooRule();
-        rule.definePropertyDescriptor(descriptor);
-        rule.setProperty(descriptor, Collections.singletonList(1000d)); // not in range
-        RuleSet ruleSet = RuleSet.forSingleRule(rule);
-
-        List dysfunctional = new ArrayList<>();
-        ruleSet.removeDysfunctionalRules(dysfunctional);
-
-        assertEquals(1, dysfunctional.size());
-        assertThat(dysfunctional, hasItem(rule));
-    }
 
     @Test
     void testDefaultValueConstraintViolationCausesFailure() {
         PropertyConstraint constraint = inRange(1, 10);
 
-        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+        IllegalArgumentException thrown = assertThrows(ConstraintViolatedException.class, () ->
             PropertyFactory.intProperty("fooProp")
                            .desc("hello")
                            .defaultValue(1000)
                            .require(constraint)
                            .build());
-        assertThat(thrown.getMessage(), allOf(containsIgnoreCase("Constraint violat"/*-ed or -ion*/),
-                containsIgnoreCase(constraint.getConstraintDescription())));
+        assertThat(thrown.getMessage(),
+                containsIgnoreCase(constraint.getConstraintDescription()));
     }
 
 
@@ -102,14 +55,14 @@ class PropertyDescriptorTest {
     void testDefaultValueConstraintViolationCausesFailureMulti() {
         PropertyConstraint constraint = inRange(1d, 10d);
 
-        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+        IllegalArgumentException thrown = assertThrows(ConstraintViolatedException.class, () ->
             PropertyFactory.doubleListProperty("fooProp")
                            .desc("hello")
                            .defaultValues(2., 11.) // 11. is out of range
                            .requireEach(constraint)
                            .build());
-        assertThat(thrown.getMessage(), allOf(containsIgnoreCase("Constraint violat"/*-ed or -ion*/),
-                containsIgnoreCase(constraint.getConstraintDescription())));
+        assertThat(thrown.getMessage(),
+                containsIgnoreCase(constraint.getConstraintDescription()));
     }
 
 

From cfae0a13f9674eca6e6d08d08e2340679a2a5c88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Wed, 27 Jul 2022 22:53:05 +0200
Subject: [PATCH 085/347] Use exceptions to validate PropertyConstraint

---
 .../pmd/properties/ConstraintDecorator.java   |  8 +--
 .../ConstraintViolatedException.java          | 17 ++++--
 .../pmd/properties/PropertyConstraint.java    | 32 ++++------
 .../pmd/properties/PropertyDescriptor.java    |  3 +-
 .../pmd/properties/PropertyParsingUtil.java   | 36 ++++-------
 .../pmd/properties/ValueSyntax.java           |  6 +-
 .../sourceforge/pmd/rules/RuleFactory.java    |  3 +
 .../pmd/RuleSetFactoryMessagesTest.java       | 25 ++++++++
 .../pmd/RulesetFactoryTestBase.java           |  2 +-
 .../sourceforge/pmd/lang/rule/MockRule.java   |  5 +-
 .../properties/NumericConstraintsTest.java    | 60 +++++++++++--------
 11 files changed, 108 insertions(+), 89 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
index 45b376e980..b6ebef0adb 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
@@ -39,14 +39,8 @@ class ConstraintDecorator extends PropertySerializer {
     @Override
     public T fromString(@NonNull String attributeData) {
         T t = propertySerializer.fromString(attributeData);
-
         // perform constraint validation
-        PropertyParsingUtil.checkConstraintsThrow(
-            t,
-            constraints,
-            ConstraintViolatedException::new
-        );
-
+        PropertyParsingUtil.checkConstraintsThrow(t, constraints);
         return t;
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintViolatedException.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintViolatedException.java
index 0c7f759e5f..a5537ac34e 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintViolatedException.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintViolatedException.java
@@ -4,6 +4,8 @@
 
 package net.sourceforge.pmd.properties;
 
+import org.apache.commons.lang3.StringUtils;
+
 /**
  * Thrown when a property constraint is violated. Detected while parsing
  * values from XML.
@@ -12,11 +14,18 @@ package net.sourceforge.pmd.properties;
  */
 public class ConstraintViolatedException extends IllegalArgumentException {
 
-    public ConstraintViolatedException(String message) {
-        super(message);
+    private final PropertyConstraint constraint;
+
+     ConstraintViolatedException(PropertyConstraint constraint, T value) {
+        super("'" + value + "' " + StringUtils.uncapitalize(constraint.getConstraintDescription()));
+        this.constraint = constraint;
     }
 
-    public ConstraintViolatedException(Throwable cause) {
-        super(cause);
+    public PropertyConstraint getConstraint() {
+        return constraint;
+    }
+
+    public String getMessageWithoutValue() {
+        return "Value " + StringUtils.uncapitalize(constraint.getConstraintDescription());
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyConstraint.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyConstraint.java
index 0e547f6afe..335443cb74 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyConstraint.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyConstraint.java
@@ -4,13 +4,10 @@
 
 package net.sourceforge.pmd.properties;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Optional;
 import java.util.function.Predicate;
 
 import org.apache.commons.lang3.StringUtils;
-import org.checkerframework.checker.nullness.qual.Nullable;
 
 import net.sourceforge.pmd.annotation.Experimental;
 
@@ -28,15 +25,14 @@ import net.sourceforge.pmd.annotation.Experimental;
 public interface PropertyConstraint {
 
     /**
-     * Returns a diagnostic message if the value
-     * has a problem. Otherwise returns null.
+     * Checks that the value conforms to this constraint. Throws if that
+     * is not the case.
      *
      * @param value The value to validate
      *
-     * @return A diagnostic message
+     * @throws ConstraintViolatedException If this constraint is violated
      */
-    @Nullable
-    String validate(T value);
+    void validate(T value);
 
 
     /**
@@ -59,8 +55,8 @@ public interface PropertyConstraint {
     default PropertyConstraint> toOptionalConstraint() {
         return new PropertyConstraint>() {
             @Override
-            public @Nullable String validate(Optional value) {
-                return value.map(PropertyConstraint.this::validate).orElse(null);
+            public void validate(Optional value) {
+                value.ifPresent(PropertyConstraint.this::validate);
             }
 
             @Override
@@ -80,16 +76,10 @@ public interface PropertyConstraint {
     default PropertyConstraint> toCollectionConstraint() {
         return new PropertyConstraint>() {
             @Override
-            public @Nullable String validate(Iterable value) {
-                List errors = new ArrayList<>();
+            public void validate(Iterable value) {
                 for (T t : value) {
-                    String compValidation = PropertyConstraint.this.validate(t);
-                    if (compValidation != null) {
-                        errors.add(compValidation);
-                    }
+                    PropertyConstraint.this.validate(t);
                 }
-                return errors.isEmpty() ? null
-                                        : String.join(", ", errors);
             }
 
             @Override
@@ -118,8 +108,10 @@ public interface PropertyConstraint {
         return new PropertyConstraint() {
 
             @Override
-            public String validate(U value) {
-                return pred.test(value) ? null : "'" + value + "' " + StringUtils.uncapitalize(constraintDescription);
+            public void validate(U value) {
+                if (!pred.test(value)) {
+                    throw new ConstraintViolatedException(this, value);
+                }
             }
 
             @Override
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
index 3f4559a829..160aea4e5a 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptor.java
@@ -57,8 +57,7 @@ public final class PropertyDescriptor {
 
         PropertyParsingUtil.checkConstraintsThrow(
             defaultValue,
-            parser.getConstraints(),
-            ConstraintViolatedException::new
+            parser.getConstraints()
         );
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
index ce37988557..1400f25e7b 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
@@ -15,8 +15,6 @@ import java.util.regex.Pattern;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
-import org.checkerframework.checker.nullness.qual.Nullable;
-
 import net.sourceforge.pmd.internal.util.IteratorUtil;
 import net.sourceforge.pmd.util.internal.xml.XmlUtil;
 
@@ -81,32 +79,22 @@ final class PropertyParsingUtil {
      * Checks the result of the constraints defined by this mapper on
      * the given element. Returns all failures as a list of strings.
      */
-    public static  List checkConstraints(T t, List> constraints) {
-        List failures = new ArrayList<>();
+    public static  void checkConstraintsThrow(T t, List> constraints) {
+        ConstraintViolatedException exception = null;
         for (PropertyConstraint constraint : constraints) {
-            String validationResult = constraint.validate(t);
-            if (validationResult != null) {
-                failures.add(validationResult);
+            try {
+                constraint.validate(t);
+            } catch (ConstraintViolatedException e) {
+                if (exception == null) {
+                    exception = e;
+                } else {
+                    exception.addSuppressed(e);
+                }
             }
         }
-        return failures;
-    }
 
-    public static @Nullable  String checkConstraintsJoin(T t, List> constraints) {
-        List failures = checkConstraints(t, constraints);
-        if (!failures.isEmpty()) {
-            return String.join(", ", failures);
-        }
-        return null;
-    }
-
-    public static  void checkConstraintsThrow(T t,
-                                                 List> constraints,
-                                                 Function exceptionMaker) {
-
-        String failures = checkConstraintsJoin(t, constraints);
-        if (failures != null) {
-            throw exceptionMaker.apply(failures);
+        if (exception != null) {
+            throw exception;
         }
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java
index fe13e09fdd..5192e458d8 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ValueSyntax.java
@@ -75,11 +75,7 @@ class ValueSyntax extends PropertySerializer {
         return new ValueSyntax<>(
             toString,
             s -> {
-                // this is the crucial place where constraints are applied.
-                String error = checker.validate(s);
-                if (error != null) {
-                    throw new ConstraintViolatedException(error);
-                }
+                checker.validate(s);
                 return fromString.apply(s);
             },
             listOf(docConstraint)
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index 544bf057e3..01284449ab 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -34,6 +34,7 @@ import net.sourceforge.pmd.lang.Language;
 import net.sourceforge.pmd.lang.LanguageRegistry;
 import net.sourceforge.pmd.lang.LanguageVersion;
 import net.sourceforge.pmd.lang.rule.RuleReference;
+import net.sourceforge.pmd.properties.ConstraintViolatedException;
 import net.sourceforge.pmd.properties.PropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
@@ -415,6 +416,8 @@ public class RuleFactory {
 
         try {
             return syntax.fromString(valueStr);
+        } catch (ConstraintViolatedException e) {
+            throw err.at(node).error(e, StringUtil.quoteMessageFormat(e.getMessageWithoutValue()));
         } catch (IllegalArgumentException e) {
             throw err.at(node).error(e);
         }
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java
index 05538f6c2b..a4581aae07 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java
@@ -9,6 +9,8 @@ import static org.hamcrest.Matchers.containsString;
 
 import org.junit.jupiter.api.Test;
 
+import net.sourceforge.pmd.lang.rule.MockRule;
+
 import com.github.stefanbirkner.systemlambda.SystemLambda;
 
 public class RuleSetFactoryMessagesTest extends RulesetFactoryTestBase {
@@ -33,4 +35,27 @@ public class RuleSetFactoryMessagesTest extends RulesetFactoryTestBase {
     }
 
 
+    @Test
+    public void testPropertyConstraintFailure() throws Exception {
+        String log = SystemLambda.tapSystemErr(() -> assertCannotParse(
+            rulesetXml(
+                dummyRule(
+                    properties(
+                        propertyWithValueAttr(MockRule.PROP.name(), "-4")
+                    )
+                )
+            )
+        ));
+
+        assertThat(log, containsString(
+            "Error at dummyRuleset.xml:9:1\n"
+                + " 7| \n"
+                + " 8| \n"
+                + " 9| \n"
+                + " 10| \n"
+                + "                                      ^^^^^ Value should be between 1 and 100"
+        ));
+    }
+
+
 }
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java
index 5167df3e10..5d7ccefdd2 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java
@@ -210,7 +210,7 @@ public class RulesetFactoryTestBase {
     }
 
     protected static @NonNull String propertyWithValueAttr(String name, String valueAttr) {
-        return "\n";
     }
 
     protected static @NonNull String propertyDefWithValueAttr(String name,
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java
index d55e5f2294..5446339b1d 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java
@@ -21,7 +21,10 @@ import net.sourceforge.pmd.properties.PropertyFactory;
  */
 public class MockRule extends AbstractRule {
 
-    private static final PropertyDescriptor PROP = PropertyFactory.intProperty("testIntProperty").desc("testIntProperty").require(inRange(1, 100)).defaultValue(1).build();
+    public static final PropertyDescriptor PROP =
+        PropertyFactory.intProperty("testIntProperty")
+                       .desc("testIntProperty")
+                       .require(inRange(1, 100)).defaultValue(1).build();
 
     public MockRule() {
         super();
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/NumericConstraintsTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/NumericConstraintsTest.java
index 4c848e537e..31a95e39ba 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/NumericConstraintsTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/NumericConstraintsTest.java
@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
+import org.checkerframework.checker.nullness.qual.Nullable;
 import org.junit.jupiter.api.Test;
 
 class NumericConstraintsTest {
@@ -15,40 +16,49 @@ class NumericConstraintsTest {
     @Test
     void testInRangeInteger() {
         PropertyConstraint constraint = NumericConstraints.inRange(1, 10);
-        assertNull(constraint.validate(1));
-        assertNull(constraint.validate(5));
-        assertNull(constraint.validate(10));
-        assertNotNull(constraint.validate(0));
-        assertEquals("'-1' should be between 1 and 10", constraint.validate(-1));
-        assertNotNull(constraint.validate(11));
-        assertNotNull(constraint.validate(100));
+        assertNull(errorAsString(constraint, 1));
+        assertNull(errorAsString(constraint, 5));
+        assertNull(errorAsString(constraint, 10));
+        assertNotNull(errorAsString(constraint, 0));
+        assertEquals("'-1' should be between 1 and 10", errorAsString(constraint, -1));
+        assertNotNull(errorAsString(constraint, 11));
+        assertNotNull(errorAsString(constraint, 100));
+    }
+
+    private @Nullable  String errorAsString(PropertyConstraint constraint, T value) {
+        try {
+            constraint.validate(value);
+            return null;
+        } catch (ConstraintViolatedException e) {
+            return e.getMessage();
+        }
     }
 
     @Test
     void testInRangeDouble() {
         PropertyConstraint constraint = NumericConstraints.inRange(1.0, 10.0);
-        assertNull(constraint.validate(1.0));
-        assertNull(constraint.validate(5.5));
-        assertNull(constraint.validate(10.0));
-        assertNotNull(constraint.validate(0.0));
-        assertNotNull(constraint.validate(-1.0));
-        assertNotNull(constraint.validate(11.1));
-        assertNotNull(constraint.validate(100.0));
+        assertNull(errorAsString(constraint, 1.0));
+        assertNull(errorAsString(constraint, 5.5));
+        assertNull(errorAsString(constraint, 10.0));
+        assertNotNull(errorAsString(constraint, 0.0));
+        assertNotNull(errorAsString(constraint, -1.0));
+        assertNotNull(errorAsString(constraint, 11.1));
+        assertNotNull(errorAsString(constraint, 100.0));
     }
 
     @Test
     void testPositive() {
         PropertyConstraint constraint = NumericConstraints.positive();
-        assertNull(constraint.validate(1));
-        assertNull(constraint.validate(1.5f));
-        assertNull(constraint.validate(1.5d));
-        assertNull(constraint.validate(100));
-        assertNotNull(constraint.validate(0));
-        assertEquals("'0.1' should be positive", constraint.validate(0.1f));
-        assertNotNull(constraint.validate(0.9d));
-        assertNotNull(constraint.validate(-1));
-        assertNotNull(constraint.validate(-100));
-        assertNotNull(constraint.validate(-0.1f));
-        assertNotNull(constraint.validate(-0.1d));
+        assertNull(errorAsString(constraint, 1));
+        assertNull(errorAsString(constraint, 1.5f));
+        assertNull(errorAsString(constraint, 1.5d));
+        assertNull(errorAsString(constraint, 100));
+        assertNotNull(errorAsString(constraint, 0));
+        assertEquals("'0.1' should be positive", errorAsString(constraint, 0.1f));
+        assertNotNull(errorAsString(constraint, 0.9d));
+        assertNotNull(errorAsString(constraint, -1));
+        assertNotNull(errorAsString(constraint, -100));
+        assertNotNull(errorAsString(constraint, -0.1f));
+        assertNotNull(errorAsString(constraint, -0.1d));
     }
 }

From ff79d2e78f773ed120add60068320b0eeed88cde Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Wed, 27 Jul 2022 23:08:43 +0200
Subject: [PATCH 086/347] Fix build

---
 .../src/main/java/net/sourceforge/pmd/RuleSetWriter.java  | 2 +-
 .../sourceforge/pmd/properties/ConstraintDecorator.java   | 8 ++++----
 .../sourceforge/pmd/properties/PropertyParsingUtil.java   | 4 +++-
 .../net/sourceforge/pmd/RuleSetFactoryMessagesTest.java   | 6 +-----
 .../JUnitTestContainsTooManyAssertsRule.java              | 2 +-
 .../java/rule/errorprone/AvoidDuplicateLiteralsRule.java  | 2 +-
 .../pmd/lang/java/rule/design/xml/NPathComplexity.xml     | 4 ++--
 7 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
index 4f5e54e6c2..7fa1ec2b57 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java
@@ -34,9 +34,9 @@ import net.sourceforge.pmd.lang.Language;
 import net.sourceforge.pmd.lang.LanguageVersion;
 import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
+import net.sourceforge.pmd.properties.PropertySerializer;
 import net.sourceforge.pmd.properties.PropertySource;
 import net.sourceforge.pmd.properties.PropertyTypeId;
-import net.sourceforge.pmd.properties.PropertySerializer;
 import net.sourceforge.pmd.util.IOUtil;
 import net.sourceforge.pmd.util.internal.xml.SchemaConstants;
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
index b6ebef0adb..7c42c30d0f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/ConstraintDecorator.java
@@ -51,9 +51,9 @@ class ConstraintDecorator extends PropertySerializer {
 
     @Override
     public String toString() {
-        return "ConstraintDecorator{" +
-            "propertySerializer=" + propertySerializer +
-            ", constraints=" + constraints +
-            '}';
+        return "ConstraintDecorator{"
+            + "propertySerializer=" + propertySerializer
+            + ", constraints=" + constraints
+            + '}';
     }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
index 1400f25e7b..87eec296fa 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
@@ -68,7 +68,9 @@ final class PropertyParsingUtil {
         return ValueSyntax.create(
             opt -> opt.map(itemSyntax::toString).orElse(missingValue),
             str -> {
-                if (str.equals(missingValue)) return Optional.empty();
+                if (str.equals(missingValue)) {
+                    return Optional.empty();
+                }
                 return Optional.of(itemSyntax.fromString(str));
             }
         );
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java
index a4581aae07..973b54ac1a 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java
@@ -48,11 +48,7 @@ public class RuleSetFactoryMessagesTest extends RulesetFactoryTestBase {
         ));
 
         assertThat(log, containsString(
-            "Error at dummyRuleset.xml:9:1\n"
-                + " 7| \n"
-                + " 8| \n"
-                + " 9| \n"
-                + " 10| \n"
+            " 10| \n"
                 + "                                      ^^^^^ Value should be between 1 and 100"
         ));
     }
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java
index 104db6415c..bfb958878a 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java
@@ -9,9 +9,9 @@ import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
 import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil;
+import net.sourceforge.pmd.properties.NumericConstraints;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.PropertyFactory;
-import net.sourceforge.pmd.properties.NumericConstraints;
 
 public class JUnitTestContainsTooManyAssertsRule extends AbstractJavaRulechainRule {
 
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java
index 910493cba8..12bb48cc35 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java
@@ -4,10 +4,10 @@
 
 package net.sourceforge.pmd.lang.java.rule.errorprone;
 
+import static net.sourceforge.pmd.properties.NumericConstraints.positive;
 import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty;
 import static net.sourceforge.pmd.properties.PropertyFactory.intProperty;
 import static net.sourceforge.pmd.properties.PropertyFactory.stringProperty;
-import static net.sourceforge.pmd.properties.NumericConstraints.positive;
 
 import java.util.Collections;
 import java.util.HashMap;
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/NPathComplexity.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/NPathComplexity.xml
index 2ada769e42..f5ec1b249a 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/NPathComplexity.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/NPathComplexity.xml
@@ -67,10 +67,10 @@ public class Foo {
 
     
         Test default report level - report 200
-        0
+        1
         1
         
-            The method 'bar()' has an NPath complexity of 200, current threshold is 0
+            The method 'bar()' has an NPath complexity of 200, current threshold is 1
         
         
Date: Sat, 26 Nov 2022 18:25:46 +0100
Subject: [PATCH 087/347] Cleanups

---
 pmd-core/src/main/java/net/sourceforge/pmd/Rule.java        | 1 -
 .../net/sourceforge/pmd/internal/util/IteratorUtil.java     | 6 +++++-
 .../net/sourceforge/pmd/properties/PropertyBuilder.java     | 2 +-
 .../net/sourceforge/pmd/properties/PropertyFactory.java     | 5 +++--
 4 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
index 51c3ec3b43..4c2f0ed65d 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java
@@ -44,7 +44,6 @@ public interface Rule extends PropertySource {
      * Name of the property to universally suppress violations on nodes which
      * match a given relative XPath expression.
      */
-    // TODO 7.0.0 use PropertyDescriptor>
     PropertyDescriptor> VIOLATION_SUPPRESS_XPATH_DESCRIPTOR =
         PropertyFactory.stringProperty("violationSuppressXPath")
                        .desc("Suppress violations on nodes which match a given relative XPath expression.")
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java
index cf6d9f51a0..92312c6035 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java
@@ -6,6 +6,7 @@ package net.sourceforge.pmd.internal.util;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -503,7 +504,10 @@ public final class IteratorUtil {
         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iter, 0), false);
     }
 
-    public static  Stream stream(Iterable iter) {
+    public static  Stream toStream(Iterable iter) {
+        if (iter instanceof Collection) {
+            return ((Collection) iter).stream();
+        }
         return StreamSupport.stream(iter.spliterator(), false);
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index 353f3bc46c..0466e44fd5 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -439,7 +439,7 @@ public abstract class PropertyBuilder, T> {
 
 
         private C getDefaultValue(Iterable list) {
-            return IteratorUtil.stream(list).collect(collector);
+            return IteratorUtil.toStream(list).collect(collector);
         }
 
         @Override
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
index c842518edd..64cbd41bb1 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
@@ -320,7 +320,8 @@ public final class PropertyFactory {
 
     /**
      * Returns a builder for an enumerated property for the given enum
-     * class, using the name of its enum constants as labels.
+     * class, using the {@link Object#toString() toString} of its enum
+     * constants as labels.
      *
      * @param name      Property name
      * @param enumClass Enum class
@@ -329,7 +330,7 @@ public final class PropertyFactory {
      * @return A new builder
      */
     public static > GenericPropertyBuilder enumProperty(String name, Class enumClass) {
-        return enumProperty(name, enumClass, Enum::name);
+        return enumProperty(name, enumClass, Object::toString);
     }
 
     /**

From 552b9e4bc0dbef7532677d0c1a5502231ef1197b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Sun, 5 Feb 2023 19:59:17 +0100
Subject: [PATCH 088/347] Find problem with delimiter

---
 .../sourceforge/pmd/RuleSetWriterTest.java    | 19 ++++++++++++
 .../sourceforge/pmd/TestRulesetProperties.xml | 29 +++++++++++++++++++
 .../pmd/lang/java/RuleSetFactoryTest.java     | 20 -------------
 .../pmd/AbstractRuleSetFactoryTest.java       |  3 +-
 4 files changed, 50 insertions(+), 21 deletions(-)
 create mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml

diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java
index 27c68f645d..3d88a33c27 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java
@@ -4,6 +4,10 @@
 
 package net.sourceforge.pmd;
 
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.ByteArrayOutputStream;
@@ -87,4 +91,19 @@ class RuleSetWriterTest {
         String written = out.toString("UTF-8");
         assertTrue(written.contains("ref=\"rulesets/dummy/basic.xml/DummyBasicMockRule\""));
     }
+
+    @Test
+    void testXmlPropertyWithDelimiter() throws Exception {
+        RuleSet rs = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRulesetProperties.xml");
+
+        Rule rule = rs.getRuleByName("MockRule4");
+        assertEquals(listOf("bar", "foo"), rule.getProperty(rule.getPropertyDescriptor("stringList")));
+        assertEquals(listOf("bar", "foo"), rule.getProperty(rule.getPropertyDescriptor("stringListWithDelim")));
+
+        writer.write(rs);
+
+        String written = out.toString("UTF-8");
+        assertThat(written,  containsString("delimiter="));
+    }
+
 }
diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml
new file mode 100644
index 0000000000..7301b6185f
--- /dev/null
+++ b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml
@@ -0,0 +1,29 @@
+
+
+
+    
+  Ruleset used by test net.sourceforge.pmd.RuleSetWriter and RuleSetFactoryTest
+  
+
+    
+        
+            Just for test
+        
+        3
+        
+            
+            
+            
+            
+        
+        
+            
+        
+    
+
+
+
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/RuleSetFactoryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/RuleSetFactoryTest.java
index 4efb26bb28..d6cafa265a 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/RuleSetFactoryTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/RuleSetFactoryTest.java
@@ -4,31 +4,11 @@
 
 package net.sourceforge.pmd.lang.java;
 
-import static org.junit.jupiter.api.Assertions.assertNull;
-
-import org.junit.jupiter.api.Test;
-
 import net.sourceforge.pmd.AbstractRuleSetFactoryTest;
-import net.sourceforge.pmd.Rule;
-import net.sourceforge.pmd.RuleSet;
-import net.sourceforge.pmd.RuleSetLoader;
 
 /**
  * Test java's rulesets
  */
 class RuleSetFactoryTest extends AbstractRuleSetFactoryTest {
 
-    @Test
-    void testExclusionOfUselessParantheses() {
-        RuleSet ruleset = new RuleSetLoader().loadFromString("",
-                                                             "\n" + "\n"
-                        + "  Custom ruleset for tests\n"
-                        + "  \n"
-                        + "    \n" + "  \n" + "\n");
-        Rule rule = ruleset.getRuleByName("UselessParentheses");
-        assertNull(rule);
-    }
 }
diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java
index 06c79b7aee..5cebc2accd 100644
--- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java
+++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java
@@ -503,7 +503,8 @@ public abstract class AbstractRuleSetFactoryTest {
                     value1 = ((Pattern) value1).pattern();
                     value2 = ((Pattern) value2).pattern();
                 }
-                assertEquals(value1, value2, message + ", Rule property value " + j);
+                assertEquals(value1, value2, message + ", Rule " + rule1.getName() + " property "
+                    + propertyDescriptors1.get(j).name());
             }
             assertEquals(propertyDescriptors1.size(), propertyDescriptors2.size(),
                     message + ", Rule property descriptor count");

From ebe46103895b4c44e9fbcd466ce029a8df94dcd4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Sun, 5 Feb 2023 20:09:56 +0100
Subject: [PATCH 089/347] Remove ability to set custom delimiter

---
 .../pmd/properties/PropertyBuilder.java       | 25 +---------
 .../pmd/properties/PropertyFactory.java       | 19 ++++----
 .../pmd/properties/PropertyParsingUtil.java   | 20 ++++----
 .../sourceforge/pmd/rules/RuleFactory.java    | 21 ++-------
 .../util/internal/xml/XmlErrorMessages.java   |  1 +
 .../sourceforge/pmd/RuleSetFactoryTest.java   | 47 ++++++++++---------
 .../sourceforge/pmd/RuleSetWriterTest.java    | 18 -------
 .../properties/PropertyDescriptorTest.java    | 24 +++++-----
 .../pmd/properties/PropertySyntaxTest.java    |  2 +-
 .../renderers/CodeClimateRendererTest.java    |  2 +-
 .../sourceforge/pmd/TestRulesetProperties.xml | 29 ------------
 .../bestpractices/GuardLogStatementRule.java  |  3 +-
 .../UseTryWithResourcesRule.java              |  1 -
 .../java/rule/design/InvalidJavaBeanRule.java |  1 -
 .../rule/design/LoosePackageCouplingRule.java |  4 +-
 .../AvoidDuplicateLiteralsRule.java           |  1 -
 .../rule/errorprone/CloseResourceRule.java    |  4 +-
 .../lang/java/metrics/impl/xml/CycloTest.xml  |  2 +-
 .../xml/AvoidUsingHardCodedIP.xml             |  2 +-
 .../bestpractices/xml/UnusedPrivateField.xml  |  2 +-
 .../codestyle/xml/FieldNamingConventions.xml  |  2 +-
 .../AvoidBranchingStatementAsLastInLoop.xml   |  6 +--
 .../sourceforge/pmd/lang/vf/VfHandler.java    |  2 -
 .../resources/category/pom/errorprone.xml     |  2 +-
 24 files changed, 78 insertions(+), 162 deletions(-)
 delete mode 100644 pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
index 65cc783c29..53303531e5 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyBuilder.java
@@ -18,7 +18,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.checkerframework.checker.nullness.qual.NonNull;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
-import net.sourceforge.pmd.annotation.InternalApi;
 import net.sourceforge.pmd.util.AssertionUtil;
 import net.sourceforge.pmd.util.CollectionUtil;
 import net.sourceforge.pmd.util.IteratorUtil;
@@ -423,7 +422,6 @@ public abstract class PropertyBuilder, T> {
         private PropertySerializer itemParser;
         private final Collector collector;
         private final List> collectionConstraints = new ArrayList<>();
-        private char multiValueDelimiter = PropertyFactory.DEFAULT_DELIMITER;
 
 
         /**
@@ -461,27 +459,6 @@ public abstract class PropertyBuilder, T> {
             return this;
         }
 
-        /**
-         * Specify a delimiter character. By default it's {@value PropertyFactory#DEFAULT_DELIMITER}.
-         * This is only used for properties that are parsed from a value attribute.
-         * If the item type is not parsable from a string, then the delimiter
-         * is ignored as the property can only be parsed using the {@code } syntax.
-         *
-         * @param delim Delimiter
-         *
-         * @return The same builder
-         */
-        public GenericCollectionPropertyBuilder delim(char delim) {
-            this.multiValueDelimiter = delim;
-            return this;
-        }
-
-
-        @InternalApi
-        public char getMultiValueDelimiter() {
-            return multiValueDelimiter;
-        }
-
 
         /**
          * Specify default values. To specify an empty
@@ -524,7 +501,7 @@ public abstract class PropertyBuilder, T> {
 
         @Override
         public PropertyDescriptor build() {
-            PropertySerializer syntax = PropertyParsingUtil.delimitedString(itemParser, collector, multiValueDelimiter);
+            PropertySerializer syntax = PropertyParsingUtil.delimitedString(itemParser, collector);
             syntax = PropertyParsingUtil.withAllConstraints(syntax, CollectionUtil.map(itemParser.getConstraints(), PropertyConstraint::toCollectionConstraint));
             syntax = PropertyParsingUtil.withAllConstraints(syntax, collectionConstraints);
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
index 64cbd41bb1..a989c1714c 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyFactory.java
@@ -83,12 +83,13 @@ import net.sourceforge.pmd.util.CollectionUtil;
 public final class PropertyFactory {
 
 
-    /** Default delimiter for all properties. */
-    public static final char DEFAULT_DELIMITER = '|';
-
-
-    /** Default delimiter for numeric properties. */
-    public static final char DEFAULT_NUMERIC_DELIMITER = ',';
+    /**
+     * Default delimiter for all properties. Note that in PMD 6 this was
+     * the pipe character {@code |}, while now it is {@value}. In PMD 6,
+     * numeric properties had a different delimiter, whereas in PMD 7, all
+     * properties have the same delimiter.
+     */
+    public static final char DEFAULT_DELIMITER = ',';
 
 
     private PropertyFactory() {
@@ -127,7 +128,7 @@ public final class PropertyFactory {
      * @return A new builder
      */
     public static GenericCollectionPropertyBuilder> intListProperty(String name) {
-        return intProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER);
+        return intProperty(name).toList();
     }
 
 
@@ -164,7 +165,7 @@ public final class PropertyFactory {
      * @return A new builder
      */
     public static GenericCollectionPropertyBuilder> longIntListProperty(String name) {
-        return longIntProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER);
+        return longIntProperty(name).toList();
     }
 
 
@@ -196,7 +197,7 @@ public final class PropertyFactory {
      * @return A new builder
      */
     public static GenericCollectionPropertyBuilder> doubleListProperty(String name) {
-        return doubleProperty(name).toList().delim(DEFAULT_NUMERIC_DELIMITER);
+        return doubleProperty(name).toList();
     }
 
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
index d431d82973..1f6052bf33 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyParsingUtil.java
@@ -53,11 +53,11 @@ final class PropertyParsingUtil {
 
 
     private static  PropertySerializer> numberList(ValueSyntax valueSyntax) {
-        return delimitedString(valueSyntax, Collectors.toList(), PropertyFactory.DEFAULT_NUMERIC_DELIMITER);
+        return delimitedString(valueSyntax, Collectors.toList());
     }
 
     private static  PropertySerializer> otherList(ValueSyntax valueSyntax) {
-        return delimitedString(valueSyntax, Collectors.toList(), /* prefer old syntax for now */  PropertyFactory.DEFAULT_DELIMITER);
+        return delimitedString(valueSyntax, Collectors.toList() /* prefer old syntax for now */);
     }
 
     private static  Function preTrim(Function parser) {
@@ -113,21 +113,19 @@ final class PropertyParsingUtil {
      * Builds an XML syntax that understands a {@code } syntax and
      * a delimited {@code } syntax.
      *
-     * @param itemSyntax      Serializer for the items, must support string mapping
-     * @param collector       Collector to create the collection from strings
-     * @param delimiter       Delimiter for the {@code } syntax
-     * @param              Type of items
-     * @param              Type of collection to handle
+     * @param         Type of items
+     * @param         Type of collection to handle
+     * @param itemSyntax Serializer for the items, must support string mapping
+     * @param collector  Collector to create the collection from strings
      *
      * @throws IllegalArgumentException If the item syntax doesn't support string mapping
      */
     public static > PropertySerializer delimitedString(PropertySerializer itemSyntax,
-                                                                                   Collector collector,
-                                                                                   char delimiter) {
-        String delim = "" + delimiter;
+                                                                                   Collector collector) {
+        String delim = "" + PropertyFactory.DEFAULT_DELIMITER;
         return ValueSyntax.create(
             coll -> IteratorUtil.toStream(coll.iterator()).map(itemSyntax::toString).collect(Collectors.joining(delim)),
-            string -> parseListWithEscapes(string, delimiter, itemSyntax::fromString).stream().collect(collector)
+            string -> parseListWithEscapes(string, PropertyFactory.DEFAULT_DELIMITER, itemSyntax::fromString).stream().collect(collector)
         );
     }
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index d2b477f507..ea8e66cf33 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -36,7 +36,6 @@ import net.sourceforge.pmd.lang.LanguageVersion;
 import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.properties.ConstraintViolatedException;
 import net.sourceforge.pmd.properties.PropertyBuilder;
-import net.sourceforge.pmd.properties.PropertyBuilder.GenericCollectionPropertyBuilder;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.properties.PropertySerializer;
 import net.sourceforge.pmd.properties.PropertyTypeId;
@@ -373,7 +372,10 @@ public class RuleFactory {
             PropertyBuilder builder = factory.newBuilder(name)
                                                    .desc(description)
                                                    .defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper()));
-            setPropDelimiter(propertyElement, err, builder);
+            if (SchemaConstants.DELIMITER.hasAttribute(propertyElement)) {
+                err.at(SchemaConstants.DELIMITER.getAttributeNode(propertyElement))
+                    .warn(XmlErrorMessages.WARN__DELIMITER_DEPRECATED);
+            }
 
             return builder.build();
 
@@ -383,21 +385,6 @@ public class RuleFactory {
         }
     }
 
-    private static void setPropDelimiter(Element propertyElement, PmdXmlReporter err, PropertyBuilder builder) {
-        if (builder instanceof PropertyBuilder.GenericCollectionPropertyBuilder) {
-            String customDelimiter = SchemaConstants.DELIMITER.getAttributeOrNull(propertyElement);
-            if (customDelimiter != null) {
-                if (customDelimiter.length() == 1) {
-                    ((GenericCollectionPropertyBuilder) builder).delim(customDelimiter.charAt(0));
-                } else {
-                    err.at(SchemaConstants.DELIMITER.getAttributeNode(propertyElement))
-                        .error("Delimiter is not a single character, it will be defaulted to ''{0}''",
-                               ((GenericCollectionPropertyBuilder) builder).getMultiValueDelimiter());
-                }
-            }
-        }
-    }
-
     private static  T parsePropertyValue(Element propertyElt, PmdXmlReporter err, PropertySerializer syntax) {
         String valueAttr = PROPERTY_VALUE.getAttributeOrNull(propertyElt);
         Element valueChild = PROPERTY_VALUE.getOptChildIn(propertyElt, err);
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java
index 558c5c5172..0338dd62ea 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java
@@ -39,6 +39,7 @@ public final class XmlErrorMessages {
     public static final String WARN__DEPRECATED_USE_OF_ATTRIBUTE = "The use of the ''{0}'' attribute is deprecated. Use a nested element, e.g. {1}";
     public static final String ERR__INVALID_PRIORITY_VALUE = "Not a valid priority: ''{0}'', expected a number in [1,5]";
     public static final String ERR__UNSUPPORTED_PROPERTY_TYPE = "Unsupported property type ''{0}''";
+    public static final String WARN__DELIMITER_DEPRECATED = "Delimiter attribute is not supported anymore, values are always comma-separated.";
 
     private XmlErrorMessages() {
         // utility class
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java
index b0940021fb..a8582aceb9 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java
@@ -5,6 +5,7 @@
 package net.sourceforge.pmd;
 
 import static net.sourceforge.pmd.PmdCoreTestUtils.dummyLanguage;
+import static net.sourceforge.pmd.util.CollectionUtil.listOf;
 import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.DEPRECATED;
 import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.NAME;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -18,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.InputStream;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -30,6 +30,7 @@ import net.sourceforge.pmd.lang.rule.RuleReference;
 import net.sourceforge.pmd.properties.PropertyDescriptor;
 import net.sourceforge.pmd.util.ResourceLoader;
 import net.sourceforge.pmd.util.internal.xml.SchemaConstants;
+import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages;
 
 import com.github.stefanbirkner.systemlambda.SystemLambda;
 
@@ -177,33 +178,37 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase {
     @Test
     void testStringMultiPropertyDefaultDelimiter() {
         Rule r = loadFirstRule(
-            "\n\n  Desc\n"
-                + "     \n"
-                + "         Please move your class to the right folder(rest \nfolder)\n"
-                + "         2\n         \n             \n         ");
+            rulesetXml(
+                dummyRule(
+                    priority("3"),
+                    properties(
+                        ""
+                    )
+                )
+            ));
         Object propValue = r.getProperty(r.getPropertyDescriptor("packageRegEx"));
 
-        assertEquals(Arrays.asList("com.aptsssss", "com.abc"), propValue);
+        assertEquals(listOf("com.aptsssss", "com.abc"), propValue);
     }
 
     @Test
     void testStringMultiPropertyDelimiter() {
-        Rule r = loadFirstRule("\n" + "\n "
-                + " ruleset desc\n     "
-                + "\n"
-                + "         Please move your class to the right folder(rest \nfolder)\n"
-                + "         2\n         \n             \n"
-                + "         " + "");
-
+        Rule r = loadFirstRule(
+            rulesetXml(
+                dummyRule(
+                    priority("3"),
+                    properties(
+                        ""
+                    )
+                )
+            ));
         Object propValue = r.getProperty(r.getPropertyDescriptor("packageRegEx"));
-        assertEquals(Arrays.asList("com.aptsssss", "com.abc"), propValue);
+
+        assertEquals(listOf("com.aptsssss|com.abc"), propValue);
+
+        verifyFoundAWarningWithMessage(
+            containing(XmlErrorMessages.WARN__DELIMITER_DEPRECATED)
+        );
     }
 
     /**
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java
index 3d88a33c27..a10d514d8f 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java
@@ -4,10 +4,6 @@
 
 package net.sourceforge.pmd;
 
-import static net.sourceforge.pmd.util.CollectionUtil.listOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.ByteArrayOutputStream;
@@ -92,18 +88,4 @@ class RuleSetWriterTest {
         assertTrue(written.contains("ref=\"rulesets/dummy/basic.xml/DummyBasicMockRule\""));
     }
 
-    @Test
-    void testXmlPropertyWithDelimiter() throws Exception {
-        RuleSet rs = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRulesetProperties.xml");
-
-        Rule rule = rs.getRuleByName("MockRule4");
-        assertEquals(listOf("bar", "foo"), rule.getProperty(rule.getPropertyDescriptor("stringList")));
-        assertEquals(listOf("bar", "foo"), rule.getProperty(rule.getPropertyDescriptor("stringListWithDelim")));
-
-        writer.write(rs);
-
-        String written = out.toString("UTF-8");
-        assertThat(written,  containsString("delimiter="));
-    }
-
 }
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
index 589ac5bfd9..97151bd608 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java
@@ -187,23 +187,23 @@ class PropertyDescriptorTest {
         assertEquals("stringListProp", listDescriptor.name());
         assertEquals("hello", listDescriptor.description());
         assertEquals(Arrays.asList("v1", "v2"), listDescriptor.defaultValue());
-        assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("foo|bar"));
-        assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("  foo |  bar  "));
+        assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("foo,bar"));
+        assertEquals(Arrays.asList("foo", "bar"), listDescriptor.valueFrom("  foo ,  bar  "));
     }
 
     private enum SampleEnum { A, B, C }
 
-    private static Map nameMap = new LinkedHashMap<>();
+    private static final Map NAME_MAP = new LinkedHashMap<>();
 
     static {
-        nameMap.put("TEST_A", SampleEnum.A);
-        nameMap.put("TEST_B", SampleEnum.B);
-        nameMap.put("TEST_C", SampleEnum.C);
+        NAME_MAP.put("TEST_A", SampleEnum.A);
+        NAME_MAP.put("TEST_B", SampleEnum.B);
+        NAME_MAP.put("TEST_C", SampleEnum.C);
     }
 
     @Test
     void testEnumProperty() {
-        PropertyDescriptor descriptor = PropertyFactory.enumProperty("enumProp", nameMap)
+        PropertyDescriptor descriptor = PropertyFactory.enumProperty("enumProp", NAME_MAP)
                 .desc("hello")
                 .defaultValue(SampleEnum.B)
                 .build();
@@ -212,20 +212,20 @@ class PropertyDescriptorTest {
         assertEquals(SampleEnum.B, descriptor.defaultValue());
         assertEquals(SampleEnum.C, descriptor.valueFrom("TEST_C"));
 
-        PropertyDescriptor> listDescriptor = PropertyFactory.enumListProperty("enumListProp", nameMap)
+        PropertyDescriptor> listDescriptor = PropertyFactory.enumListProperty("enumListProp", NAME_MAP)
                 .desc("hello")
                 .defaultValues(SampleEnum.A, SampleEnum.B)
                 .build();
         assertEquals("enumListProp", listDescriptor.name());
         assertEquals("hello", listDescriptor.description());
         assertEquals(Arrays.asList(SampleEnum.A, SampleEnum.B), listDescriptor.defaultValue());
-        assertEquals(Arrays.asList(SampleEnum.B, SampleEnum.C), listDescriptor.valueFrom("TEST_B|TEST_C"));
+        assertEquals(Arrays.asList(SampleEnum.B, SampleEnum.C), listDescriptor.valueFrom("TEST_B,TEST_C"));
     }
 
 
     @Test
     void testEnumPropertyNullValueFailsBuild() {
-        Map map = new HashMap<>(nameMap);
+        Map map = new HashMap<>(NAME_MAP);
         map.put("TEST_NULL", null);
 
         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
@@ -236,7 +236,7 @@ class PropertyDescriptorTest {
 
     @Test
     void testEnumListPropertyNullValueFailsBuild() {
-        Map map = new HashMap<>(nameMap);
+        Map map = new HashMap<>(NAME_MAP);
         map.put("TEST_NULL", null);
 
         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
@@ -247,7 +247,7 @@ class PropertyDescriptorTest {
 
     @Test
     void testEnumPropertyInvalidValue() {
-        PropertyDescriptor descriptor = PropertyFactory.enumProperty("enumProp", nameMap)
+        PropertyDescriptor descriptor = PropertyFactory.enumProperty("enumProp", NAME_MAP)
                 .desc("hello")
                 .defaultValue(SampleEnum.B)
                 .build();
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
index 0f3c07384b..81bd3c7e31 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java
@@ -24,7 +24,7 @@ class PropertySyntaxTest extends RulesetFactoryTestBase {
 
     @Test
     void testStringListProp() {
-        assertValueRoundTrip(PropertyParsingUtil.STRING_LIST, "ad|j", listOf("ad", "j"));
+        assertValueRoundTrip(PropertyParsingUtil.STRING_LIST, "ad,j", listOf("ad", "j"));
     }
 
 
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java
index 89ef1588c8..64f8ed897f 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java
@@ -48,7 +48,7 @@ class CodeClimateRendererTest extends AbstractRendererTest {
                 + "violationSuppressRegex | | Suppress violations with messages matching a regular expression\\n"
                 + "violationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\\n"
                 + "stringProperty | the string value\\nsecond line with 'quotes' | simple string property\\n"
-                + "multiString | default1|default2 | multi string property\\n" // todo doesn't the delimiter need escaping?
+                + "multiString | default1,default2 | multi string property\\n"
                 + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}"
                 + "\u0000" + PMD.EOL;
     }
diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml
deleted file mode 100644
index 7301b6185f..0000000000
--- a/pmd-core/src/test/resources/net/sourceforge/pmd/TestRulesetProperties.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-    
-  Ruleset used by test net.sourceforge.pmd.RuleSetWriter and RuleSetFactoryTest
-  
-
-    
-        
-            Just for test
-        
-        3
-        
-            
-            
-            
-            
-        
-        
-            
-        
-    
-
-
-
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java
index c102432ec3..b351be0afa 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java
@@ -67,14 +67,13 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule {
                     .desc("LogLevels to guard")
                     .defaultValues("trace", "debug", "info", "warn", "error",
                                    "log", "finest", "finer", "fine", "info", "warning", "severe")
-                    .delim(',')
                     .build();
 
     private static final PropertyDescriptor> GUARD_METHODS =
             stringListProperty("guardsMethods")
                     .desc("Method use to guard the log statement")
                     .defaultValues("isTraceEnabled", "isDebugEnabled", "isInfoEnabled", "isWarnEnabled", "isErrorEnabled", "isLoggable")
-                    .delim(',').build();
+                    .build();
 
     private final Map guardStmtByLogLevel = new HashMap<>(12);
 
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseTryWithResourcesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseTryWithResourcesRule.java
index c57b4b3790..b59b963202 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseTryWithResourcesRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseTryWithResourcesRule.java
@@ -24,7 +24,6 @@ public final class UseTryWithResourcesRule extends AbstractJavaRulechainRule {
             stringListProperty("closeMethods")
                     .desc("Method names in finally block, which trigger this rule")
                     .defaultValues("close", "closeQuietly")
-                    .delim(',')
                     .build();
 
     public UseTryWithResourcesRule() {
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/InvalidJavaBeanRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/InvalidJavaBeanRule.java
index d7cd73317e..ff37a0d453 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/InvalidJavaBeanRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/InvalidJavaBeanRule.java
@@ -47,7 +47,6 @@ public class InvalidJavaBeanRule extends AbstractJavaRulechainRule {
     private static final PropertyDescriptor> PACKAGES_DESCRIPTOR = stringListProperty("packages")
             .desc("Consider classes in only these package to be beans. Set to an empty value to check all classes.")
             .defaultValues("org.example.beans")
-            .delim(',')
             .build();
 
     private Map properties;
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java
index 4a168d4ea4..20e5d4f7ac 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/LoosePackageCouplingRule.java
@@ -38,10 +38,10 @@ import net.sourceforge.pmd.properties.PropertySource;
 public class LoosePackageCouplingRule extends AbstractJavaRule {
 
     private static final PropertyDescriptor> PACKAGES_DESCRIPTOR =
-            stringListProperty("packages").desc("Restricted packages").emptyDefaultValue().delim(',').build();
+            stringListProperty("packages").desc("Restricted packages").emptyDefaultValue().build();
 
     private static final PropertyDescriptor> CLASSES_DESCRIPTOR =
-            stringListProperty("classes").desc("Allowed classes").emptyDefaultValue().delim(',').build();
+            stringListProperty("classes").desc("Allowed classes").emptyDefaultValue().build();
 
     // The package of this source file
     private String thisPackage;
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java
index e3bb4ae40b..df4f69a9f6 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDuplicateLiteralsRule.java
@@ -46,7 +46,6 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRulechainRule {
                                           + "Components of this list should not be surrounded by double quotes.")
                          .map(Collectors.toSet())
                          .defaultValue(Collections.emptySet())
-                         .delim(',')
                          .build();
 
     private Map> literals = new HashMap<>();
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java
index a56e86bb28..8f561ea2eb 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java
@@ -81,13 +81,13 @@ public class CloseResourceRule extends AbstractJavaRule {
             stringListProperty("closeTargets")
                            .desc("Methods which may close this resource")
                            .emptyDefaultValue()
-                           .delim(',').build();
+                           .build();
 
     private static final PropertyDescriptor> TYPES_DESCRIPTOR =
             stringListProperty("types")
                     .desc("Affected types")
                     .defaultValues("java.lang.AutoCloseable", "java.sql.Connection", "java.sql.Statement", "java.sql.ResultSet")
-                    .delim(',').build();
+                    .build();
 
     private static final PropertyDescriptor USE_CLOSE_AS_DEFAULT_TARGET =
             booleanProperty("closeAsDefaultTarget")
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml
index a589c70dfb..c60ffeca93 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/CycloTest.xml
@@ -136,7 +136,7 @@ public class Complicated {
 
     
         Full example - considerAssert + ignoreBooleanPaths
-        ignoreBooleanPaths|considerAssert
+        ignoreBooleanPaths,considerAssert
         8
         
             'Complicated#exception()' has value 4.
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml
index d9cfb323e8..0253557f28 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AvoidUsingHardCodedIP.xml
@@ -146,7 +146,7 @@ public class Foo {
 
     
         Comprehensive, check for IPv6 and IPv4 mapped IPv6
-        IPv6|IPv4 mapped IPv6
+        IPv6,IPv4 mapped IPv6
         15
         
     
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml
index ee6b069fb3..c207f45897 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml
@@ -801,7 +801,7 @@ class ClassWithUnusedField {
 
     
         [java] UnusedPrivateField doesn't find annotated unused private fields anymore #4166 (configuration)
-        java.lang.Deprecated|org.openqa.selenium.support.FindBy
+        java.lang.Deprecated,org.openqa.selenium.support.FindBy
         2
         6,9
         
         More exclusions can be configured
-        m$mangled|serialVersionUID
+        m$mangled,serialVersionUID
         0
         
         violations: break:for/do/while
-        for|do|while
+        for,do,while
         
         
         4
@@ -149,7 +149,7 @@ public class Foo {
     
         violations: continue:for/do/while
         
-        for|do|while
+        for,do,while
         
         4
         11,20,29,38
@@ -160,7 +160,7 @@ public class Foo {
         violations: return:for/do/while
         
         
-        for|do|while
+        for,do,while
         4
         5,14,23,32
         
diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java
index 6a3858f6b6..848b712fc8 100644
--- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java
+++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java
@@ -29,7 +29,6 @@ public class VfHandler extends AbstractPmdLanguageVersionHandler {
         PropertyFactory.stringListProperty("apexDirectories")
                        .desc("Location of Apex Class directories. Absolute or relative to the Visualforce directory.")
                        .defaultValue(DEFAULT_APEX_DIRECTORIES)
-                       .delim(',')
                        .build();
 
     /**
@@ -41,7 +40,6 @@ public class VfHandler extends AbstractPmdLanguageVersionHandler {
         PropertyFactory.stringListProperty("objectsDirectories")
                        .desc("Location of Custom Object directories. Absolute or relative to the Visualforce directory.")
                        .defaultValue(DEFAULT_OBJECT_DIRECTORIES)
-                       .delim(',')
                        .build();
 
     @Override
diff --git a/pmd-xml/src/main/resources/category/pom/errorprone.xml b/pmd-xml/src/main/resources/category/pom/errorprone.xml
index 66c6e3c2d2..b0193f4403 100644
--- a/pmd-xml/src/main/resources/category/pom/errorprone.xml
+++ b/pmd-xml/src/main/resources/category/pom/errorprone.xml
@@ -31,7 +31,7 @@ The following types are considered valid: pom, jar, maven-plugin, ejb, war, ear,
                 
             
             
         

From 13cd1e4a5b458c9585fea36e323866836a43a9ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Sun, 5 Feb 2023 20:45:48 +0100
Subject: [PATCH 090/347] Update tests in doc module

---
 pmd-doc/src/test/resources/expected/sample.md | 192 +++++-------------
 1 file changed, 48 insertions(+), 144 deletions(-)

diff --git a/pmd-doc/src/test/resources/expected/sample.md b/pmd-doc/src/test/resources/expected/sample.md
index afded95cd6..e8a7016e4d 100644
--- a/pmd-doc/src/test/resources/expected/sample.md
+++ b/pmd-doc/src/test/resources/expected/sample.md
@@ -93,33 +93,15 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-            the value
-        
-        
-            Value1,Value2
-        
-        
-            \/\*\s+(default|package)\s+\*\/
-        
-        
-            [a-z]*
-        
-        
-            \s+
-        
-        
-            _dd_
-        
-        
-            [0-9]{1,3}
-        
-        
-            \b
-        
-        
-            \n
-        
+        
+        
+        
+        
+        
+        
+        
+        
+        
     
 
 ```
@@ -284,33 +266,15 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-            the value
-        
-        
-            Value1,Value2
-        
-        
-            \/\*\s+(default|package)\s+\*\/
-        
-        
-            [a-z]*
-        
-        
-            \s+
-        
-        
-            _dd_
-        
-        
-            [0-9]{1,3}
-        
-        
-            \b
-        
-        
-            \n
-        
+        
+        
+        
+        
+        
+        
+        
+        
+        
     
 
 ```
@@ -375,33 +339,15 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-            the value
-        
-        
-            Value1,Value2
-        
-        
-            \/\*\s+(default|package)\s+\*\/
-        
-        
-            [a-z]*
-        
-        
-            \s+
-        
-        
-            _dd_
-        
-        
-            [0-9]{1,3}
-        
-        
-            \b
-        
-        
-            \n
-        
+        
+        
+        
+        
+        
+        
+        
+        
+        
     
 
 ```
@@ -468,33 +414,15 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-            the value
-        
-        
-            Value1,Value2
-        
-        
-            \/\*\s+(default|package)\s+\*\/
-        
-        
-            [a-z]*
-        
-        
-            \s+
-        
-        
-            _dd_
-        
-        
-            [0-9]{1,3}
-        
-        
-            \b
-        
-        
-            \n
-        
+        
+        
+        
+        
+        
+        
+        
+        
+        
     
 
 ```
@@ -561,33 +489,15 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i
 ``` xml
 
     
-        
-            the value
-        
-        
-            Value1,Value2
-        
-        
-            \/\*\s+(default|package)\s+\*\/
-        
-        
-            [a-z]*
-        
-        
-            \s+
-        
-        
-            _dd_
-        
-        
-            [0-9]{1,3}
-        
-        
-            \b
-        
-        
-            \n
-        
+        
+        
+        
+        
+        
+        
+        
+        
+        
     
 
 ```
@@ -669,15 +579,9 @@ if (0 > 1 && 0 < 1) {
 ``` xml
 
     
-        
-            \/\*\s+(default|package)\s+\*\/
-        
-        
-            <script>alert('XSS');</script>
-        
-        
-            this is escaped: |
-        
+        
+        
+        
     
 
 ```

From d5819db600721889562829f97670611f24554de0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Sun, 5 Feb 2023 21:00:58 +0100
Subject: [PATCH 091/347] Cleanup

---
 .../java/net/sourceforge/pmd/properties/PropertyTypeId.java  | 3 +++
 .../src/main/java/net/sourceforge/pmd/rules/RuleFactory.java | 5 +----
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java
index 88b9befd86..4454f16ee7 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java
@@ -9,6 +9,8 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.function.Function;
 
+import net.sourceforge.pmd.annotation.InternalApi;
+
 
 /**
  * Enumerates the properties that can be built from the XML. Defining a property in
@@ -25,6 +27,7 @@ import java.util.function.Function;
  * @author Clรฉment Fournier
  * @since 6.0.0
  */
+@InternalApi
 public enum PropertyTypeId {
     // These are exclusively used for XPath rules. It would make more sense to model the supported
     // property types around XML Schema Datatypes (XSD) 1.0 or 1.1 instead of Java datatypes (save for
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
index ea8e66cf33..4c6639802e 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java
@@ -351,10 +351,7 @@ public class RuleFactory {
         PropertyTypeId factory = PropertyTypeId.lookupMnemonic(typeId);
         if (factory == null) {
             throw err.at(PROPERTY_TYPE.getAttributeNode(propertyElement))
-                     .error(
-                         "Unsupported property type ''{0}''",
-                         typeId
-                     );
+                     .error(XmlErrorMessages.ERR__UNSUPPORTED_PROPERTY_TYPE, typeId);
         }
 
         return propertyDefCapture(propertyElement, err, factory.getBuilderUtils());

From d4c05d1fb5073f5375ce81a2b53926dea95c56e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= 
Date: Fri, 10 Feb 2023 16:24:11 +0100
Subject: [PATCH 092/347] Make pmd language have a hook to launch CPD

This allows removing the CPD language instances,
sharing more logic between CPD and PMD, and using
language properties to configure CPD and PMD uniformly.
---
 .../sourceforge/pmd/cpd/ApexTokenizer.java    |  3 +-
 .../pmd/cpd/AbstractTokenizer.java            |  4 +-
 .../net/sourceforge/pmd/cpd/AnyTokenizer.java |  3 +-
 .../net/sourceforge/pmd/cpd/Tokenizer.java    | 38 ++++++++++++-
 .../pmd/cpd/internal/AntlrTokenizer.java      | 46 +++------------
 .../pmd/cpd/internal/JavaCCTokenizer.java     | 55 +-----------------
 .../pmd/cpd/internal/TokenizerBase.java       | 42 ++++++++++++++
 .../pmd/cpd/token/TokenFilter.java            |  4 +-
 .../cpd/token/internal/BaseTokenFilter.java   |  2 +-
 .../pmd/lang/CpdOnlyLanguageModuleBase.java   | 28 +++++++++
 .../net/sourceforge/pmd/lang/Language.java    | 34 ++++++++++-
 .../pmd/lang/LanguageRegistry.java            |  1 +
 .../pmd/lang/ast/impl/javacc/CharStream.java  |  9 +++
 .../lang/impl/SimpleLanguageModuleBase.java   |  7 ++-
 .../net/sourceforge/pmd/cpd/CPPLanguage.java  | 32 -----------
 .../net/sourceforge/pmd/cpd/CPPTokenizer.java | 57 +++++++------------
 .../pmd/lang/cpp/CppLanguageModule.java       | 56 ++++++++++++++++++
 .../services/net.sourceforge.pmd.cpd.Language |  1 -
 .../net.sourceforge.pmd.lang.Language         |  1 +
 .../pmd/cpd/CppCharStreamTest.java            | 12 ++--
 .../net/sourceforge/pmd/cpd/CsLanguage.java   | 28 ---------
 .../net/sourceforge/pmd/cpd/CsTokenizer.java  | 38 +++++--------
 .../pmd/lang/cs/CsLanguageModule.java         | 42 ++++++++++++++
 .../services/net.sourceforge.pmd.cpd.Language |  1 -
 .../net.sourceforge.pmd.lang.Language         |  1 +
 .../sourceforge/pmd/cpd/DartTokenizer.java    |  9 +--
 .../sourceforge/pmd/cpd/GroovyTokenizer.java  |  3 +-
 .../pmd/lang/html/ast/HtmlTokenizer.java      |  3 +-
 .../sourceforge/pmd/cpd/JavaTokenizer.java    | 16 ++----
 .../pmd/cpd/EcmascriptTokenizer.java          |  5 +-
 .../net/sourceforge/pmd/cpd/JSPTokenizer.java | 11 +---
 .../sourceforge/pmd/cpd/MatlabTokenizer.java  |  6 +-
 .../pmd/cpd/ModelicaTokenizer.java            |  8 +--
 .../pmd/cpd/ObjectiveCTokenizer.java          |  6 +-
 .../net/sourceforge/pmd/cpd/PHPTokenizer.java |  4 +-
 .../sourceforge/pmd/cpd/PLSQLTokenizer.java   |  6 +-
 .../sourceforge/pmd/cpd/PythonTokenizer.java  | 11 +---
 .../sourceforge/pmd/cpd/ScalaTokenizer.java   |  2 +-
 .../net/sourceforge/pmd/cpd/VfTokenizer.java  |  5 +-
 39 files changed, 357 insertions(+), 283 deletions(-)
 create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java
 create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java
 delete mode 100644 pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPLanguage.java
 create mode 100644 pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java
 delete mode 100644 pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
 create mode 100644 pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
 delete mode 100644 pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsLanguage.java
 create mode 100644 pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java
 delete mode 100644 pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
 create mode 100644 pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language

diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java
index afe8db0b3f..d2a01abb01 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java
@@ -13,6 +13,7 @@ import org.antlr.runtime.Token;
 
 import net.sourceforge.pmd.lang.apex.ApexJorjeLogging;
 import net.sourceforge.pmd.lang.ast.TokenMgrError;
+import net.sourceforge.pmd.lang.document.TextDocument;
 
 import apex.jorje.parser.impl.ApexLexer;
 
@@ -35,7 +36,7 @@ public class ApexTokenizer implements Tokenizer {
     }
 
     @Override
-    public void tokenize(SourceCode sourceCode, Tokens tokenEntries) {
+    public void tokenize(TextDocument sourceCode, Tokens tokenEntries) {
         StringBuilder code = sourceCode.getCodeBuffer();
 
         ANTLRStringStream ass = new ANTLRStringStream(code.toString());
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java
index aa21a4db28..5db9827346 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java
@@ -7,6 +7,8 @@ package net.sourceforge.pmd.cpd;
 import java.util.List;
 import java.util.Locale;
 
+import net.sourceforge.pmd.lang.document.TextDocument;
+
 /**
  *
  * @author Zev Blut zb@ubit.com
@@ -48,7 +50,7 @@ public abstract class AbstractTokenizer implements Tokenizer {
     private boolean downcaseString = true;
 
     @Override
-    public void tokenize(SourceCode tokens, Tokens tokenEntries) {
+    public void tokenize(TextDocument tokens, Tokens tokenEntries) {
         code = tokens.getCode();
 
         for (lineNumber = 0; lineNumber < code.size(); lineNumber++) {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java
index e53f29e533..6e02dda6b8 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java
@@ -9,6 +9,7 @@ import java.util.regex.Pattern;
 
 import org.apache.commons.lang3.StringUtils;
 
+import net.sourceforge.pmd.lang.document.TextDocument;
 import net.sourceforge.pmd.util.StringUtil;
 
 /**
@@ -60,7 +61,7 @@ public class AnyTokenizer implements Tokenizer {
     }
 
     @Override
-    public void tokenize(SourceCode sourceCode, Tokens tokenEntries) {
+    public void tokenize(TextDocument sourceCode, Tokens tokenEntries) {
         CharSequence text = sourceCode.getCodeBuffer();
         Matcher matcher = pattern.matcher(text);
         int lineNo = 1;
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java
index e6876fb960..2e0d77f770 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java
@@ -6,7 +6,43 @@ package net.sourceforge.pmd.cpd;
 
 import java.io.IOException;
 
+import net.sourceforge.pmd.lang.document.TextDocument;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+import net.sourceforge.pmd.properties.PropertyFactory;
+
 public interface Tokenizer {
+
+    PropertyDescriptor CPD_IGNORE_LITERAL_SEQUENCES =
+        PropertyFactory.booleanProperty("cpdIgnoreLiteralSequences")
+                       .defaultValue(false)
+                       .desc("Ignore sequences of literals, eg `0, 0, 0, 0`")
+                       .build();
+
+    PropertyDescriptor CPD_ANONYMiZE_LITERALS =
+        PropertyFactory.booleanProperty("cpdAnonymizeLiterals")
+                       .defaultValue(false)
+                       .desc("Anonymize literals. They are still part of the token stream but all literals appear to have the same value.")
+                       .build();
+    PropertyDescriptor CPD_ANONYMIZE_IDENTIFIERS =
+        PropertyFactory.booleanProperty("cpdAnonymizeIdentifiers")
+                       .defaultValue(false)
+                       .desc("Anonymize identifiers. They are still part of the token stream but all literals appear to have the same value.")
+                       .build();
+
+
+    PropertyDescriptor CPD_IGNORE_IMPORTS =
+        PropertyFactory.booleanProperty("cpdIgnoreImports")
+                       .defaultValue(true)
+                       .desc("Ignore import statements and equivalent (eg using statements in C#).")
+                       .build();
+
+    PropertyDescriptor CPD_IGNORE_METADATA =
+        PropertyFactory.booleanProperty("cpdIgnoreMetadata")
+                       .defaultValue(false)
+                       .desc("Ignore metadata such as Java annotations or C# attributes.")
+                       .build();
+
+
     String IGNORE_LITERALS = "ignore_literals";
     String IGNORE_IDENTIFIERS = "ignore_identifiers";
     String IGNORE_ANNOTATIONS = "ignore_annotations";
@@ -39,5 +75,5 @@ public interface Tokenizer {
 
     String DEFAULT_SKIP_BLOCKS_PATTERN = "#if 0|#endif";
 
-    void tokenize(SourceCode sourceCode, Tokens tokenEntries) throws IOException;
+    void tokenize(TextDocument sourceCode, Tokens tokenEntries) throws IOException;
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java
index b09703881a..d5a3472281 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java
@@ -4,58 +4,26 @@
 
 package net.sourceforge.pmd.cpd.internal;
 
-import java.io.IOException;
-import java.io.UncheckedIOException;
-
 import org.antlr.v4.runtime.CharStream;
 import org.antlr.v4.runtime.CharStreams;
 import org.antlr.v4.runtime.Lexer;
 
-import net.sourceforge.pmd.cpd.SourceCode;
-import net.sourceforge.pmd.cpd.TokenEntry;
 import net.sourceforge.pmd.cpd.Tokenizer;
-import net.sourceforge.pmd.cpd.Tokens;
-import net.sourceforge.pmd.cpd.token.AntlrTokenFilter;
+import net.sourceforge.pmd.lang.TokenManager;
 import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken;
 import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager;
-import net.sourceforge.pmd.lang.document.CpdCompat;
 import net.sourceforge.pmd.lang.document.TextDocument;
 
 /**
  * Generic implementation of a {@link Tokenizer} useful to any Antlr grammar.
  */
-public abstract class AntlrTokenizer implements Tokenizer {
+public abstract class AntlrTokenizer extends TokenizerBase {
+    @Override
+    protected final TokenManager makeLexerImpl(TextDocument doc) {
+        CharStream charStream = CharStreams.fromString(doc.getText().toString(), doc.getDisplayName());
+        return new AntlrTokenManager(getLexerForSource(charStream), doc);
+    }
 
     protected abstract Lexer getLexerForSource(CharStream charStream);
 
-    @Override
-    public void tokenize(final SourceCode sourceCode, final Tokens tokenEntries) {
-        try (TextDocument textDoc = TextDocument.create(CpdCompat.cpdCompat(sourceCode))) {
-
-            CharStream charStream = CharStreams.fromString(textDoc.getText().toString(), textDoc.getDisplayName());
-
-            final AntlrTokenManager tokenManager = new AntlrTokenManager(getLexerForSource(charStream), textDoc);
-            final AntlrTokenFilter tokenFilter = getTokenFilter(tokenManager);
-
-            AntlrToken currentToken = tokenFilter.getNextToken();
-            while (currentToken != null) {
-                processToken(tokenEntries, currentToken);
-                currentToken = tokenFilter.getNextToken();
-            }
-
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        } finally {
-            tokenEntries.add(TokenEntry.getEOF());
-        }
-    }
-
-    protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) {
-        return new AntlrTokenFilter(tokenManager);
-    }
-
-    private void processToken(final Tokens tokenEntries, final AntlrToken token) {
-        final TokenEntry tokenEntry = new TokenEntry(token.getImage(), token.getReportLocation());
-        tokenEntries.add(tokenEntry);
-    }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java
index 3c45b96033..3a629d5af4 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java
@@ -4,61 +4,8 @@
 
 package net.sourceforge.pmd.cpd.internal;
 
-import java.io.IOException;
-
-import net.sourceforge.pmd.cpd.SourceCode;
-import net.sourceforge.pmd.cpd.TokenEntry;
-import net.sourceforge.pmd.cpd.Tokenizer;
-import net.sourceforge.pmd.cpd.Tokens;
-import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter;
-import net.sourceforge.pmd.cpd.token.TokenFilter;
-import net.sourceforge.pmd.lang.TokenManager;
-import net.sourceforge.pmd.lang.ast.FileAnalysisException;
-import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream;
 import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
-import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior;
-import net.sourceforge.pmd.lang.document.CpdCompat;
-import net.sourceforge.pmd.lang.document.TextDocument;
 
-public abstract class JavaCCTokenizer implements Tokenizer {
+public abstract class JavaCCTokenizer extends TokenizerBase {
 
-    @SuppressWarnings("PMD.CloseResource")
-    protected TokenManager getLexerForSource(TextDocument sourceCode) throws IOException {
-        return makeLexerImpl(CharStream.create(sourceCode, tokenBehavior()));
-    }
-
-    protected TokenDocumentBehavior tokenBehavior() {
-        return TokenDocumentBehavior.DEFAULT;
-    }
-
-    protected abstract TokenManager makeLexerImpl(CharStream sourceCode);
-
-    protected TokenFilter getTokenFilter(TokenManager tokenManager) {
-        return new JavaCCTokenFilter(tokenManager);
-    }
-
-    protected TokenEntry processToken(Tokens tokenEntries, JavaccToken currentToken) {
-        return new TokenEntry(getImage(currentToken), currentToken.getReportLocation());
-    }
-
-    protected String getImage(JavaccToken token) {
-        return token.getImage();
-    }
-
-    @Override
-    public void tokenize(SourceCode sourceCode, Tokens tokenEntries) throws IOException {
-        try (TextDocument textDoc = TextDocument.create(CpdCompat.cpdCompat(sourceCode))) {
-            TokenManager tokenManager = getLexerForSource(textDoc);
-            final TokenFilter tokenFilter = getTokenFilter(tokenManager);
-            JavaccToken currentToken = tokenFilter.getNextToken();
-            while (currentToken != null) {
-                tokenEntries.add(processToken(tokenEntries, currentToken));
-                currentToken = tokenFilter.getNextToken();
-            }
-        } catch (FileAnalysisException e) {
-            throw e.setFileName(sourceCode.getFileName());
-        } finally {
-            tokenEntries.add(TokenEntry.getEOF());
-        }
-    }
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java
new file mode 100644
index 0000000000..07d6e9894f
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java
@@ -0,0 +1,42 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.cpd.internal;
+
+import java.io.IOException;
+
+import net.sourceforge.pmd.cpd.TokenEntry;
+import net.sourceforge.pmd.cpd.Tokenizer;
+import net.sourceforge.pmd.cpd.Tokens;
+import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter;
+import net.sourceforge.pmd.lang.TokenManager;
+import net.sourceforge.pmd.lang.ast.GenericToken;
+import net.sourceforge.pmd.lang.document.TextDocument;
+
+public abstract class TokenizerBase> implements Tokenizer {
+
+    protected abstract TokenManager makeLexerImpl(TextDocument doc);
+
+    protected TokenManager filterTokenStream(TokenManager tokenManager) {
+        return new BaseTokenFilter<>(tokenManager);
+    }
+
+    protected TokenEntry processToken(Tokens tokenEntries, T currentToken) {
+        return new TokenEntry(getImage(currentToken), currentToken.getReportLocation());
+    }
+
+    protected String getImage(T token) {
+        return token.getImage();
+    }
+
+    @Override
+    public void tokenize(TextDocument document, Tokens tokenEntries) throws IOException {
+        TokenManager tokenManager = filterTokenStream(makeLexerImpl(document));
+        T currentToken = tokenManager.getNextToken();
+        while (currentToken != null) {
+            tokenEntries.add(processToken(tokenEntries, currentToken));
+            currentToken = tokenManager.getNextToken();
+        }
+    }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java
index 3671f109db..469b33d89f 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java
@@ -4,16 +4,18 @@
 
 package net.sourceforge.pmd.cpd.token;
 
+import net.sourceforge.pmd.lang.TokenManager;
 import net.sourceforge.pmd.lang.ast.GenericToken;
 
 /**
  * Defines filter to be applied to the token stream during CPD analysis
  */
-public interface TokenFilter> {
+public interface TokenFilter> extends TokenManager {
 
     /**
      * Retrieves the next token to pass the filter
      * @return The next token to pass the filter, or null if the end of the stream was reached
      */
+    @Override
     T getNextToken();
 }
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java
index 6d980ea41e..d4d6e7c90b 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java
@@ -18,7 +18,7 @@ import net.sourceforge.pmd.lang.ast.GenericToken;
  * A generic filter for PMD token managers that allows to use comments
  * to enable / disable analysis of parts of the stream
  */
-public abstract class BaseTokenFilter> implements TokenFilter {
+public class BaseTokenFilter> implements TokenFilter {
 
     private final TokenManager tokenManager;
     private final LinkedList unprocessedTokens; // NOPMD - used both as Queue and List
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java
new file mode 100644
index 0000000000..ade537f08e
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java
@@ -0,0 +1,28 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang;
+
+/**
+ * Base class for language modules that only support CPD and not PMD.
+ *
+ * @author Clรฉment Fournier
+ */
+public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase {
+
+    /**
+     * Construct a module instance using the given metadata. The metadata must
+     * be properly constructed.
+     *
+     * @throws IllegalStateException If the metadata is invalid (eg missing extensions or name)
+     */
+    protected CpdOnlyLanguageModuleBase(LanguageMetadata metadata) {
+        super(metadata);
+    }
+
+    @Override
+    public boolean supportsParsing() {
+        return false;
+    }
+}
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java
index 4d10b41e4d..045dd2c9a6 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java
@@ -8,6 +8,8 @@ import java.util.List;
 import java.util.ServiceLoader;
 import java.util.Set;
 
+import net.sourceforge.pmd.cpd.Tokenizer;
+
 /**
  * Represents a language module, and provides access to language-specific
  * functionality. You can get a language instance from a {@link LanguageRegistry}.
@@ -156,6 +158,14 @@ public interface Language extends Comparable {
         return new LanguagePropertyBundle(this);
     }
 
+    /**
+     * Return true if this language supports parsing files into an AST.
+     * In that case {@link #createProcessor(LanguagePropertyBundle)} should
+     * also be implemented.
+     */
+    default boolean supportsParsing() {
+        return false;
+    }
 
     /**
      * Create a new {@link LanguageProcessor} for this language, given
@@ -167,8 +177,30 @@ public interface Language extends Comparable {
      * @param bundle A bundle of properties created by this instance.
      *
      * @return A new language processor
+     *
+     * @throws UnsupportedOperationException if this language does not support PMD
      */
-    LanguageProcessor createProcessor(LanguagePropertyBundle bundle);
+    default LanguageProcessor createProcessor(LanguagePropertyBundle bundle) {
+        throw new UnsupportedOperationException(this + " does not support running a PMD analysis.");
+    }
+
+
+    /**
+     * Create a new {@link Tokenizer} for this language, given
+     * a property bundle with configuration. The bundle was created by
+     * this instance using {@link #newPropertyBundle()}. It can be assumed
+     * that the bundle will never be mutated anymore, and this method
+     * takes ownership of it.
+     *
+     * @param bundle A bundle of properties created by this instance.
+     *
+     * @return A new language processor
+     *
+     * @throws UnsupportedOperationException if this language does not support CPD
+     */
+    default Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) {
+        throw new UnsupportedOperationException(this + " does not support running a CPD analysis.");
+    }
 
 
     /**
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java
index 3adf16c8fb..5cfd9f9baf 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java
@@ -41,6 +41,7 @@ public final class LanguageRegistry implements Iterable {
      * of the classloader of this class. This can be used as a "default" registry.
      */
     public static final LanguageRegistry PMD = loadLanguages(LanguageRegistry.class.getClassLoader());
+    public static final LanguageRegistry CPD = loadLanguages(LanguageRegistry.class.getClassLoader()); // todo
 
     private final Set languages;
 
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStream.java
index d598bff263..9635cf784b 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStream.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/CharStream.java
@@ -42,6 +42,15 @@ public final class CharStream {
         return new CharStream(new JavaccTokenDocument(translated, behavior));
     }
 
+    /**
+     * Create a new char stream for the given document with the default token
+     * document behavior. This may create a new {@link TextDocument} view
+     * over the original, which reflects its character escapes.
+     */
+    public static CharStream create(TextDocument doc) throws MalformedSourceException {
+        return create(doc, TokenDocumentBehavior.DEFAULT);
+    }
+
     /**
      * Returns the next character from the input. After a {@link #backup(int)},
      * some of the already read chars must be spit out again.
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java
index 6f907780b3..23cf5ee7b1 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java
@@ -20,7 +20,7 @@ import net.sourceforge.pmd.lang.LanguageVersionHandler;
  * @author Clรฉment Fournier
  * @since 7.0.0
  */
-public class SimpleLanguageModuleBase extends LanguageModuleBase {
+public abstract class SimpleLanguageModuleBase extends LanguageModuleBase {
 
     private final Function handler;
 
@@ -33,6 +33,11 @@ public class SimpleLanguageModuleBase extends LanguageModuleBase {
         this.handler = makeHandler;
     }
 
+    @Override
+    public boolean supportsParsing() {
+        return true;
+    }
+
     @Override
     public LanguageProcessor createProcessor(LanguagePropertyBundle bundle) {
         LanguageVersionHandler services = handler.apply(bundle);
diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPLanguage.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPLanguage.java
deleted file mode 100644
index a3dfce0c96..0000000000
--- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPLanguage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.cpd;
-
-import java.util.Properties;
-
-/**
- * Defines the Language module for C/C++
- */
-public class CPPLanguage extends AbstractLanguage {
-
-    /**
-     * Creates a new instance of {@link CPPLanguage} with the default extensions
-     * for c/c++ files.
-     */
-    public CPPLanguage() {
-        this(System.getProperties());
-    }
-
-    public CPPLanguage(Properties properties) {
-        super("C++", "cpp", new CPPTokenizer(), ".h", ".hpp", ".hxx", ".c", ".cpp", ".cxx", ".cc", ".C");
-        setProperties(properties);
-    }
-
-    @Override
-    public void setProperties(Properties properties) {
-        super.setProperties(properties);
-        ((CPPTokenizer) getTokenizer()).setProperties(properties);
-    }
-}
diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java
index 0728e9afbf..bb9f5c2811 100644
--- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java
+++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java
@@ -4,46 +4,37 @@
 
 package net.sourceforge.pmd.cpd;
 
-import java.util.Properties;
 import java.util.regex.Pattern;
 
-import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer;
+import org.apache.commons.lang3.StringUtils;
+
+import net.sourceforge.pmd.cpd.internal.TokenizerBase;
 import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter;
-import net.sourceforge.pmd.cpd.token.TokenFilter;
+import net.sourceforge.pmd.lang.LanguagePropertyBundle;
 import net.sourceforge.pmd.lang.TokenManager;
 import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream;
 import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
 import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior;
 import net.sourceforge.pmd.lang.ast.impl.javacc.MalformedSourceException;
+import net.sourceforge.pmd.lang.cpp.CppLanguageModule;
 import net.sourceforge.pmd.lang.cpp.ast.CppTokenKinds;
 import net.sourceforge.pmd.lang.document.TextDocument;
 
 /**
  * The C++ tokenizer.
  */
-public class CPPTokenizer extends JavaCCTokenizer {
+public class CPPTokenizer extends TokenizerBase {
 
     private boolean skipBlocks;
     private Pattern skipBlocksStart;
     private Pattern skipBlocksEnd;
-    private boolean ignoreLiteralSequences = false;
+    private final boolean ignoreLiteralSequences;
 
-    public CPPTokenizer() {
-        setProperties(new Properties()); // set the defaults
-    }
-
-    /**
-     * Sets the possible options for the C++ tokenizer.
-     *
-     * @param properties the properties
-     * @see #OPTION_SKIP_BLOCKS
-     * @see #OPTION_SKIP_BLOCKS_PATTERN
-     * @see #OPTION_IGNORE_LITERAL_SEQUENCES
-     */
-    public void setProperties(Properties properties) {
-        skipBlocks = Boolean.parseBoolean(properties.getProperty(OPTION_SKIP_BLOCKS, Boolean.TRUE.toString()));
-        if (skipBlocks) {
-            String skipBlocksPattern = properties.getProperty(OPTION_SKIP_BLOCKS_PATTERN, DEFAULT_SKIP_BLOCKS_PATTERN);
+    public CPPTokenizer(LanguagePropertyBundle cppProperties) {
+        ignoreLiteralSequences = cppProperties.getProperty(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES);
+        String skipBlocksPattern = cppProperties.getProperty(CppLanguageModule.CPD_SKIP_BLOCKS);
+        if (StringUtils.isNotBlank(skipBlocksPattern)) {
+            skipBlocks = true;
             String[] split = skipBlocksPattern.split("\\|", 2);
             skipBlocksStart = CppBlockSkipper.compileSkipMarker(split[0]);
             if (split.length == 1) {
@@ -52,14 +43,15 @@ public class CPPTokenizer extends JavaCCTokenizer {
                 skipBlocksEnd = CppBlockSkipper.compileSkipMarker(split[1]);
             }
         }
-        ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES,
-                Boolean.FALSE.toString()));
     }
 
-
     @Override
-    protected TokenDocumentBehavior tokenBehavior() {
-        return new TokenDocumentBehavior(CppTokenKinds.TOKEN_NAMES) {
+    protected TokenManager makeLexerImpl(TextDocument doc) {
+        return CppTokenKinds.newTokenManager(newCharStream(doc));
+    }
+
+    CharStream newCharStream(TextDocument doc) {
+        return CharStream.create(doc, new TokenDocumentBehavior(CppTokenKinds.TOKEN_NAMES) {
 
             @Override
             public TextDocument translate(TextDocument text) throws MalformedSourceException {
@@ -68,20 +60,16 @@ public class CPPTokenizer extends JavaCCTokenizer {
                 }
                 return new CppEscapeTranslator(text).translateDocument();
             }
-        };
+        });
     }
 
     @Override
-    protected TokenManager makeLexerImpl(CharStream sourceCode) {
-        return CppTokenKinds.newTokenManager(sourceCode);
-    }
-
-    @Override
-    protected TokenFilter getTokenFilter(final TokenManager tokenManager) {
+    protected TokenManager filterTokenStream(final TokenManager tokenManager) {
         return new CppTokenFilter(tokenManager, ignoreLiteralSequences);
     }
 
     private static class CppTokenFilter extends JavaCCTokenFilter {
+
         private final boolean ignoreLiteralSequences;
         private JavaccToken discardingLiteralsUntil = null;
         private boolean discardCurrent = false;
@@ -106,8 +94,7 @@ public class CPPTokenizer extends JavaCCTokenizer {
                         discardCurrent = true;
                     }
                 } else if (kind == CppTokenKinds.LCURLYBRACE) {
-                    final JavaccToken finalToken = findEndOfSequenceOfLiterals(remainingTokens);
-                    discardingLiteralsUntil = finalToken;
+                    discardingLiteralsUntil = findEndOfSequenceOfLiterals(remainingTokens);
                 }
             }
         }
diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java
new file mode 100644
index 0000000000..eadecc970f
--- /dev/null
+++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java
@@ -0,0 +1,56 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.cpp;
+
+import net.sourceforge.pmd.cpd.CPPTokenizer;
+import net.sourceforge.pmd.cpd.Tokenizer;
+import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase;
+import net.sourceforge.pmd.lang.LanguagePropertyBundle;
+import net.sourceforge.pmd.lang.LanguageRegistry;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+import net.sourceforge.pmd.properties.PropertyFactory;
+
+/**
+ * Defines the Language module for C/C++
+ */
+public class CppLanguageModule extends CpdOnlyLanguageModuleBase {
+
+
+    public static final PropertyDescriptor CPD_SKIP_BLOCKS =
+    PropertyFactory.stringProperty("cpdSkipBlocksPattern")
+                       .defaultValue("#if 0|#endif")
+                       .desc("Specifies a start and end delimiter for CPD to completely ignore. "
+                                 + "The delimiters are separated by a pipe |. The default skips code "
+                                 + " that is conditionally compiled out. Set this property to empty to disable this.")
+                       .build();
+
+    /**
+     * Creates a new instance of {@link CppLanguageModule} with the default extensions
+     * for c/c++ files.
+     */
+    public CppLanguageModule() {
+        super(LanguageMetadata.withId("cpp")
+                              .name("C++")
+                              .addDefaultVersion("any")
+                              .extensions("h", "hpp", "hxx", "c", "cpp", "cxx", "cc", "C"));
+    }
+
+    public static CppLanguageModule getInstance() {
+        return (CppLanguageModule) LanguageRegistry.CPD.getLanguageById("cpp");
+    }
+
+    @Override
+    public LanguagePropertyBundle newPropertyBundle() {
+        LanguagePropertyBundle bundle = super.newPropertyBundle();
+        bundle.definePropertyDescriptor(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES);
+        bundle.definePropertyDescriptor(CPD_SKIP_BLOCKS);
+        return bundle;
+    }
+
+    @Override
+    public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) {
+        return new CPPTokenizer(bundle);
+    }
+}
diff --git a/pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
deleted file mode 100644
index 2170e55e7f..0000000000
--- a/pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language
+++ /dev/null
@@ -1 +0,0 @@
-net.sourceforge.pmd.cpd.CPPLanguage
diff --git a/pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
new file mode 100644
index 0000000000..ecb3ec91fa
--- /dev/null
+++ b/pmd-cpp/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
@@ -0,0 +1 @@
+net.sourceforge.pmd.lang.cpp.CppLanguageModule
diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java
index 3546db9e6c..a44e7cf970 100644
--- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java
+++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java
@@ -8,20 +8,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.io.IOException;
 
-import org.checkerframework.checker.nullness.qual.NonNull;
 import org.junit.jupiter.api.Test;
 
 import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream;
-import net.sourceforge.pmd.lang.document.CpdCompat;
+import net.sourceforge.pmd.lang.cpp.CppLanguageModule;
 import net.sourceforge.pmd.lang.document.TextDocument;
 import net.sourceforge.pmd.lang.document.TextFile;
 
 class CppCharStreamTest {
 
-    @NonNull
-    public CharStream charStreamFor(String source) throws IOException {
-        TextDocument textDoc = TextDocument.readOnlyString(source, TextFile.UNKNOWN_FILENAME, CpdCompat.dummyVersion());
-        return CharStream.create(textDoc, new CPPTokenizer().tokenBehavior());
+    public CharStream charStreamFor(String source) {
+        CppLanguageModule cpp = CppLanguageModule.getInstance();
+        TextDocument textDoc = TextDocument.readOnlyString(source, TextFile.UNKNOWN_FILENAME, cpp.getDefaultVersion());
+        CPPTokenizer tokenizer = new CPPTokenizer(cpp.newPropertyBundle());
+        return tokenizer.newCharStream(textDoc);
     }
 
     @Test
diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsLanguage.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsLanguage.java
deleted file mode 100644
index e54edcddbe..0000000000
--- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsLanguage.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.cpd;
-
-import java.util.Properties;
-
-/**
- * Language implementation for C#
- */
-public class CsLanguage extends AbstractLanguage {
-
-    public CsLanguage() {
-        this(System.getProperties());
-    }
-
-    public CsLanguage(Properties properties) {
-        super("C#", "cs", new CsTokenizer(), ".cs");
-        setProperties(properties);
-    }
-
-    @Override
-    public final void setProperties(Properties properties) {
-        CsTokenizer tokenizer = (CsTokenizer) getTokenizer();
-        tokenizer.setProperties(properties);
-    }
-}
diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java
index 64822f1a38..d58ccdb0d7 100644
--- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java
+++ b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java
@@ -4,15 +4,15 @@
 
 package net.sourceforge.pmd.cpd;
 
-import java.util.Properties;
-
 import org.antlr.v4.runtime.CharStream;
 import org.antlr.v4.runtime.Lexer;
 
 import net.sourceforge.pmd.cpd.internal.AntlrTokenizer;
 import net.sourceforge.pmd.cpd.token.AntlrTokenFilter;
+import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter;
+import net.sourceforge.pmd.lang.LanguagePropertyBundle;
+import net.sourceforge.pmd.lang.TokenManager;
 import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken;
-import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager;
 import net.sourceforge.pmd.lang.cs.ast.CSharpLexer;
 
 /**
@@ -20,26 +20,14 @@ import net.sourceforge.pmd.lang.cs.ast.CSharpLexer;
  */
 public class CsTokenizer extends AntlrTokenizer {
 
-    private boolean ignoreUsings = false;
-    private boolean ignoreLiteralSequences = false;
-    private boolean ignoreAttributes = false;
+    private final boolean ignoreUsings;
+    private final boolean ignoreLiteralSequences;
+    private final boolean ignoreAttributes;
 
-    /**
-     * Sets the possible options for the C# tokenizer.
-     *
-     * @param properties the properties
-     * @see #IGNORE_USINGS
-     * @see #OPTION_IGNORE_LITERAL_SEQUENCES
-     * @see #IGNORE_ANNOTATIONS
-     */
-    public void setProperties(Properties properties) {
-        ignoreUsings = getBooleanProperty(properties, IGNORE_USINGS);
-        ignoreLiteralSequences = getBooleanProperty(properties, OPTION_IGNORE_LITERAL_SEQUENCES);
-        ignoreAttributes = getBooleanProperty(properties, IGNORE_ANNOTATIONS);
-    }
-
-    private boolean getBooleanProperty(final Properties properties, final String property) {
-        return Boolean.parseBoolean(properties.getProperty(property, Boolean.FALSE.toString()));
+    public CsTokenizer(LanguagePropertyBundle properties) {
+        ignoreUsings = properties.getProperty(Tokenizer.CPD_IGNORE_IMPORTS);
+        ignoreLiteralSequences = properties.getProperty(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES);
+        ignoreAttributes = properties.getProperty(Tokenizer.CPD_IGNORE_METADATA);
     }
 
     @Override
@@ -48,7 +36,7 @@ public class CsTokenizer extends AntlrTokenizer {
     }
 
     @Override
-    protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) {
+    protected TokenManager filterTokenStream(TokenManager tokenManager) {
         return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences, ignoreAttributes);
     }
 
@@ -60,7 +48,7 @@ public class CsTokenizer extends AntlrTokenizer {
      * If the --ignoreUsings flag is provided, using directives are filtered out.
      * 

*/ - private static class CsTokenFilter extends AntlrTokenFilter { + private static class CsTokenFilter extends BaseTokenFilter { private enum UsingState { KEYWORD, // just encountered the using keyword IDENTIFIER, // just encountered an identifier or var keyword @@ -75,7 +63,7 @@ public class CsTokenizer extends AntlrTokenizer { private AntlrToken discardingLiteralsUntil = null; private boolean discardCurrent = false; - CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { + CsTokenFilter(final TokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { super(tokenManager); this.ignoreUsings = ignoreUsings; this.ignoreLiteralSequences = ignoreLiteralSequences; diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java new file mode 100644 index 0000000000..5f122b8e35 --- /dev/null +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java @@ -0,0 +1,42 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.cs; + +import net.sourceforge.pmd.cpd.CsTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.LanguageRegistry; + +/** + * Defines the Language module for C#. + */ +public class CsLanguageModule extends CpdOnlyLanguageModuleBase { + + public CsLanguageModule() { + super(LanguageMetadata.withId("cs") + .name("C#") + .addDefaultVersion("any") + .extensions("cs")); + } + + public static CsLanguageModule getInstance() { + return (CsLanguageModule) LanguageRegistry.CPD.getLanguageById("cs"); + } + + @Override + public LanguagePropertyBundle newPropertyBundle() { + LanguagePropertyBundle bundle = super.newPropertyBundle(); + bundle.definePropertyDescriptor(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES); + bundle.definePropertyDescriptor(Tokenizer.CPD_IGNORE_IMPORTS); + bundle.definePropertyDescriptor(Tokenizer.CPD_IGNORE_METADATA); + return bundle; + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new CsTokenizer(bundle); + } +} diff --git a/pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 76459b4741..0000000000 --- a/pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.CsLanguage diff --git a/pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..1b979f896f --- /dev/null +++ b/pmd-cs/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.cs.CsLanguageModule diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartTokenizer.java b/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartTokenizer.java index 6c847754a2..06a2527d9e 100644 --- a/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartTokenizer.java +++ b/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartTokenizer.java @@ -9,8 +9,9 @@ import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; +import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; +import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; -import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager; import net.sourceforge.pmd.lang.dart.ast.DartLexer; /** @@ -24,7 +25,7 @@ public class DartTokenizer extends AntlrTokenizer { } @Override - protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { + protected TokenManager filterTokenStream(TokenManager tokenManager) { return new DartTokenFilter(tokenManager); } @@ -36,12 +37,12 @@ public class DartTokenizer extends AntlrTokenizer { * enables comment-based CPD suppression. *

*/ - private static class DartTokenFilter extends AntlrTokenFilter { + private static class DartTokenFilter extends BaseTokenFilter { private boolean discardingLibraryAndImport = false; private boolean discardingNL = false; private boolean discardingSemicolon = false; - /* default */ DartTokenFilter(final AntlrTokenManager tokenManager) { + /* default */ DartTokenFilter(final TokenManager tokenManager) { super(tokenManager); } diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java index 654342e2b6..79ecf7b6b5 100644 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java @@ -10,6 +10,7 @@ import org.codehaus.groovy.antlr.SourceInfo; import org.codehaus.groovy.antlr.parser.GroovyLexer; import net.sourceforge.pmd.lang.ast.TokenMgrError; +import net.sourceforge.pmd.lang.document.TextDocument; import groovyjarjarantlr.Token; import groovyjarjarantlr.TokenStream; @@ -21,7 +22,7 @@ import groovyjarjarantlr.TokenStreamException; public class GroovyTokenizer implements Tokenizer { @Override - public void tokenize(SourceCode sourceCode, Tokens tokenEntries) { + public void tokenize(TextDocument sourceCode, Tokens tokenEntries) { StringBuilder buffer = sourceCode.getCodeBuffer(); GroovyLexer lexer = new GroovyLexer(new StringReader(buffer.toString())); diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java index e7fad1cbe9..8f70dc69f0 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.html.ast; import java.io.IOException; import java.io.UncheckedIOException; -import net.sourceforge.pmd.cpd.SourceCode; import net.sourceforge.pmd.cpd.TokenEntry; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.Tokens; @@ -22,7 +21,7 @@ import net.sourceforge.pmd.lang.html.HtmlLanguageModule; public class HtmlTokenizer implements Tokenizer { @Override - public void tokenize(SourceCode sourceCode, Tokens tokenEntries) { + public void tokenize(TextDocument sourceCode, Tokens tokenEntries) { HtmlLanguageModule html = HtmlLanguageModule.getInstance(); try (LanguageProcessor processor = html.createProcessor(html.newPropertyBundle()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java index 525d1731b2..8a54eca671 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java @@ -11,11 +11,10 @@ import java.util.Properties; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; -import net.sourceforge.pmd.cpd.token.TokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.java.ast.JavaTokenKinds; @@ -37,23 +36,18 @@ public class JavaTokenizer extends JavaCCTokenizer { } @Override - public void tokenize(SourceCode sourceCode, Tokens tokenEntries) throws IOException { + public void tokenize(TextDocument sourceCode, Tokens tokenEntries) throws IOException { constructorDetector = new ConstructorDetector(ignoreIdentifiers); super.tokenize(sourceCode, tokenEntries); } @Override - protected JavaccTokenDocument.TokenDocumentBehavior tokenBehavior() { - return InternalApiBridge.javaTokenDoc(); + protected TokenManager makeLexerImpl(TextDocument doc) { + return JavaTokenKinds.newTokenManager(CharStream.create(doc, InternalApiBridge.javaTokenDoc())); } @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return JavaTokenKinds.newTokenManager(sourceCode); - } - - @Override - protected TokenFilter getTokenFilter(TokenManager tokenManager) { + protected TokenManager filterTokenStream(TokenManager tokenManager) { return new JavaTokenFilter(tokenManager, ignoreAnnotations); } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java index d66d74949f..1a2de570ec 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java @@ -8,6 +8,7 @@ import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.ecmascript5.ast.Ecmascript5TokenKinds; /** @@ -16,8 +17,8 @@ import net.sourceforge.pmd.lang.ecmascript5.ast.Ecmascript5TokenKinds; public class EcmascriptTokenizer extends JavaCCTokenizer { @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return Ecmascript5TokenKinds.newTokenManager(sourceCode); + protected TokenManager makeLexerImpl(TextDocument doc) { + return Ecmascript5TokenKinds.newTokenManager(CharStream.create(doc)); } @Override diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java index 5617484d1b..d32b96973e 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java @@ -8,20 +8,15 @@ import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.jsp.ast.JspParser; import net.sourceforge.pmd.lang.jsp.ast.JspTokenKinds; public class JSPTokenizer extends JavaCCTokenizer { @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return JspTokenKinds.newTokenManager(sourceCode); - } - - @Override - protected JavaccTokenDocument.TokenDocumentBehavior tokenBehavior() { - return JspParser.getTokenBehavior(); + protected TokenManager makeLexerImpl(TextDocument doc) { + return JspTokenKinds.newTokenManager(CharStream.create(doc, JspParser.getTokenBehavior())); } } diff --git a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java b/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java index 9459c44696..b2233923b3 100644 --- a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java +++ b/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java @@ -6,8 +6,8 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.matlab.ast.MatlabTokenKinds; /** @@ -16,7 +16,7 @@ import net.sourceforge.pmd.lang.matlab.ast.MatlabTokenKinds; public class MatlabTokenizer extends JavaCCTokenizer { @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return MatlabTokenKinds.newTokenManager(sourceCode); + protected TokenManager makeLexerImpl(TextDocument doc) { + return MatlabTokenKinds.newTokenManager(doc); } } diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java index 3258a3cda7..61bbad3226 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java @@ -7,20 +7,20 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.modelica.ast.ModelicaTokenKinds; public class ModelicaTokenizer extends JavaCCTokenizer { @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return ModelicaTokenKinds.newTokenManager(sourceCode); + protected TokenManager makeLexerImpl(TextDocument doc) { + return ModelicaTokenKinds.newTokenManager(doc); } @Override - protected JavaCCTokenFilter getTokenFilter(TokenManager tokenManager) { + protected TokenManager filterTokenStream(TokenManager tokenManager) { return new ModelicaTokenFilter(tokenManager); } diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java index acccfcd24a..6c338b4067 100644 --- a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java @@ -6,8 +6,8 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.objectivec.ast.ObjectiveCTokenKinds; /** @@ -16,7 +16,7 @@ import net.sourceforge.pmd.lang.objectivec.ast.ObjectiveCTokenKinds; public class ObjectiveCTokenizer extends JavaCCTokenizer { @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return ObjectiveCTokenKinds.newTokenManager(sourceCode); + protected TokenManager makeLexerImpl(TextDocument doc) { + return ObjectiveCTokenKinds.newTokenManager(doc); } } diff --git a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java b/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java index 974a32c1e5..b63cfecd0e 100644 --- a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java +++ b/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java @@ -6,13 +6,15 @@ package net.sourceforge.pmd.cpd; import java.util.List; +import net.sourceforge.pmd.lang.document.TextDocument; + /** * Simple tokenizer for PHP. */ public class PHPTokenizer implements Tokenizer { @Override - public void tokenize(SourceCode tokens, Tokens tokenEntries) { + public void tokenize(TextDocument tokens, Tokens tokenEntries) { List code = tokens.getCode(); for (int i = 0; i < code.size(); i++) { String currentLine = code.get(i); diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java index 77abbf8794..4d66cf3089 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java @@ -8,8 +8,8 @@ import java.util.Properties; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.plsql.ast.PLSQLTokenKinds; public class PLSQLTokenizer extends JavaCCTokenizer { @@ -66,7 +66,7 @@ public class PLSQLTokenizer extends JavaCCTokenizer { } @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return PLSQLTokenKinds.newTokenManager(sourceCode); + protected TokenManager makeLexerImpl(TextDocument doc) { + return PLSQLTokenKinds.newTokenManager(doc); } } diff --git a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java b/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java index c80d572f67..89f8dce9ae 100644 --- a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java +++ b/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java @@ -10,8 +10,8 @@ import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; -import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument.TokenDocumentBehavior; +import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.python.ast.PythonTokenKinds; /** @@ -24,13 +24,8 @@ public class PythonTokenizer extends JavaCCTokenizer { private static final TokenDocumentBehavior TOKEN_BEHAVIOR = new TokenDocumentBehavior(PythonTokenKinds.TOKEN_NAMES); @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return PythonTokenKinds.newTokenManager(sourceCode); - } - - @Override - protected JavaccTokenDocument.TokenDocumentBehavior tokenBehavior() { - return TOKEN_BEHAVIOR; + protected TokenManager makeLexerImpl(TextDocument doc) { + return PythonTokenKinds.newTokenManager(CharStream.create(doc, TOKEN_BEHAVIOR)); } @Override diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 060f24cbd4..5c2b98892b 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -62,7 +62,7 @@ public class ScalaTokenizer implements Tokenizer { } @Override - public void tokenize(SourceCode sourceCode, Tokens tokenEntries) throws IOException { + public void tokenize(TextDocument sourceCode, Tokens tokenEntries) throws IOException { try (TextDocument textDoc = TextDocument.create(CpdCompat.cpdCompat(sourceCode))) { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java index 17926160b0..d831ee0f7d 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaEscapeTranslator; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; @@ -21,8 +20,8 @@ import net.sourceforge.pmd.lang.vf.ast.VfTokenKinds; public class VfTokenizer extends JavaCCTokenizer { @Override - protected TokenManager makeLexerImpl(CharStream sourceCode) { - return VfTokenKinds.newTokenManager(sourceCode); + protected TokenManager makeLexerImpl(TextDocument doc) { + return VfTokenKinds.newTokenManager(doc); } @Override From cf81809990fb441c57cb8f7e56aca5ba1d8c0add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 10 Feb 2023 18:21:23 +0100 Subject: [PATCH 093/347] Change a ton of stuff in CPD --- .../net/sourceforge/pmd/cpd/ApexLanguage.java | 25 --- .../sourceforge/pmd/cpd/ApexTokenizer.java | 71 ++----- .../pmd/lang/apex/ApexLanguageModule.java | 7 + .../pmd/lang/apex/ApexLanguageProperties.java | 2 + .../services/net.sourceforge.pmd.cpd.Language | 1 - .../pmd/cpd/ApexTokenizerTest.java | 21 +- .../pmd/cli/commands/internal/CpdCommand.java | 2 +- .../pmd/cpd/AbstractTokenizer.java | 184 ------------------ .../net/sourceforge/pmd/cpd/AnyTokenizer.java | 49 +++-- .../java/net/sourceforge/pmd/cpd/CPD.java | 35 ++-- .../sourceforge/pmd/cpd/CPDConfiguration.java | 11 +- .../net/sourceforge/pmd/cpd/CPDListener.java | 4 +- .../sourceforge/pmd/cpd/CPDNullListener.java | 4 +- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 147 ++++++++++++++ .../java/net/sourceforge/pmd/cpd/GUI.java | 2 +- .../java/net/sourceforge/pmd/cpd/Mark.java | 13 +- .../java/net/sourceforge/pmd/cpd/Match.java | 3 +- .../sourceforge/pmd/cpd/MatchAlgorithm.java | 12 +- .../sourceforge/pmd/cpd/SimpleRenderer.java | 5 +- .../net/sourceforge/pmd/cpd/SourceCode.java | 184 ++---------------- .../sourceforge/pmd/cpd/SourceManager.java | 43 ++++ .../net/sourceforge/pmd/cpd/TokenEntry.java | 22 --- .../net/sourceforge/pmd/cpd/TokenFactory.java | 42 ++++ .../net/sourceforge/pmd/cpd/Tokenizer.java | 9 +- .../java/net/sourceforge/pmd/cpd/Tokens.java | 36 +++- .../pmd/cpd/internal/TokenizerBase.java | 11 +- .../pmd/lang/document/FileCollector.java | 19 +- .../net/sourceforge/pmd/util/StringUtil.java | 6 +- .../java/net/sourceforge/pmd/cpd/CPDTest.java | 4 +- .../sourceforge/pmd/cpd/CSVRendererTest.java | 2 + .../sourceforge/pmd/cpd/CPPTokenizerTest.java | 41 ++-- .../sourceforge/pmd/cpd/CsTokenizerTest.java | 9 +- .../pmd/cpd/DartTokenizerTest.java | 7 - .../pmd/cpd/FortranTokenizerTest.java | 7 - .../pmd/cpd/GherkinTokenizerTest.java | 9 - .../sourceforge/pmd/cpd/GoTokenizerTest.java | 7 - .../sourceforge/pmd/cpd/GroovyTokenizer.java | 16 +- .../pmd/cpd/GroovyTokenizerTest.java | 7 - .../pmd/lang/html/ast/HtmlTokenizer.java | 23 +-- .../pmd/lang/html/HtmlTokenizerTest.java | 9 - .../sourceforge/pmd/cpd/JavaTokenizer.java | 27 +-- .../pmd/cpd/JavaTokenizerTest.java | 7 - .../cpd/AnyTokenizerForTypescriptTest.java | 7 - .../pmd/cpd/EcmascriptTokenizerTest.java | 7 - .../sourceforge/pmd/cpd/JSPTokenizerTest.java | 7 - .../pmd/cpd/KotlinTokenizerTest.java | 7 - .../pmd/cpd/test/CpdTextComparisonTest.kt | 53 +++-- .../sourceforge/pmd/cpd/LuaTokenizerTest.java | 7 - .../pmd/cpd/MatlabTokenizerTest.java | 7 - .../pmd/cpd/ObjectiveCTokenizerTest.java | 7 - .../pmd/lang/perl/cpd/PerlTokenizerTest.java | 9 - .../net/sourceforge/pmd/cpd/PHPTokenizer.java | 20 +- .../sourceforge/pmd/cpd/PLSQLLanguage.java | 31 --- .../sourceforge/pmd/cpd/PLSQLTokenizer.java | 34 +--- .../pmd/lang/plsql/PLSQLLanguageModule.java | 22 +++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../pmd/cpd/PLSQLTokenizerTest.java | 10 +- .../pmd/cpd/PythonTokenizerTest.java | 9 +- .../pmd/cpd/RubyTokenizerTest.java | 7 - .../sourceforge/pmd/cpd/ScalaTokenizer.java | 22 +-- .../pmd/cpd/ScalaTokenizerTest.java | 7 - .../pmd/cpd/SwiftTokenizerTest.java | 7 - .../pmd/lang/vf/cpd/VfTokenizerTest.java | 10 - .../pmd/xml/cpd/XmlCPDTokenizerTest.java | 8 - 64 files changed, 543 insertions(+), 901 deletions(-) delete mode 100644 pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexLanguage.java delete mode 100644 pmd-apex/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java delete mode 100755 pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLLanguage.java delete mode 100644 pmd-plsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexLanguage.java b/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexLanguage.java deleted file mode 100644 index 0bb7bd7014..0000000000 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexLanguage.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; - -public class ApexLanguage extends AbstractLanguage { - - public ApexLanguage() { - this(new Properties()); - } - - public ApexLanguage(Properties properties) { - super("Apex", "apex", new ApexTokenizer(), ".cls"); - setProperties(properties); - } - - @Override - public final void setProperties(Properties properties) { - ApexTokenizer tokenizer = (ApexTokenizer) getTokenizer(); - tokenizer.setProperties(properties); - } -} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java index d2a01abb01..37872c2cc2 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java @@ -5,67 +5,30 @@ package net.sourceforge.pmd.cpd; import java.util.Locale; -import java.util.Properties; -import org.antlr.runtime.ANTLRStringStream; -import org.antlr.runtime.Lexer; -import org.antlr.runtime.Token; +import org.antlr.v4.runtime.CharStream; -import net.sourceforge.pmd.lang.apex.ApexJorjeLogging; -import net.sourceforge.pmd.lang.ast.TokenMgrError; -import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.lang.apex.ApexLanguageProperties; +import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; -import apex.jorje.parser.impl.ApexLexer; +public class ApexTokenizer extends AntlrTokenizer { + private final boolean caseSensitive; -public class ApexTokenizer implements Tokenizer { - - public ApexTokenizer() { - ApexJorjeLogging.disableLogging(); - } - - /** - * If the properties is false (default), then the case of any token - * is ignored. - */ - public static final String CASE_SENSITIVE = "net.sourceforge.pmd.cpd.ApexTokenizer.caseSensitive"; - - private boolean caseSensitive; - - public void setProperties(Properties properties) { - caseSensitive = Boolean.parseBoolean(properties.getProperty(CASE_SENSITIVE, "false")); + public ApexTokenizer(ApexLanguageProperties properties) { + this.caseSensitive = properties.getProperty(Tokenizer.CPD_CASE_SENSITIVE); } @Override - public void tokenize(TextDocument sourceCode, Tokens tokenEntries) { - StringBuilder code = sourceCode.getCodeBuffer(); - - ANTLRStringStream ass = new ANTLRStringStream(code.toString()); - ApexLexer lexer = new ApexLexer(ass) { - @Override - public void emitErrorMessage(String msg) { - throw new TokenMgrError(getLine(), getCharPositionInLine(), getSourceName(), msg, null); - } - }; - - try { - Token token = lexer.nextToken(); - - while (token.getType() != Token.EOF) { - if (token.getChannel() != Lexer.HIDDEN) { - String tokenText = token.getText(); - if (!caseSensitive) { - tokenText = tokenText.toLowerCase(Locale.ROOT); - } - TokenEntry tokenEntry = new TokenEntry(tokenText, sourceCode.getFileName(), - token.getLine(), - token.getCharPositionInLine() + 1, - token.getCharPositionInLine() + tokenText.length() + 1); - tokenEntries.add(tokenEntry); - } - token = lexer.nextToken(); - } - } finally { - tokenEntries.add(TokenEntry.getEOF()); + protected String getImage(AntlrToken token) { + if (caseSensitive) { + return token.getImage(); } + return token.getImage().toLowerCase(Locale.ROOT); + } + + @Override + protected org.antlr.v4.runtime.Lexer getLexerForSource(CharStream charStream) { + return new com.nawforce.runtime.parsers.ApexLexer(charStream); } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java index 7b57934bd7..690c228c0b 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.apex; +import net.sourceforge.pmd.cpd.ApexTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguageProcessor; @@ -32,6 +34,11 @@ public class ApexLanguageModule extends LanguageModuleBase { return new ApexLanguageProcessor((ApexLanguageProperties) bundle); } + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new ApexTokenizer((ApexLanguageProperties) bundle); + } + public static Language getInstance() { return LanguageRegistry.PMD.getLanguageByFullName(NAME); } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java index 1b33565565..3431c89dd5 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.apex; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -23,6 +24,7 @@ public class ApexLanguageProperties extends LanguagePropertyBundle { public ApexLanguageProperties() { super(ApexLanguageModule.getInstance()); definePropertyDescriptor(MULTIFILE_DIRECTORY); + definePropertyDescriptor(Tokenizer.CPD_CASE_SENSITIVE); } diff --git a/pmd-apex/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-apex/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index fd84d4a22b..0000000000 --- a/pmd-apex/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.ApexLanguage diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexTokenizerTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexTokenizerTest.java index aeb4a51252..47fd06778b 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexTokenizerTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexTokenizerTest.java @@ -4,16 +4,16 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.cpd.test.LanguagePropertyConfig; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; class ApexTokenizerTest extends CpdTextComparisonTest { ApexTokenizerTest() { - super(".cls"); + super(ApexLanguageModule.getInstance(), ".cls"); } @Override @@ -21,13 +21,6 @@ class ApexTokenizerTest extends CpdTextComparisonTest { return "../lang/apex/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - ApexTokenizer tokenizer = new ApexTokenizer(); - tokenizer.setProperties(properties); - return tokenizer; - } - @Test void testTokenize() { @@ -52,14 +45,12 @@ class ApexTokenizerTest extends CpdTextComparisonTest { doTest("tabWidth"); } - private Properties caseSensitive() { + private LanguagePropertyConfig caseSensitive() { return properties(true); } - private Properties properties(boolean caseSensitive) { - Properties properties = new Properties(); - properties.setProperty(ApexTokenizer.CASE_SENSITIVE, Boolean.toString(caseSensitive)); - return properties; + private LanguagePropertyConfig properties(boolean caseSensitive) { + return properties -> properties.setProperty(Tokenizer.CPD_CASE_SENSITIVE, caseSensitive); } } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index da3fdacdc0..b228f2ee92 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -116,7 +116,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { configuration.setSkipDuplicates(skipDuplicates); configuration.setSkipLexicalErrors(skipLexicalErrors); configuration.setSourceEncoding(encoding.getEncoding().name()); - configuration.setURI(uri == null ? null : uri.toString()); + configuration.setURI(uri); configuration.postContruct(); // Pass extra parameters as System properties to allow language diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java deleted file mode 100644 index 5db9827346..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractTokenizer.java +++ /dev/null @@ -1,184 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.List; -import java.util.Locale; - -import net.sourceforge.pmd.lang.document.TextDocument; - -/** - * - * @author Zev Blut zb@ubit.com - * @author Romain PELISSE belaran@gmail.com - * - * @deprecated Use an {@link AnyTokenizer} instead, it's basically as powerful. - */ -@Deprecated -public abstract class AbstractTokenizer implements Tokenizer { - - // FIXME depending on subclasses to assign local vars is rather fragile - - // better to make private and setup via explicit hook methods - - protected List stringToken; // List, should be set by sub - // classes - protected List ignorableCharacter; // List, should be set by - // sub classes - // FIXME:Maybe an array of 'char' - // would be better for - // performance ? - protected List ignorableStmt; // List, should be set by sub - // classes - protected char oneLineCommentChar = '#'; // Most script languages ( shell, - // ruby, python,...) use this - // symbol for comment line - - private List code; - private int lineNumber = 0; - private String currentLine; - - // both zero-based - private int tokBeginLine; - private int tokBeginCol; - - protected boolean spanMultipleLinesString = true; // Most languages do, so - // default is true - protected Character spanMultipleLinesLineContinuationCharacter = null; - - private boolean downcaseString = true; - - @Override - public void tokenize(TextDocument tokens, Tokens tokenEntries) { - code = tokens.getCode(); - - for (lineNumber = 0; lineNumber < code.size(); lineNumber++) { - currentLine = code.get(lineNumber); - int loc = 0; - while (loc < currentLine.length()) { - StringBuilder token = new StringBuilder(); - loc = getTokenFromLine(token, loc); // may jump several lines - - if (token.length() > 0 && !isIgnorableString(token.toString())) { - final String image; - if (downcaseString) { - image = token.toString().toLowerCase(Locale.ROOT); - } else { - image = token.toString(); - } - - tokenEntries.add(new TokenEntry(image, - tokens.getFileName(), - tokBeginLine + 1, - tokBeginCol + 1, - loc + 1)); - } - } - } - tokenEntries.add(TokenEntry.getEOF()); - } - - /** - * Returns (0-based) EXclusive offset of the end of the token, - * may jump several lines (sets {@link #lineNumber} in this case). - */ - private int getTokenFromLine(StringBuilder token, int loc) { - tokBeginLine = lineNumber; - tokBeginCol = loc; - - for (int j = loc; j < currentLine.length(); j++) { - char tok = currentLine.charAt(j); - if (!Character.isWhitespace(tok) && !ignoreCharacter(tok)) { - if (isComment(tok)) { - if (token.length() > 0) { - return j; - } else { - return getCommentToken(token, loc); - } - } else if (isString(tok)) { - if (token.length() > 0) { - return j; // we need to now parse the string as a - // separate token. - } else { - // we are at the start of a string - return parseString(token, j, tok); - } - } else { - token.append(tok); - } - } else { - if (token.length() > 0) { - return j; - } else { - // ignored char - tokBeginCol++; - } - } - loc = j; - } - return loc + 1; - } - - private int parseString(StringBuilder token, int loc, char stringDelimiter) { - boolean escaped = false; - boolean done = false; - char tok; - while (loc < currentLine.length() && !done) { - tok = currentLine.charAt(loc); - if (escaped && tok == stringDelimiter) { // Found an escaped string - escaped = false; - } else if (tok == stringDelimiter && token.length() > 0) { - // We are done, we found the end of the string... - done = true; - } else { - // Found an escaped char? - escaped = tok == '\\'; - } - // Adding char to String:" + token.toString()); - token.append(tok); - loc++; - } - // Handling multiple lines string - if (!done // ... we didn't find the end of the string (but the end of the line) - && spanMultipleLinesString // ... the language allow multiple line span Strings - && lineNumber < code.size() - 1 // ... there is still more lines to parse - ) { - // removes last character, if it is the line continuation (e.g. - // backslash) character - if (spanMultipleLinesLineContinuationCharacter != null - && token.length() > 0 - && token.charAt(token.length() - 1) == spanMultipleLinesLineContinuationCharacter) { - token.setLength(token.length() - 1); - } - // parsing new line - currentLine = code.get(++lineNumber); - // Warning : recursive call ! - loc = parseString(token, 0, stringDelimiter); - } - return loc; - } - - private boolean ignoreCharacter(char tok) { - return ignorableCharacter.contains(String.valueOf(tok)); - } - - private boolean isString(char tok) { - return stringToken.contains(String.valueOf(tok)); - } - - private boolean isComment(char tok) { - return tok == oneLineCommentChar; - } - - private int getCommentToken(StringBuilder token, int loc) { - while (loc < currentLine.length()) { - token.append(currentLine.charAt(loc++)); - } - return loc; - } - - private boolean isIgnorableString(String token) { - return ignorableStmt.contains(token); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java index 6e02dda6b8..671644eae6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java @@ -9,6 +9,7 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.util.StringUtil; @@ -61,36 +62,32 @@ public class AnyTokenizer implements Tokenizer { } @Override - public void tokenize(TextDocument sourceCode, Tokens tokenEntries) { - CharSequence text = sourceCode.getCodeBuffer(); + public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { + Chars text = sourceCode.getText(); Matcher matcher = pattern.matcher(text); int lineNo = 1; int lastLineStart = 0; - try { - while (matcher.find()) { - String image = matcher.group(); - if (isComment(image)) { - continue; - } else if (StringUtils.isWhitespace(image)) { - lineNo++; - lastLineStart = matcher.end(); - continue; - } - - int bline = lineNo; - int bcol = 1 + matcher.start() - lastLineStart; // + 1 because columns are 1 based - int ecol = StringUtil.columnNumberAt(image, image.length()); // this already outputs a 1-based column - if (ecol == image.length() + 1) { - ecol = bcol + image.length(); // single-line token - } else { - // multiline, need to update the line count - lineNo += StringUtil.lineNumberAt(image, image.length()) - 1; - lastLineStart = matcher.start() + image.length() - ecol + 1; - } - tokenEntries.add(new TokenEntry(image, sourceCode.getFileName(), bline, bcol, ecol)); + while (matcher.find()) { + String image = matcher.group(); + if (isComment(image)) { + continue; + } else if (StringUtils.isWhitespace(image)) { + lineNo++; + lastLineStart = matcher.end(); + continue; } - } finally { - tokenEntries.add(TokenEntry.getEOF()); + + int bline = lineNo; + int bcol = 1 + matcher.start() - lastLineStart; // + 1 because columns are 1 based + int ecol = StringUtil.columnNumberAt(image, image.length()); // this already outputs a 1-based column + if (ecol == image.length() + 1) { + ecol = bcol + image.length(); // single-line token + } else { + // multiline, need to update the line count + lineNo += StringUtil.lineNumberAt(image, image.length()) - 1; + lastLineStart = matcher.start() + image.length() - ecol + 1; + } + tokenEntries.recordToken(image, bline, bcol, lineNo, ecol); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index a4bf7e24c2..3c8a6e0265 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -27,6 +27,9 @@ import net.sourceforge.pmd.internal.util.FileFinder; import net.sourceforge.pmd.internal.util.FileUtil; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.ast.TokenMgrError; +import net.sourceforge.pmd.lang.document.SourceCode; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -41,7 +44,7 @@ public class CPD { private CPDConfiguration configuration; - private Map source = new TreeMap<>(); + private final SourceManager sourceManager = new SourceManager(); private CPDListener listener = new CPDNullListener(); private Tokens tokens = new Tokens(); private MatchAlgorithm matchAlgorithm; @@ -128,8 +131,8 @@ public class CPD { } public void go() { - log.debug("Running match algorithm on {} files...", source.size()); - matchAlgorithm = new MatchAlgorithm(source, tokens, configuration.getMinimumTileSize(), listener); + log.debug("Running match algorithm on {} files...", sourceManager.size()); + matchAlgorithm = new MatchAlgorithm(sourceManager, tokens, configuration.getMinimumTileSize(), listener); matchAlgorithm.findMatches(); log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); } @@ -216,8 +219,7 @@ public class CPD { } } - @Experimental - public void add(SourceCode sourceCode) throws IOException { + private void add(SourceCode sourceCode) throws IOException { if (configuration.isSkipLexicalErrors()) { addAndSkipLexicalErrors(sourceCode); } else { @@ -226,11 +228,13 @@ public class CPD { } private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException { - log.debug("Tokenizing {}", sourceCode.getFileName()); - configuration.tokenizer().tokenize(sourceCode, tokens); - listener.addedFile(1, new File(sourceCode.getFileName())); - source.put(sourceCode.getFileName(), sourceCode); - numberOfTokensPerFile.put(sourceCode.getFileName(), tokens.size() - lastTokenSize - 1 /*EOF*/); + log.debug("Tokenizing {}", sourceCode.getPathId()); + try (TextDocument doc = sourceCode.load()) { + configuration.tokenizer().tokenize(doc, tokens); + } + listener.addedFile(1); + source.put(sourceCode.getPathId(), sourceCode); + numberOfTokensPerFile.put(sourceCode.getPathId(), tokens.size() - lastTokenSize - 1 /*EOF*/); lastTokenSize = tokens.size(); } @@ -239,7 +243,7 @@ public class CPD { try { addAndThrowLexicalError(sourceCode); } catch (TokenMgrError e) { - System.err.println("Skipping " + sourceCode.getFileName() + ". Reason: " + e.getMessage()); + System.err.println("Skipping " + sourceCode.getDisplayName() + ". Reason: " + e.getMessage()); savedState.restore(tokens); } } @@ -253,15 +257,6 @@ public class CPD { return new ArrayList<>(source.keySet()); } - /** - * Get each Source to be processed. - * - * @return all Sources to be processed - */ - public List getSources() { - return new ArrayList<>(source.values()); - } - /** * Entry to invoke CPD as command line tool. Note that this will * invoke {@link System#exit(int)}. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index efe6550261..2a4950d2f8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -11,6 +11,7 @@ import java.io.FilenameFilter; import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.URI; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -24,6 +25,8 @@ import net.sourceforge.pmd.AbstractConfiguration; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.internal.util.FileFinder; import net.sourceforge.pmd.internal.util.FileUtil; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.document.TextFile; /** * @@ -79,7 +82,7 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean nonRecursive; - private String uri; + private URI uri; private boolean help; @@ -87,7 +90,7 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean debug = false; - public SourceCode sourceCodeFor(File file) { + public TextFile sourceCodeFor(File file) { return new SourceCode(new SourceCode.FileCodeLoader(file, getSourceEncoding().name())); } @@ -340,11 +343,11 @@ public class CPDConfiguration extends AbstractConfiguration { this.fileListPath = fileListPath; } - public String getURI() { + public URI getURI() { return uri; } - public void setURI(String uri) { + public void setURI(URI uri) { this.uri = uri; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDListener.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDListener.java index ae180b4c97..6f361d1afb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDListener.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDListener.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.io.File; - public interface CPDListener { int INIT = 0; @@ -14,7 +12,7 @@ public interface CPDListener { int GROUPING = 3; int DONE = 4; - void addedFile(int fileCount, File file); + void addedFile(int fileCount); void phaseUpdate(int phase); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDNullListener.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDNullListener.java index 64b6060166..3566a9a6cf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDNullListener.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDNullListener.java @@ -4,11 +4,9 @@ package net.sourceforge.pmd.cpd; -import java.io.File; - public class CPDNullListener implements CPDListener { @Override - public void addedFile(int fileCount, File file) { + public void addedFile(int fileCount) { // does nothing - override it if necessary } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java new file mode 100644 index 0000000000..e0cd1aa5b2 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -0,0 +1,147 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.sourceforge.pmd.internal.util.FileCollectionUtil; +import net.sourceforge.pmd.internal.util.FileFinder; +import net.sourceforge.pmd.internal.util.FileUtil; +import net.sourceforge.pmd.internal.util.IOUtil; +import net.sourceforge.pmd.lang.ast.TokenMgrError; +import net.sourceforge.pmd.lang.document.FileCollector; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.util.database.DBMSMetadata; +import net.sourceforge.pmd.util.database.DBURI; +import net.sourceforge.pmd.util.database.SourceObject; +import net.sourceforge.pmd.util.log.MessageReporter; + +/** + * @deprecated Use the module pmd-cli for CLI support. + */ +@Deprecated +public class CpdAnalysis { + + private CPDConfiguration configuration; + private FileCollector files; + private MessageReporter reporter; + private CPDListener listener; + + + public CpdAnalysis(CPDConfiguration theConfiguration) { + configuration = theConfiguration; + + // Add all sources + try { + extractAllSources(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public FileCollector files() { + return files; + } + + private void extractAllSources() throws IOException { + // Add files + if (null != configuration.getFiles() && !configuration.getFiles().isEmpty()) { + addSourcesFilesToCPD(configuration.getFiles()); + } + + // Add Database URIS + if (null != configuration.getURI()) { + FileCollectionUtil.collectDB(files(), configuration.getURI()); + } + + if (null != configuration.getFileListPath()) { + FileCollectionUtil.collectFileList(files(), FileUtil.toExistingPath(configuration.getFileListPath())); + } + } + + private void addSourcesFilesToCPD(List files) throws IOException { + for (File file : files) { + files().addFileOrDirectory(file.toPath()); + } + } + + public void setCpdListener(CPDListener cpdListener) { + this.listener = cpdListener; + } + + private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException { + log.debug("Tokenizing {}", sourceCode.getPathId()); + try (TextDocument doc = sourceCode.load()) { + configuration.tokenizer().tokenize(doc, tokens); + } + listener.addedFile(1); + source.put(sourceCode.getPathId(), sourceCode); + numberOfTokensPerFile.put(sourceCode.getPathId(), tokens.size() - lastTokenSize - 1 /*EOF*/); + lastTokenSize = tokens.size(); + } + + public CPDReport performAnalysis() { + + try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) { + Tokens tokens = new Tokens(); + + + log.debug("Running match algorithm on {} files...", sourceManager.size()); + MatchAlgorithm matchAlgorithm = new MatchAlgorithm(sourceManager, tokens, configuration.getMinimumTileSize(), listener); + matchAlgorithm.findMatches(); + log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); + + + + } catch (Exception e) { + reporter.errorEx("Exception while running CPD", e); + } + } + + public void add(File file) throws IOException { + + if (configuration.isSkipDuplicates()) { + // TODO refactor this thing into a separate class + String signature = file.getName() + '_' + file.length(); + if (current.contains(signature)) { + System.err.println("Skipping " + file.getAbsolutePath() + + " since it appears to be a duplicate file and --skip-duplicate-files is set"); + return; + } + current.add(signature); + } + + if (!IOUtil.equalsNormalizedPaths(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) { + System.err.println("Skipping " + file + " since it appears to be a symlink"); + return; + } + + if (!file.exists()) { + System.err.println("Skipping " + file + " since it doesn't exist (broken symlink?)"); + return; + } + + SourceCode sourceCode = configuration.sourceCodeFor(file); + add(sourceCode); + } + + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index d3aa2ae9eb..13fd308305 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -911,7 +911,7 @@ public class GUI implements CPDListener { } @Override - public void addedFile(int fileCount, File file) { + public void addedFile(int fileCount) { tokenizingFilesBar.setMaximum(fileCount); tokenizingFilesBar.setValue(tokenizingFilesBar.getValue() + 1); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index 30e13c4b04..4cfe7dfd73 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -4,11 +4,14 @@ package net.sourceforge.pmd.cpd; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextDocument; + public class Mark implements Comparable { private TokenEntry token; private TokenEntry endToken; private int lineCount; - private SourceCode code; + private TextDocument code; public Mark(TokenEntry token) { this.token = token; @@ -69,11 +72,13 @@ public class Mark implements Comparable { } /** Newlines are normalized to \n. */ - public String getSourceCodeSlice() { - return this.code.getSlice(getBeginLine(), getEndLine()); + public Chars getSourceCodeSlice() { + return this.code.sliceOriginalText( + this.code.createLineRange(getBeginLine(), getEndLine()) + ); } - public void setSourceCode(SourceCode code) { + public void setSourceCode(TextDocument code) { this.code = code; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java index 992f551e9c..3dbb8e2a9e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.TreeSet; import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.lang.document.Chars; public class Match implements Comparable, Iterable { @@ -74,7 +75,7 @@ public class Match implements Comparable, Iterable { } /** Newlines are normalized to \n. */ - public String getSourceCodeSlice() { + public Chars getSourceCodeSlice() { return this.getMark(0).getSourceCodeSlice(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java index d6f482dd05..ca198f032f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java @@ -11,23 +11,25 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -public class MatchAlgorithm { +import net.sourceforge.pmd.lang.document.TextDocument; + +class MatchAlgorithm { private static final int MOD = 37; private int lastMod = 1; private List matches; - private Map source; + private Map source; private Tokens tokens; private List code; private CPDListener cpdListener; private int min; - public MatchAlgorithm(Map sourceCode, Tokens tokens, int min) { + public MatchAlgorithm(Map sourceCode, Tokens tokens, int min) { this(sourceCode, tokens, min, new CPDNullListener()); } - public MatchAlgorithm(Map sourceCode, Tokens tokens, int min, CPDListener listener) { + public MatchAlgorithm(SourceManager sourceCode, Tokens tokens, int min, CPDListener listener) { this.source = sourceCode; this.tokens = tokens; this.code = tokens.getTokens(); @@ -85,7 +87,7 @@ public class MatchAlgorithm { mark.setLineCount(lineCount); mark.setEndToken(endToken); - SourceCode sourceCode = source.get(token.getTokenSrcID()); + TextDocument sourceCode = source.get(token.getTokenSrcID()); assert sourceCode != null : token.getTokenSrcID() + " is not registered in " + source.keySet(); mark.setSourceCode(sourceCode); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java index 8595b38c3c..9ec4b99bc7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java @@ -60,7 +60,7 @@ public class SimpleRenderer implements CPDReportRenderer { writer.append(PMD.EOL); // add a line to separate the source from the desc above - String source = match.getSourceCodeSlice(); + Chars source = match.getSourceCodeSlice(); if (trimLeadingWhitespace) { for (Chars line : StringUtil.linesWithTrimIndent(source)) { @@ -70,7 +70,8 @@ public class SimpleRenderer implements CPDReportRenderer { return; } - writer.append(source).append(PMD.EOL); + source.writeFully(writer); + writer.append(PMD.EOL); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java index bd93023de7..34288552cd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java @@ -4,190 +4,32 @@ package net.sourceforge.pmd.cpd; -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStreamReader; +import java.io.IOException; import java.io.Reader; -import java.io.StringReader; import java.lang.ref.SoftReference; -import java.nio.file.Files; -import java.util.ArrayList; import java.util.List; -import net.sourceforge.pmd.internal.util.IOUtil; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.document.TextFile; public class SourceCode { - public abstract static class CodeLoader { - private SoftReference> code; + private SoftReference softRef; + private final TextFile textFile; - public List getCode() { - List c = null; - if (code != null) { - c = code.get(); - } - if (c != null) { - return c; - } - this.code = new SoftReference<>(load()); - return code.get(); - } - - /** - * Loads a range of lines. - * - * @param startLine Start line (inclusive, 1-based) - * @param endLine End line (inclusive, 1-based) - */ - public List getCodeSlice(int startLine, int endLine) { - List c = null; - if (code != null) { - c = code.get(); - } - if (c != null) { - return c.subList(startLine - 1, endLine); - } - return load(startLine, endLine); - } - - public abstract String getFileName(); - - protected abstract Reader getReader() throws Exception; - - protected List load() { - try (BufferedReader reader = new BufferedReader(getReader())) { - List lines = new ArrayList<>(); - String currentLine; - while ((currentLine = reader.readLine()) != null) { - lines.add(currentLine); - } - return lines; - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("Problem while reading " + getFileName() + ":" + e.getMessage()); - } - } - - /** - * Loads a range of lines. - * - * @param startLine Start line (inclusive, 1-based) - * @param endLine End line (inclusive, 1-based) - */ - protected List load(int startLine, int endLine) { - try (BufferedReader reader = new BufferedReader(getReader())) { - int linesToRead = 1 + endLine - startLine; // +1 because endLine is inclusive - List lines = new ArrayList<>(linesToRead); - - // Skip lines until we reach the start point - for (int i = 0; i < startLine - 1; i++) { - reader.readLine(); - } - - String currentLine; - while ((currentLine = reader.readLine()) != null) { - lines.add(currentLine); - - if (lines.size() == linesToRead) { - break; - } - } - return lines; - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("Problem while reading " + getFileName() + ":" + e.getMessage()); - } - } + public SourceCode(TextFile textFile) { + this.textFile = textFile; } - public static class FileCodeLoader extends CodeLoader { - private File file; - private String encoding; - - public FileCodeLoader(File file, String encoding) { - this.file = file; - this.encoding = encoding; - } - - @Override - public Reader getReader() throws Exception { - IOUtil.BomAwareInputStream inputStream = new IOUtil.BomAwareInputStream(Files.newInputStream(file.toPath())); - - if (inputStream.hasBom()) { - encoding = inputStream.getBomCharsetName(); - } - return new InputStreamReader(inputStream, encoding); - } - - public String getEncoding() { - return encoding; - } - - @Override - public String getFileName() { - return file.getAbsolutePath(); + public TextDocument load() throws IOException { + if (softRef != null && softRef.get() != null) { + return softRef.get(); } + TextDocument doc = TextDocument.create(textFile); + softRef = new SoftReference<>(doc); + return doc; } - public static class StringCodeLoader extends CodeLoader { - public static final String DEFAULT_NAME = "CODE_LOADED_FROM_STRING"; - - private String code; - - private String name; - - public StringCodeLoader(String code) { - this(code, DEFAULT_NAME); - } - - public StringCodeLoader(String code, String name) { - this.code = code; - this.name = name; - } - - @Override - public Reader getReader() { - return new StringReader(code); - } - - @Override - public String getFileName() { - return name; - } - } - - public static class ReaderCodeLoader extends CodeLoader { - public static final String DEFAULT_NAME = "CODE_LOADED_FROM_READER"; - - private Reader code; - - private String name; - - public ReaderCodeLoader(Reader code) { - this(code, DEFAULT_NAME); - } - - public ReaderCodeLoader(Reader code, String name) { - this.code = code; - this.name = name; - } - - @Override - public Reader getReader() { - return code; - } - - @Override - public String getFileName() { - return name; - } - } - - private CodeLoader cl; - - public SourceCode(CodeLoader cl) { - this.cl = cl; - } public List getCode() { return cl.getCode(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java new file mode 100644 index 0000000000..bbcc75e051 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -0,0 +1,43 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import net.sourceforge.pmd.internal.util.IOUtil; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.document.TextFile; + +public class SourceManager implements AutoCloseable { + + private final Map> files = new ConcurrentHashMap<>(); + private final List textFiles; + + public SourceManager(List files) { + textFiles = new ArrayList<>(files); + } + + + TextDocument get(String pathId) { + + } + + public int size() { + return files.size(); + } + + + @Override + public void close() throws Exception { + Exception exception = IOUtil.closeAll(textFiles); + if (exception != null) { + throw exception; + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java index 2d5ad99b4a..96c89cebda 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java @@ -25,19 +25,6 @@ public class TokenEntry implements Comparable { private int identifier; private int hashCode; - private static final ThreadLocal> TOKENS = new ThreadLocal>() { - @Override - protected Map initialValue() { - return new HashMap<>(); - } - }; - private static final ThreadLocal TOKEN_COUNT = new ThreadLocal() { - @Override - protected AtomicInteger initialValue() { - return new AtomicInteger(0); - } - }; - private TokenEntry() { this.identifier = 0; this.tokenSrcID = "EOFMarker"; @@ -59,14 +46,6 @@ public class TokenEntry implements Comparable { this(image, tokenSrcID, beginLine, -1, -1); } - /** - * Creates a new token entry with the given informations. - * @param image - * @param tokenSrcID - * @param beginLine the linenumber, 1-based. - * @param beginColumn the column number, 1-based - * @param endColumn the column number, 1-based - */ public TokenEntry(String image, String tokenSrcID, int beginLine, int beginColumn, int endColumn) { assert isOk(beginLine) && isOk(beginColumn) && isOk(endColumn) : "Coordinates are 1-based"; setImage(image); @@ -74,7 +53,6 @@ public class TokenEntry implements Comparable { this.beginLine = beginLine; this.beginColumn = beginColumn; this.endColumn = endColumn; - this.index = TOKEN_COUNT.get().getAndIncrement(); } public TokenEntry(String image, FileLocation location) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java new file mode 100644 index 0000000000..d72821f4ab --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java @@ -0,0 +1,42 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import net.sourceforge.pmd.lang.document.FileLocation; +import net.sourceforge.pmd.lang.document.TextFile; + +public interface TokenFactory { + + void recordToken(String image, int startLine, int startCol, int endLine, int endCol); + + default void recordToken(String image, FileLocation location) { + recordToken(image, location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()); + } + + void setImage(TokenEntry entry, String newImage); + + TokenEntry peekLastToken(); + + static TokenFactory forFile(TextFile file, Tokens sink) { + return new TokenFactory() { + final String name = file.getPathId(); + + @Override + public void recordToken(String image, int startLine, int startCol, int endLine, int endCol) { + sink.addToken(image, name, startLine, startCol, endLine, endCol); + } + + @Override + public void setImage(TokenEntry entry, String newImage) { + sink.setImage(entry, newImage); + } + + @Override + public TokenEntry peekLastToken() { + return sink.peekLastToken(); + } + }; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index 2e0d77f770..8abe88cd86 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -43,6 +43,13 @@ public interface Tokenizer { .build(); + PropertyDescriptor CPD_CASE_SENSITIVE = + PropertyFactory.booleanProperty("cpdCaseSensitive") + .defaultValue(true) + .desc("Whether CPD should ignore the case of tokens. Affects all tokens.") + .build(); + + String IGNORE_LITERALS = "ignore_literals"; String IGNORE_IDENTIFIERS = "ignore_identifiers"; String IGNORE_ANNOTATIONS = "ignore_annotations"; @@ -75,5 +82,5 @@ public interface Tokenizer { String DEFAULT_SKIP_BLOCKS_PATTERN = "#if 0|#endif"; - void tokenize(TextDocument sourceCode, Tokens tokenEntries) throws IOException; + void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) throws IOException; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 0840613dcc..1be2d32a5c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -5,17 +5,47 @@ package net.sourceforge.pmd.cpd; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import net.sourceforge.pmd.lang.document.FileLocation; public class Tokens { - private List tokens = new ArrayList<>(); + private final List tokens = new ArrayList<>(); + + private static final ThreadLocal> TOKENS = new ThreadLocal>() { + @Override + protected Map initialValue() { + return new HashMap<>(); + } + }; + private static final ThreadLocal TOKEN_COUNT = new ThreadLocal() { + @Override + protected AtomicInteger initialValue() { + return new AtomicInteger(0); + } + }; public void add(TokenEntry tokenEntry) { this.tokens.add(tokenEntry); } + public void addToken(String image, FileLocation location) { + this.tokens.add(new TokenEntry(image, location)); + } + + public void setImage(TokenEntry entry, String newImage) { + entry.setImage(newImage); + } + + public TokenEntry peekLastToken() { + return get(size() - 1); + } + public Iterator iterator() { return tokens.iterator(); } @@ -43,4 +73,8 @@ public class Tokens { public List getTokens() { return tokens; } + + void addToken(String image, String fileName, int startLine, int startCol, int endLine, int endCol) { + + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java index 07d6e9894f..6168c2f58b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java @@ -6,9 +6,8 @@ package net.sourceforge.pmd.cpd.internal; import java.io.IOException; -import net.sourceforge.pmd.cpd.TokenEntry; +import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.Tokens; import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.GenericToken; @@ -22,8 +21,8 @@ public abstract class TokenizerBase> implements Tokeni return new BaseTokenFilter<>(tokenManager); } - protected TokenEntry processToken(Tokens tokenEntries, T currentToken) { - return new TokenEntry(getImage(currentToken), currentToken.getReportLocation()); + protected void processToken(TokenFactory tokenEntries, T currentToken) { + tokenEntries.recordToken(getImage(currentToken), currentToken.getReportLocation()); } protected String getImage(T token) { @@ -31,11 +30,11 @@ public abstract class TokenizerBase> implements Tokeni } @Override - public void tokenize(TextDocument document, Tokens tokenEntries) throws IOException { + public void tokenize(TextDocument document, TokenFactory tokenEntries) throws IOException { TokenManager tokenManager = filterTokenStream(makeLexerImpl(document)); T currentToken = tokenManager.getNextToken(); while (currentToken != null) { - tokenEntries.add(processToken(tokenEntries, currentToken)); + processToken(tokenEntries, currentToken); currentToken = tokenManager.getNextToken(); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java index 516851b167..55c9a1c952 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java @@ -343,11 +343,16 @@ public final class FileCollector implements AutoCloseable { * @return True if the directory has been added */ public boolean addDirectory(Path dir) throws IOException { + return addDirectory(dir, true); + } + + public boolean addDirectory(Path dir, boolean recurse) throws IOException { if (!Files.isDirectory(dir)) { reporter.error("Not a directory {0}", dir); return false; } - Files.walkFileTree(dir, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor() { + int maxDepth = recurse ? Integer.MAX_VALUE : 1; + Files.walkFileTree(dir, EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (attrs.isRegularFile()) { @@ -367,8 +372,18 @@ public final class FileCollector implements AutoCloseable { * @return True if the file or directory has been added */ public boolean addFileOrDirectory(Path file) throws IOException { + return addFileOrDirectory(file, true); + } + + /** + * Add a file or directory recursively. Language is determined automatically + * from the extension/file patterns. + * + * @return True if the file or directory has been added + */ + public boolean addFileOrDirectory(Path file, boolean recurseIfDirectory) throws IOException { if (Files.isDirectory(file)) { - return addDirectory(file); + return addDirectory(file, recurseIfDirectory); } else if (Files.isRegularFile(file)) { return addFile(file); } else { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java index 1913a713ff..c6413b59ee 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.util; import java.text.MessageFormat; -import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; @@ -291,9 +290,8 @@ public final class StringUtil { /** * Returns a list of */ - public static List linesWithTrimIndent(String source) { - List lines = Arrays.asList(source.split("\n")); - List result = lines.stream().map(Chars::wrap).collect(CollectionUtil.toMutableList()); + public static List linesWithTrimIndent(Chars source) { + List result = source.lineStream().collect(CollectionUtil.toMutableList()); trimIndentInPlace(result); return result; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDTest.java index a4f966ca1f..8ac89bcf74 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDTest.java @@ -146,10 +146,10 @@ class CPDTest { } @Override - public void addedFile(int fileCount, File file) { + public void addedFile(int fileCount) { files++; if (files > expectedFilesCount) { - fail("File was added! - " + file); + fail("File was added!"); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java index 942e838c34..d06d1e7c47 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java @@ -56,6 +56,8 @@ class CSVRendererTest { } private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) { + Tokens tokens = new Tokens(); + tokens.addToken(image, ); Mark result = new Mark(new TokenEntry(image, tokenSrcID, beginLine)); result.setLineCount(lineCount); diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java index b4746b7f64..64cc27eb69 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java @@ -6,16 +6,17 @@ package net.sourceforge.pmd.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Properties; - +import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.cpd.test.LanguagePropertyConfig; +import net.sourceforge.pmd.lang.cpp.CppLanguageModule; class CPPTokenizerTest extends CpdTextComparisonTest { CPPTokenizerTest() { - super(".cpp"); + super(CppLanguageModule.getInstance(), ".cpp"); } @Override @@ -24,14 +25,7 @@ class CPPTokenizerTest extends CpdTextComparisonTest { } @Override - public Tokenizer newTokenizer(Properties props) { - CPPTokenizer tok = new CPPTokenizer(); - tok.setProperties(props); - return tok; - } - - @Override - public Properties defaultProperties() { + public @NonNull LanguagePropertyConfig defaultProperties() { return dontSkipBlocks(); } @@ -139,29 +133,30 @@ class CPPTokenizerTest extends CpdTextComparisonTest { doTest("listOfNumbers", "_ignored", skipLiteralSequences()); } - private static Properties skipBlocks(String skipPattern) { + private static LanguagePropertyConfig skipBlocks(String skipPattern) { return properties(true, skipPattern, false); } - private static Properties skipBlocks() { + private static LanguagePropertyConfig skipBlocks() { return skipBlocks(null); } - private static Properties dontSkipBlocks() { + private static LanguagePropertyConfig dontSkipBlocks() { return properties(false, null, false); } - private static Properties skipLiteralSequences() { + private static LanguagePropertyConfig skipLiteralSequences() { return properties(false, null, true); } - private static Properties properties(boolean skipBlocks, String skipPattern, boolean skipLiteralSequences) { - Properties properties = new Properties(); - properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS, Boolean.toString(skipBlocks)); - if (skipPattern != null) { - properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS_PATTERN, skipPattern); - } - properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(skipLiteralSequences)); - return properties; + private static LanguagePropertyConfig properties(boolean skipBlocks, String skipPattern, boolean skipLiteralSequences) { + return properties -> { + if (!skipBlocks) { + properties.setProperty(CppLanguageModule.CPD_SKIP_BLOCKS, ""); + } else if (skipPattern != null) { + properties.setProperty(CppLanguageModule.CPD_SKIP_BLOCKS, skipPattern); + } + properties.setProperty(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES, skipLiteralSequences); + }; } } diff --git a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java index 0901187e79..d2d3e78f80 100644 --- a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java +++ b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java @@ -8,9 +8,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Properties; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.ast.TokenMgrError; class CsTokenizerTest extends CpdTextComparisonTest { @@ -24,13 +26,6 @@ class CsTokenizerTest extends CpdTextComparisonTest { return "../lang/cs/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - CsTokenizer tok = new CsTokenizer(); - tok.setProperties(properties); - return tok; - } - @Test void testSimpleClass() { doTest("simpleClass"); diff --git a/pmd-dart/src/test/java/net/sourceforge/pmd/cpd/DartTokenizerTest.java b/pmd-dart/src/test/java/net/sourceforge/pmd/cpd/DartTokenizerTest.java index 5d08b1b849..bf89bff6a5 100644 --- a/pmd-dart/src/test/java/net/sourceforge/pmd/cpd/DartTokenizerTest.java +++ b/pmd-dart/src/test/java/net/sourceforge/pmd/cpd/DartTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -16,11 +14,6 @@ class DartTokenizerTest extends CpdTextComparisonTest { super(".dart"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new DartTokenizer(); - } - @Test void testComment() { diff --git a/pmd-fortran/src/test/java/net/sourceforge/pmd/cpd/FortranTokenizerTest.java b/pmd-fortran/src/test/java/net/sourceforge/pmd/cpd/FortranTokenizerTest.java index c07b363a0a..54adb90fa5 100644 --- a/pmd-fortran/src/test/java/net/sourceforge/pmd/cpd/FortranTokenizerTest.java +++ b/pmd-fortran/src/test/java/net/sourceforge/pmd/cpd/FortranTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -25,11 +23,6 @@ class FortranTokenizerTest extends CpdTextComparisonTest { return "../lang/fortran/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new FortranLanguage().getTokenizer(); - } - @Test void testSample() { doTest("sample"); diff --git a/pmd-gherkin/src/test/java/net/sourceforge/pmd/cpd/GherkinTokenizerTest.java b/pmd-gherkin/src/test/java/net/sourceforge/pmd/cpd/GherkinTokenizerTest.java index 9b0f20c667..86ba9bf7ab 100644 --- a/pmd-gherkin/src/test/java/net/sourceforge/pmd/cpd/GherkinTokenizerTest.java +++ b/pmd-gherkin/src/test/java/net/sourceforge/pmd/cpd/GherkinTokenizerTest.java @@ -4,12 +4,9 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; -import net.sourceforge.pmd.lang.gherkin.cpd.GherkinTokenizer; class GherkinTokenizerTest extends CpdTextComparisonTest { GherkinTokenizerTest() { @@ -21,12 +18,6 @@ class GherkinTokenizerTest extends CpdTextComparisonTest { return "../lang/gherkin/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - GherkinTokenizer tok = new GherkinTokenizer(); - return tok; - } - @Test void testAnnotatedSource() { doTest("annotatedSource"); diff --git a/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java b/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java index eada52e8f1..b40d4ff3bb 100644 --- a/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java +++ b/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -16,11 +14,6 @@ class GoTokenizerTest extends CpdTextComparisonTest { super(".go"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new GoTokenizer(); - } - @Override protected String getResourcePrefix() { return "../lang/go/cpd/testdata"; diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java index 79ecf7b6b5..1b9283c279 100644 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.io.StringReader; - import org.codehaus.groovy.antlr.SourceInfo; import org.codehaus.groovy.antlr.parser.GroovyLexer; @@ -22,10 +20,8 @@ import groovyjarjarantlr.TokenStreamException; public class GroovyTokenizer implements Tokenizer { @Override - public void tokenize(TextDocument sourceCode, Tokens tokenEntries) { - StringBuilder buffer = sourceCode.getCodeBuffer(); - - GroovyLexer lexer = new GroovyLexer(new StringReader(buffer.toString())); + public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { + GroovyLexer lexer = new GroovyLexer(sourceCode.newReader()); TokenStream tokenStream = lexer.plumb(); try { @@ -36,15 +32,17 @@ public class GroovyTokenizer implements Tokenizer { int lastCol; + int lastLine; if (token instanceof SourceInfo) { lastCol = ((SourceInfo) token).getColumnLast(); + lastLine = ((SourceInfo) token).getLineLast(); } else { // fallback lastCol = token.getColumn() + tokenText.length(); + lastLine = token.getLine(); // todo inaccurate } - TokenEntry tokenEntry = new TokenEntry(tokenText, sourceCode.getFileName(), token.getLine(), token.getColumn(), lastCol); - tokenEntries.add(tokenEntry); + tokenEntries.recordToken(tokenText, token.getLine(), token.getColumn(), lastLine, lastCol); token = tokenStream.nextToken(); } } catch (TokenStreamException err) { @@ -53,8 +51,6 @@ public class GroovyTokenizer implements Tokenizer { // when CPD is executed with the '--skipLexicalErrors' command line // option throw new TokenMgrError(lexer.getLine(), lexer.getColumn(), lexer.getFilename(), err.getMessage(), err); - } finally { - tokenEntries.add(TokenEntry.getEOF()); } } } diff --git a/pmd-groovy/src/test/java/net/sourceforge/pmd/cpd/GroovyTokenizerTest.java b/pmd-groovy/src/test/java/net/sourceforge/pmd/cpd/GroovyTokenizerTest.java index a11b546a87..0cbb81b7d5 100644 --- a/pmd-groovy/src/test/java/net/sourceforge/pmd/cpd/GroovyTokenizerTest.java +++ b/pmd-groovy/src/test/java/net/sourceforge/pmd/cpd/GroovyTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -21,11 +19,6 @@ class GroovyTokenizerTest extends CpdTextComparisonTest { return "../lang/groovy/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new GroovyTokenizer(); - } - @Test void testSample() { doTest("sample"); diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java index 8f70dc69f0..63fa4e45b6 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java @@ -7,33 +7,25 @@ package net.sourceforge.pmd.lang.html.ast; import java.io.IOException; import java.io.UncheckedIOException; -import net.sourceforge.pmd.cpd.TokenEntry; +import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.Tokens; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguageProcessorRegistry; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.html.HtmlLanguageModule; public class HtmlTokenizer implements Tokenizer { @Override - public void tokenize(TextDocument sourceCode, Tokens tokenEntries) { + public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { HtmlLanguageModule html = HtmlLanguageModule.getInstance(); - try (LanguageProcessor processor = html.createProcessor(html.newPropertyBundle()); - TextFile tf = TextFile.forCharSeq( - sourceCode.getCodeBuffer(), - sourceCode.getFileName(), - html.getDefaultVersion() - ); - TextDocument textDoc = TextDocument.create(tf)) { + try (LanguageProcessor processor = html.createProcessor(html.newPropertyBundle())) { ParserTask task = new ParserTask( - textDoc, + sourceCode, SemanticErrorReporter.noop(), LanguageProcessorRegistry.singleton(processor) ); @@ -46,20 +38,17 @@ public class HtmlTokenizer implements Tokenizer { throw new UncheckedIOException(e); } catch (Exception e) { throw new RuntimeException(e); - } finally { - tokenEntries.add(TokenEntry.EOF); } } - private void traverse(HtmlNode node, Tokens tokenEntries) { + private void traverse(HtmlNode node, TokenFactory tokenEntries) { String image = node.getXPathNodeName(); if (node instanceof ASTHtmlTextNode) { image = ((ASTHtmlTextNode) node).getText(); } - TokenEntry token = new TokenEntry(image, node.getReportLocation()); - tokenEntries.add(token); + tokenEntries.recordToken(image, node.getReportLocation()); for (HtmlNode child : node.children()) { traverse(child, tokenEntries); diff --git a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlTokenizerTest.java b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlTokenizerTest.java index f98bf2ed96..5e17879ddf 100644 --- a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlTokenizerTest.java +++ b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlTokenizerTest.java @@ -5,13 +5,9 @@ package net.sourceforge.pmd.lang.html; -import java.util.Properties; - import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; -import net.sourceforge.pmd.lang.html.ast.HtmlTokenizer; class HtmlTokenizerTest extends CpdTextComparisonTest { @@ -19,11 +15,6 @@ class HtmlTokenizerTest extends CpdTextComparisonTest { super(".html"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new HtmlTokenizer(); - } - @Override protected String getResourcePrefix() { return "cpd"; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java index 8a54eca671..fdec888007 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java @@ -4,10 +4,8 @@ package net.sourceforge.pmd.cpd; -import java.io.IOException; import java.util.Deque; import java.util.LinkedList; -import java.util.Properties; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; @@ -17,6 +15,7 @@ import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.java.ast.JavaTokenKinds; +import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties; public class JavaTokenizer extends JavaCCTokenizer { @@ -29,16 +28,11 @@ public class JavaTokenizer extends JavaCCTokenizer { private ConstructorDetector constructorDetector; - public void setProperties(Properties properties) { - ignoreAnnotations = Boolean.parseBoolean(properties.getProperty(IGNORE_ANNOTATIONS, "false")); - ignoreLiterals = Boolean.parseBoolean(properties.getProperty(IGNORE_LITERALS, "false")); - ignoreIdentifiers = Boolean.parseBoolean(properties.getProperty(IGNORE_IDENTIFIERS, "false")); - } - - @Override - public void tokenize(TextDocument sourceCode, Tokens tokenEntries) throws IOException { + public JavaTokenizer(JavaLanguageProperties properties) { + ignoreAnnotations = properties.getProperty(Tokenizer.CPD_IGNORE_METADATA); + ignoreLiterals = properties.getProperty(Tokenizer.CPD_ANONYMiZE_LITERALS); + ignoreIdentifiers = properties.getProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); constructorDetector = new ConstructorDetector(ignoreIdentifiers); - super.tokenize(sourceCode, tokenEntries); } @Override @@ -52,7 +46,7 @@ public class JavaTokenizer extends JavaCCTokenizer { } @Override - protected TokenEntry processToken(Tokens tokenEntries, JavaccToken javaToken) { + protected void processToken(TokenFactory tokenEntries, JavaccToken javaToken) { String image = javaToken.getImage(); constructorDetector.restoreConstructorToken(tokenEntries, javaToken); @@ -69,7 +63,7 @@ public class JavaTokenizer extends JavaCCTokenizer { constructorDetector.processToken(javaToken); - return new TokenEntry(image, javaToken.getReportLocation()); + tokenEntries.recordToken(image, javaToken.getReportLocation()); } public void setIgnoreLiterals(boolean ignore) { @@ -268,7 +262,7 @@ public class JavaTokenizer extends JavaCCTokenizer { storeNextIdentifier = true; } - public void restoreConstructorToken(Tokens tokenEntries, JavaccToken currentToken) { + public void restoreConstructorToken(TokenFactory tokenEntries, JavaccToken currentToken) { if (!ignoreIdentifiers) { return; } @@ -278,9 +272,8 @@ public class JavaTokenizer extends JavaCCTokenizer { // identifier if (!classMembersIndentations.isEmpty() && classMembersIndentations.peek().name.equals(prevIdentifier)) { - int lastTokenIndex = tokenEntries.size() - 1; - TokenEntry lastToken = tokenEntries.getTokens().get(lastTokenIndex); - lastToken.setImage(prevIdentifier); + TokenEntry lastToken = tokenEntries.peekLastToken(); + tokenEntries.setImage(lastToken, prevIdentifier); } } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokenizerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokenizerTest.java index f2840b90f3..5f95ef8785 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokenizerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokenizerTest.java @@ -19,13 +19,6 @@ class JavaTokenizerTest extends CpdTextComparisonTest { super(".java"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - JavaTokenizer javaTokenizer = new JavaTokenizer(); - javaTokenizer.setProperties(properties); - return javaTokenizer; - } - @Override protected String getResourcePrefix() { return "../lang/java/cpd/testdata"; diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerForTypescriptTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerForTypescriptTest.java index 79a48c9ea6..c70c736f07 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerForTypescriptTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerForTypescriptTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -24,11 +22,6 @@ class AnyTokenizerForTypescriptTest extends CpdTextComparisonTest { return "testdata/ts"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new AnyTokenizer(); - } - @Test void testFile1() { doTest("SampleTypeScript"); diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/EcmascriptTokenizerTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/EcmascriptTokenizerTest.java index 9554a436d9..d097841bbd 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/EcmascriptTokenizerTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/EcmascriptTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -16,11 +14,6 @@ class EcmascriptTokenizerTest extends CpdTextComparisonTest { super(".js"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new EcmascriptTokenizer(); - } - @Override protected String getResourcePrefix() { return "../lang/ecmascript/cpd/testdata"; diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/cpd/JSPTokenizerTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/cpd/JSPTokenizerTest.java index fc4a29a13c..b1a3c3879e 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/cpd/JSPTokenizerTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/cpd/JSPTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -22,11 +20,6 @@ class JSPTokenizerTest extends CpdTextComparisonTest { return "../lang/jsp/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new JSPTokenizer(); - } - @Test void scriptletWithString() { doTest("scriptletWithString"); diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java index ab960e055d..2f9241296f 100644 --- a/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -21,11 +19,6 @@ class KotlinTokenizerTest extends CpdTextComparisonTest { return "../lang/kotlin/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new KotlinTokenizer(); - } - @Test void testComments() { doTest("comment"); diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index 2a09f7874f..611caa3781 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -9,7 +9,11 @@ import net.sourceforge.pmd.cpd.SourceCode import net.sourceforge.pmd.cpd.TokenEntry import net.sourceforge.pmd.cpd.Tokenizer import net.sourceforge.pmd.cpd.Tokens +import net.sourceforge.pmd.lang.Language +import net.sourceforge.pmd.lang.LanguagePropertyBundle import net.sourceforge.pmd.lang.ast.TokenMgrError +import net.sourceforge.pmd.lang.document.TextDocument +import net.sourceforge.pmd.lang.document.TextFile import net.sourceforge.pmd.test.BaseTextComparisonTest import org.apache.commons.lang3.StringUtils import java.util.* @@ -22,10 +26,15 @@ import java.util.* * Baseline files are saved in txt files. */ abstract class CpdTextComparisonTest( - override val extensionIncludingDot: String + val language: Language, + override val extensionIncludingDot: String ) : BaseTextComparisonTest() { - abstract fun newTokenizer(properties: Properties): Tokenizer + + fun newTokenizer(config: LanguagePropertyConfig): Tokenizer { + val properties = language.newPropertyBundle().also { config.setProperties(it) } + return language.createCpdTokenizer(properties) + } override val resourceLoader: Class<*> get() = javaClass @@ -34,7 +43,11 @@ abstract class CpdTextComparisonTest( get() = "testdata" - open fun defaultProperties() = Properties() + open fun defaultProperties(): LanguagePropertyConfig = object : LanguagePropertyConfig { + override fun setProperties(properties: LanguagePropertyBundle) { + // use defaults + } + } /** * A test comparing the output of the tokenizer. @@ -42,14 +55,18 @@ abstract class CpdTextComparisonTest( * @param fileBaseName Name of the source file (without extension or resource prefix) * @param expectedSuffix Suffix to append to the expected file. This allows reusing the same source file * with different configurations, provided the suffix is different - * @param properties Properties to configure [newTokenizer] + * @param config Properties to configure the tokenizer */ @JvmOverloads - fun doTest(fileBaseName: String, expectedSuffix: String = "", properties: Properties = defaultProperties()) { + fun doTest( + fileBaseName: String, + expectedSuffix: String = "", + config: LanguagePropertyConfig = defaultProperties() + ) { super.doTest(fileBaseName, expectedSuffix) { fileData -> - val sourceCode = SourceCode(SourceCode.StringCodeLoader(fileData.fileText, fileData.fileName)) + val sourceCode = TextDocument.readOnlyString(fileBaseName, fileBaseName, language.defaultVersion) val tokens = Tokens().also { - val tokenizer = newTokenizer(properties) + val tokenizer = newTokenizer(config) tokenizer.tokenize(sourceCode, it) } @@ -61,14 +78,18 @@ abstract class CpdTextComparisonTest( fun expectTokenMgrError( source: String, fileName: String = SourceCode.StringCodeLoader.DEFAULT_NAME, - properties: Properties = defaultProperties() + properties: LanguagePropertyConfig = defaultProperties() ): TokenMgrError = expectTokenMgrError(FileData(fileName, source), properties) @JvmOverloads - fun expectTokenMgrError(fileData: FileData, properties: Properties = defaultProperties()): TokenMgrError = + fun expectTokenMgrError( + fileData: FileData, + config: LanguagePropertyConfig = defaultProperties() + ): TokenMgrError = shouldThrow { - newTokenizer(properties).tokenize(sourceCodeOf(fileData), Tokens()) + val tokenizer = newTokenizer(config) + tokenizer.tokenize(sourceCodeOf(fileData), Tokens()) } @@ -147,9 +168,11 @@ abstract class CpdTextComparisonTest( } - fun sourceCodeOf(str: String): SourceCode = SourceCode(SourceCode.StringCodeLoader(str)) - fun sourceCodeOf(fileData: FileData): SourceCode = - SourceCode(SourceCode.StringCodeLoader(fileData.fileText, fileData.fileName)) + fun sourceCodeOf(str: String): TextDocument = + sourceCodeOf(FileData(fileName = TextFile.UNKNOWN_FILENAME, fileText = str)) + + fun sourceCodeOf(fileData: FileData): TextDocument = + TextDocument.readOnlyString(fileData.fileText, fileData.fileName, language.defaultVersion) fun tokenize(tokenizer: Tokenizer, str: String): Tokens = Tokens().also { @@ -163,3 +186,7 @@ abstract class CpdTextComparisonTest( val ImageSize = Col0Width - Indent.length - 2 // -2 is for the "[]" } } + +interface LanguagePropertyConfig { + fun setProperties(properties: LanguagePropertyBundle) +} diff --git a/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java b/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java index 5062ebe242..5045837e2c 100644 --- a/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java +++ b/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -20,11 +18,6 @@ class LuaTokenizerTest extends CpdTextComparisonTest { return "../lang/lua/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new LuaTokenizer(); - } - @Test void testSimple() { doTest("helloworld"); diff --git a/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java b/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java index 4492c55ba9..73400c993e 100644 --- a/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java +++ b/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -21,11 +19,6 @@ class MatlabTokenizerTest extends CpdTextComparisonTest { return "../lang/matlab/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new MatlabTokenizer(); - } - @Test void testLongSample() { doTest("sample-matlab"); diff --git a/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java b/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java index bb199ea7ac..edb8979a1c 100644 --- a/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java +++ b/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -22,11 +20,6 @@ class ObjectiveCTokenizerTest extends CpdTextComparisonTest { return "../lang/objc/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new ObjectiveCTokenizer(); - } - @Test void testLongSample() { doTest("big_sample"); diff --git a/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java b/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java index 43830a85d1..ec2a8393fb 100644 --- a/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java +++ b/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java @@ -4,12 +4,8 @@ package net.sourceforge.pmd.lang.perl.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.PerlLanguage; -import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; /** @@ -21,11 +17,6 @@ class PerlTokenizerTest extends CpdTextComparisonTest { super(".pl"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new PerlLanguage().getTokenizer(); - } - @Test void testSample() { doTest("sample"); diff --git a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java b/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java index b63cfecd0e..56f3615697 100644 --- a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java +++ b/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java @@ -4,27 +4,9 @@ package net.sourceforge.pmd.cpd; -import java.util.List; - -import net.sourceforge.pmd.lang.document.TextDocument; - /** * Simple tokenizer for PHP. */ -public class PHPTokenizer implements Tokenizer { +public class PHPTokenizer extends AnyTokenizer { - @Override - public void tokenize(TextDocument tokens, Tokens tokenEntries) { - List code = tokens.getCode(); - for (int i = 0; i < code.size(); i++) { - String currentLine = code.get(i); - for (int j = 0; j < currentLine.length(); j++) { - char tok = currentLine.charAt(j); - if (!Character.isWhitespace(tok) && tok != '{' && tok != '}' && tok != ';') { - tokenEntries.add(new TokenEntry(String.valueOf(tok), tokens.getFileName(), i + 1)); - } - } - } - tokenEntries.add(TokenEntry.getEOF()); - } } diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLLanguage.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLLanguage.java deleted file mode 100755 index 5331ec9f72..0000000000 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLLanguage.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; - -/** - * - * @author Stuart Turton sturton@users.sourceforge.net - */ -public class PLSQLLanguage extends AbstractLanguage { - public PLSQLLanguage() { - super("PL/SQL", "plsql", new PLSQLTokenizer(), - ".sql", - ".trg", // Triggers - ".prc", ".fnc", // Standalone Procedures and Functions - ".pld", // Oracle*Forms - ".pls", ".plh", ".plb", // Packages - ".pck", ".pks", ".pkh", ".pkb", // Packages - ".typ", ".tyb", // Object Types - ".tps", ".tpb" // Object Types - ); - } - - @Override - public final void setProperties(Properties properties) { - ((PLSQLTokenizer) getTokenizer()).setProperties(properties); - } -} diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java index 4d66cf3089..91abf4f844 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java @@ -4,46 +4,28 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.plsql.ast.PLSQLTokenKinds; public class PLSQLTokenizer extends JavaCCTokenizer { - // This is actually useless, the comments are special tokens, never taken into account by CPD - @Deprecated - public static final String IGNORE_COMMENTS = "ignore_comments"; - public static final String IGNORE_IDENTIFIERS = "ignore_identifiers"; - public static final String IGNORE_LITERALS = "ignore_literals"; - private boolean ignoreIdentifiers; - private boolean ignoreLiterals; + private final boolean ignoreIdentifiers; + private final boolean ignoreLiterals; - public void setProperties(Properties properties) { + public PLSQLTokenizer(LanguagePropertyBundle properties) { /* * The Tokenizer is derived from PLDoc, in which comments are very * important When looking for duplication, we are probably not * interested in comment variation, so we shall default ignoreComments * to true */ - ignoreIdentifiers = Boolean.parseBoolean(properties.getProperty(IGNORE_IDENTIFIERS, "false")); - ignoreLiterals = Boolean.parseBoolean(properties.getProperty(IGNORE_LITERALS, "false")); - } - - @Deprecated - public void setIgnoreComments(boolean ignore) { - // This is actually useless, the comments are special tokens, never taken into account by CPD - } - - public void setIgnoreLiterals(boolean ignore) { - this.ignoreLiterals = ignore; - } - - public void setIgnoreIdentifiers(boolean ignore) { - this.ignoreIdentifiers = ignore; + ignoreIdentifiers = properties.getProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); + ignoreLiterals = properties.getProperty(Tokenizer.CPD_ANONYMiZE_LITERALS); } @Override @@ -67,6 +49,6 @@ public class PLSQLTokenizer extends JavaCCTokenizer { @Override protected TokenManager makeLexerImpl(TextDocument doc) { - return PLSQLTokenKinds.newTokenManager(doc); + return PLSQLTokenKinds.newTokenManager(CharStream.create(doc)); } } diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java index 0b74d631c2..6428d5938b 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java @@ -4,6 +4,11 @@ package net.sourceforge.pmd.lang.plsql; +import net.sourceforge.pmd.cpd.PLSQLTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; /** @@ -31,4 +36,21 @@ public class PLSQLLanguageModule extends SimpleLanguageModuleBase { new PLSQLHandler() ); } + + @Override + public LanguagePropertyBundle newPropertyBundle() { + LanguagePropertyBundle bundle = super.newPropertyBundle(); + bundle.definePropertyDescriptor(Tokenizer.CPD_ANONYMiZE_LITERALS); + bundle.definePropertyDescriptor(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); + return bundle; + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new PLSQLTokenizer(bundle); + } + + public static Language getInstance() { + return LanguageRegistry.PMD.getLanguageById("plsql"); + } } diff --git a/pmd-plsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-plsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index cb05edd026..0000000000 --- a/pmd-plsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.PLSQLLanguage diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/cpd/PLSQLTokenizerTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/cpd/PLSQLTokenizerTest.java index cfd5a3260f..280c1a0eeb 100644 --- a/pmd-plsql/src/test/java/net/sourceforge/pmd/cpd/PLSQLTokenizerTest.java +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/cpd/PLSQLTokenizerTest.java @@ -4,16 +4,15 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.plsql.PLSQLLanguageModule; class PLSQLTokenizerTest extends CpdTextComparisonTest { PLSQLTokenizerTest() { - super(".sql"); + super(PLSQLLanguageModule.getInstance(), ".sql"); } @Override @@ -21,12 +20,7 @@ class PLSQLTokenizerTest extends CpdTextComparisonTest { return "../lang/plsql/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new PLSQLTokenizer(); - } - @Test void testSimple() { doTest("sample-plsql"); diff --git a/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java b/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java index 5b22f66402..b5c7988cc1 100644 --- a/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java +++ b/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -21,12 +19,7 @@ class PythonTokenizerTest extends CpdTextComparisonTest { return "../lang/python/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new PythonTokenizer(); - } - - + @Test void sampleTest() { doTest("sample_python"); diff --git a/pmd-ruby/src/test/java/net/sourceforge/pmd/cpd/RubyTokenizerTest.java b/pmd-ruby/src/test/java/net/sourceforge/pmd/cpd/RubyTokenizerTest.java index 48704a2527..dfe8574b64 100644 --- a/pmd-ruby/src/test/java/net/sourceforge/pmd/cpd/RubyTokenizerTest.java +++ b/pmd-ruby/src/test/java/net/sourceforge/pmd/cpd/RubyTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -21,11 +19,6 @@ class RubyTokenizerTest extends CpdTextComparisonTest { return "../lang/ruby/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new RubyLanguage().getTokenizer(); - } - @Test void testSimple() { diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 5c2b98892b..0fca0e0d6a 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.io.IOException; import java.util.Properties; import org.apache.commons.lang3.StringUtils; @@ -13,7 +12,6 @@ import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.TokenMgrError; -import net.sourceforge.pmd.lang.document.CpdCompat; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -62,20 +60,20 @@ public class ScalaTokenizer implements Tokenizer { } @Override - public void tokenize(TextDocument sourceCode, Tokens tokenEntries) throws IOException { + public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { - try (TextDocument textDoc = TextDocument.create(CpdCompat.cpdCompat(sourceCode))) { - String fullCode = textDoc.getText().toString(); + try { + String fullCode = sourceCode.getText().toString(); // create the input file for scala - Input.VirtualFile vf = new Input.VirtualFile(sourceCode.getFileName(), fullCode); + Input.VirtualFile vf = new Input.VirtualFile(sourceCode.getDisplayName(), fullCode); ScalametaTokenizer tokenizer = new ScalametaTokenizer(vf, dialect); // tokenize with a filter scala.meta.tokens.Tokens tokens = tokenizer.tokenize(); // use extensions to the standard PMD TokenManager and Filter - ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator(), textDoc); + ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator(), sourceCode); ScalaTokenFilter filter = new ScalaTokenFilter(scalaTokenManager); ScalaTokenAdapter token; @@ -83,21 +81,19 @@ public class ScalaTokenizer implements Tokenizer { if (StringUtils.isEmpty(token.getImage())) { continue; } - TokenEntry cpdToken = new TokenEntry(token.getImage(), - token.getReportLocation()); - tokenEntries.add(cpdToken); + tokenEntries.recordToken(token.getImage(), + token.getReportLocation()); } } catch (Exception e) { if (e instanceof TokenizeException) { // NOPMD // cannot catch it as it's a checked exception and Scala sneaky throws TokenizeException tokE = (TokenizeException) e; Position pos = tokE.pos(); - throw new TokenMgrError(pos.startLine() + 1, pos.startColumn() + 1, sourceCode.getFileName(), "Scalameta threw", tokE); + throw new TokenMgrError( + pos.startLine() + 1, pos.startColumn() + 1, sourceCode.getDisplayName(), "Scalameta threw", tokE); } else { throw e; } - } finally { - tokenEntries.add(TokenEntry.getEOF()); } } diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java index 49cc0f76c4..8bda7ffea0 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.cpd; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -24,11 +22,6 @@ class ScalaTokenizerTest extends CpdTextComparisonTest { return "../lang/scala/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new ScalaTokenizer(); - } - @Test void testSample() { doTest("sample-LiftActor"); diff --git a/pmd-swift/src/test/java/net/sourceforge/pmd/cpd/SwiftTokenizerTest.java b/pmd-swift/src/test/java/net/sourceforge/pmd/cpd/SwiftTokenizerTest.java index 4969321f77..b63688ed7b 100644 --- a/pmd-swift/src/test/java/net/sourceforge/pmd/cpd/SwiftTokenizerTest.java +++ b/pmd-swift/src/test/java/net/sourceforge/pmd/cpd/SwiftTokenizerTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; @@ -21,11 +19,6 @@ class SwiftTokenizerTest extends CpdTextComparisonTest { return "../lang/swift/cpd/testdata"; } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new SwiftTokenizer(); - } - @Test void testSwift42() { diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java index 487f4ed9ec..07842f43c6 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java @@ -5,12 +5,8 @@ package net.sourceforge.pmd.lang.vf.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.VfTokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class VfTokenizerTest extends CpdTextComparisonTest { @@ -19,12 +15,6 @@ class VfTokenizerTest extends CpdTextComparisonTest { super(".page"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - VfTokenizer tokenizer = new VfTokenizer(); - return tokenizer; - } - @Test void testTokenize() { doTest("SampleUnescapeElWithTab"); diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/xml/cpd/XmlCPDTokenizerTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/xml/cpd/XmlCPDTokenizerTest.java index 194a219b2f..9fc4f39400 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/xml/cpd/XmlCPDTokenizerTest.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/xml/cpd/XmlCPDTokenizerTest.java @@ -4,11 +4,8 @@ package net.sourceforge.pmd.xml.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class XmlCPDTokenizerTest extends CpdTextComparisonTest { @@ -17,11 +14,6 @@ class XmlCPDTokenizerTest extends CpdTextComparisonTest { super(".xml"); } - @Override - public Tokenizer newTokenizer(Properties properties) { - return new XmlTokenizer(); - } - @Test void tokenizeTest() { doTest("simple"); From 27a4aba8715afd4323074d8423dff26f84010ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 16:34:24 +0100 Subject: [PATCH 094/347] Progress --- .../net/sourceforge/pmd/cpd/ApexCpdTest.java | 33 +++--- .../pmd/AbstractConfiguration.java | 33 ++++++ .../net/sourceforge/pmd/PMDConfiguration.java | 20 +--- .../java/net/sourceforge/pmd/cpd/CPD.java | 7 -- .../sourceforge/pmd/cpd/CPDConfiguration.java | 20 ++-- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 102 +++++++----------- .../java/net/sourceforge/pmd/cpd/GUI.java | 4 +- .../java/net/sourceforge/pmd/cpd/Mark.java | 1 - .../java/net/sourceforge/pmd/cpd/Match.java | 41 ++----- .../sourceforge/pmd/cpd/SourceManager.java | 15 ++- .../net/sourceforge/pmd/cpd/TokenEntry.java | 24 ++--- .../net/sourceforge/pmd/cpd/TokenFactory.java | 4 +- .../java/net/sourceforge/pmd/cpd/Tokens.java | 17 +-- .../net/sourceforge/pmd/cpd/XMLRenderer.java | 5 +- .../sourceforge/pmd/cpd/CSVRendererTest.java | 4 +- .../net/sourceforge/pmd/cpd/MarkTest.java | 6 +- .../net/sourceforge/pmd/cpd/MatchTest.java | 9 +- .../sourceforge/pmd/cpd/TestTokenFactory.java | 11 ++ .../sourceforge/pmd/cpd/TokenEntryTest.java | 19 +--- .../sourceforge/pmd/cpd/XMLRendererTest.java | 1 - .../net/sourceforge/pmd/cpd/JavaLanguage.java | 24 ----- .../sourceforge/pmd/cpd/JavaTokenizer.java | 24 ++--- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../pmd/cpd/MatchAlgorithmTest.java | 33 +++--- 24 files changed, 198 insertions(+), 260 deletions(-) create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaLanguage.java delete mode 100644 pmd-java/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java index c966e5a330..9cc3cd00ad 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java @@ -7,9 +7,8 @@ package net.sourceforge.pmd.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.File; -import java.io.IOException; -import java.util.Iterator; +import java.nio.file.Path; +import java.nio.file.Paths; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -18,33 +17,29 @@ import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; class ApexCpdTest { - private File testdir; + + private Path testdir; @BeforeEach void setUp() { String path = IOUtil.normalizePath("src/test/resources/net/sourceforge/pmd/cpd/issue427"); - testdir = new File(path); + testdir = Paths.get(path); } @Test - void testIssue427() throws IOException { + void testIssue427() throws Exception { CPDConfiguration configuration = new CPDConfiguration(); configuration.setMinimumTileSize(10); configuration.setLanguage(LanguageFactory.createLanguage(ApexLanguageModule.TERSE_NAME)); - CPD cpd = new CPD(configuration); - cpd.add(new File(testdir, "SFDCEncoder.cls")); - cpd.add(new File(testdir, "SFDCEncoderConstants.cls")); + try (CpdAnalysis cpd = new CpdAnalysis(configuration)) { + cpd.files().addFile(testdir.resolve("SFDCEncoder.cls")); + cpd.files().addFile(testdir.resolve("SFDCEncoderConstants.cls")); - cpd.go(); - - Iterator matches = cpd.getMatches(); - int duplications = 0; - while (matches.hasNext()) { - matches.next(); - duplications++; + cpd.performAnalysis(matches -> { + assertEquals(1, matches.getMatches().size()); + Match firstDuplication = matches.getMatches().get(0); + assertTrue(firstDuplication.getSourceCodeSlice().startsWith("global with sharing class SFDCEncoder")); + }); } - assertEquals(1, duplications); - Match firstDuplication = cpd.getMatches().next(); - assertTrue(firstDuplication.getSourceCodeSlice().startsWith("global with sharing class SFDCEncoder")); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java index 671cf74da1..9620e109cb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java @@ -5,6 +5,14 @@ package net.sourceforge.pmd; import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Objects; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.LanguageRegistry; /** * Base configuration class for both PMD and CPD. @@ -15,6 +23,8 @@ public abstract class AbstractConfiguration { private Charset sourceEncoding = Charset.forName(System.getProperty("file.encoding")); private boolean debug; + private final Map langProperties = new HashMap<>(); + private final LanguageRegistry langRegistry; /** * Create a new abstract configuration. @@ -23,6 +33,10 @@ public abstract class AbstractConfiguration { super(); } + protected AbstractConfiguration(LanguageRegistry languageRegistry) { + this.langRegistry = Objects.requireNonNull(languageRegistry); + } + /** * Get the character encoding of source files. * @@ -63,4 +77,23 @@ public abstract class AbstractConfiguration { public void setDebug(boolean debug) { this.debug = debug; } + + + /** + * Returns a mutable bundle of language properties that are associated + * to the given language (always the same for a given language). + * + * @param language A language, which must be registered + */ + public @NonNull LanguagePropertyBundle getLanguageProperties(Language language) { + checkLanguageIsRegistered(language); + return langProperties.computeIfAbsent(language, Language::newPropertyBundle); + } + + void checkLanguageIsRegistered(Language language) { + if (!langRegistry.getLanguages().contains(language)) { + throw new IllegalArgumentException( + "Language '" + language.getId() + "' is not registered in " + getLanguageRegistry()); + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index c471ca76ae..58130a1ea4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -137,7 +137,6 @@ public class PMDConfiguration extends AbstractConfiguration { private boolean benchmark; private AnalysisCache analysisCache = new NoopAnalysisCache(); private boolean ignoreIncrementalAnalysis; - private final LanguageRegistry langRegistry; private final List relativizeRoots = new ArrayList<>(); private final Map langProperties = new HashMap<>(); @@ -146,7 +145,7 @@ public class PMDConfiguration extends AbstractConfiguration { } public PMDConfiguration(@NonNull LanguageRegistry languageRegistry) { - this.langRegistry = Objects.requireNonNull(languageRegistry); + super(languageRegistry); this.languageVersionDiscoverer = new LanguageVersionDiscoverer(languageRegistry); } @@ -952,21 +951,4 @@ public class PMDConfiguration extends AbstractConfiguration { return Collections.unmodifiableList(relativizeRoots); } - /** - * Returns a mutable bundle of language properties that are associated - * to the given language (always the same for a given language). - * - * @param language A language, which must be registered - */ - public @NonNull LanguagePropertyBundle getLanguageProperties(Language language) { - checkLanguageIsRegistered(language); - return langProperties.computeIfAbsent(language, Language::newPropertyBundle); - } - - void checkLanguageIsRegistered(Language language) { - if (!langRegistry.getLanguages().contains(language)) { - throw new IllegalArgumentException( - "Language '" + language.getId() + "' is not registered in " + getLanguageRegistry()); - } - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index 3c8a6e0265..47e0ee7791 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -17,19 +17,15 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.internal.util.FileFinder; import net.sourceforge.pmd.internal.util.FileUtil; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.ast.TokenMgrError; -import net.sourceforge.pmd.lang.document.SourceCode; import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -54,9 +50,6 @@ public class CPD { public CPD(CPDConfiguration theConfiguration) { configuration = theConfiguration; - // before we start any tokenizing (add(File...)), we need to reset the - // static TokenEntry status - TokenEntry.clearImages(); // Add all sources extractAllSources(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 2a4950d2f8..1c1a3a1a7a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -8,7 +8,6 @@ import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.io.File; import java.io.FilenameFilter; -import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; @@ -25,8 +24,7 @@ import net.sourceforge.pmd.AbstractConfiguration; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.internal.util.FileFinder; import net.sourceforge.pmd.internal.util.FileUtil; -import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.lang.LanguageRegistry; /** * @@ -40,6 +38,7 @@ public class CPDConfiguration extends AbstractConfiguration { private static final Map> RENDERERS = new HashMap<>(); + static { RENDERERS.put(DEFAULT_RENDERER, SimpleRenderer.class); RENDERERS.put("xml", XMLRenderer.class); @@ -48,7 +47,8 @@ public class CPDConfiguration extends AbstractConfiguration { RENDERERS.put("vs", VSRenderer.class); } - private Language language; + + private Language language = CPDConfiguration.getLanguageFromString(DEFAULT_LANGUAGE); private int minimumTileSize; @@ -90,18 +90,15 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean debug = false; - public TextFile sourceCodeFor(File file) { - return new SourceCode(new SourceCode.FileCodeLoader(file, getSourceEncoding().name())); + public CPDConfiguration() { + super(LanguageRegistry.CPD); } - public SourceCode sourceCodeFor(Reader reader, String sourceCodeName) { - return new SourceCode(new SourceCode.ReaderCodeLoader(reader, sourceCodeName)); + public CPDConfiguration(LanguageRegistry languageRegistry) { + super(languageRegistry); } public void postContruct() { - if (getLanguage() == null) { - setLanguage(CPDConfiguration.getLanguageFromString(DEFAULT_LANGUAGE)); - } if (getRendererName() == null) { setRendererName(DEFAULT_RENDERER); } @@ -166,6 +163,7 @@ public class CPDConfiguration extends AbstractConfiguration { return LanguageFactory.createLanguage(languageString); } + public static void setSystemProperties(CPDConfiguration configuration) { Properties properties = new Properties(); if (configuration.isIgnoreLiterals()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index e0cd1aa5b2..bc4e5541a7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -5,56 +5,39 @@ package net.sourceforge.pmd.cpd; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.internal.util.FileCollectionUtil; -import net.sourceforge.pmd.internal.util.FileFinder; import net.sourceforge.pmd.internal.util.FileUtil; -import net.sourceforge.pmd.internal.util.IOUtil; +import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.util.database.DBMSMetadata; -import net.sourceforge.pmd.util.database.DBURI; -import net.sourceforge.pmd.util.database.SourceObject; +import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.util.log.MessageReporter; -/** - * @deprecated Use the module pmd-cli for CLI support. - */ -@Deprecated -public class CpdAnalysis { +public final class CpdAnalysis implements AutoCloseable { + private static Logger log = LoggerFactory.getLogger(CpdAnalysis.class); private CPDConfiguration configuration; private FileCollector files; private MessageReporter reporter; private CPDListener listener; - public CpdAnalysis(CPDConfiguration theConfiguration) { + public CpdAnalysis(CPDConfiguration theConfiguration) throws IOException { configuration = theConfiguration; // Add all sources - try { - extractAllSources(); - } catch (IOException e) { - throw new RuntimeException(e); - } + extractAllSources(); } public FileCollector files() { @@ -87,21 +70,41 @@ public class CpdAnalysis { this.listener = cpdListener; } - private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException { - log.debug("Tokenizing {}", sourceCode.getPathId()); - try (TextDocument doc = sourceCode.load()) { - configuration.tokenizer().tokenize(doc, tokens); + private int doTokenize(TextDocument document, Tokenizer tokenizer, Tokens tokens) throws IOException { + log.trace("Tokenizing {}", document.getPathId()); + int lastTokenSize = tokens.size(); + try { + tokenizer.tokenize(document, TokenFactory.forFile(document, tokens)); + } catch (IOException ioe) { + reporter.errorEx("Error while lexing.", ioe); + } catch (TokenMgrError e) { + e.setFileName(document.getDisplayName()); + reporter.errorEx("Error while lexing.", e); + } finally { + tokens.addEof(); } - listener.addedFile(1); - source.put(sourceCode.getPathId(), sourceCode); - numberOfTokensPerFile.put(sourceCode.getPathId(), tokens.size() - lastTokenSize - 1 /*EOF*/); - lastTokenSize = tokens.size(); + return tokens.size() - lastTokenSize - 1; /* EOF */ } - public CPDReport performAnalysis() { + public void performAnalysis(Consumer consumer) { try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) { + Map tokenizers = + sourceManager.getTextFiles().stream() + .map(it -> it.getLanguageVersion().getLanguage()) + .collect(Collectors.toMap(lang -> lang, lang -> lang.createCpdTokenizer(configuration.getLanguageProperties(lang)))); + + Map numberOfTokensPerFile = new HashMap<>(); + Tokens tokens = new Tokens(); + for (TextFile textFile : sourceManager.getTextFiles()) { + + TextDocument textDocument = sourceManager.get(textFile); + + int newTokens = doTokenize(textDocument, tokenizers.get(textFile.getLanguageVersion().getLanguage()), tokens); + numberOfTokensPerFile.put(textDocument.getPathId(), newTokens); + listener.addedFile(1); + } log.debug("Running match algorithm on {} files...", sourceManager.size()); @@ -109,39 +112,16 @@ public class CpdAnalysis { matchAlgorithm.findMatches(); log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); - + new CPDReport(matchAlgorithm.getMatches(), matchAlgorithm.to) } catch (Exception e) { reporter.errorEx("Exception while running CPD", e); } } - public void add(File file) throws IOException { - if (configuration.isSkipDuplicates()) { - // TODO refactor this thing into a separate class - String signature = file.getName() + '_' + file.length(); - if (current.contains(signature)) { - System.err.println("Skipping " + file.getAbsolutePath() - + " since it appears to be a duplicate file and --skip-duplicate-files is set"); - return; - } - current.add(signature); - } - - if (!IOUtil.equalsNormalizedPaths(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) { - System.err.println("Skipping " + file + " since it appears to be a symlink"); - return; - } - - if (!file.exists()) { - System.err.println("Skipping " + file + " since it doesn't exist (broken symlink?)"); - return; - } - - SourceCode sourceCode = configuration.sourceCodeFor(file); - add(sourceCode); + @Override + public void close() throws IOException { + // nothing for now } - - } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 13fd308305..ca694ad209 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -224,8 +224,8 @@ public class GUI implements CPDListener { new ColumnSpec("Lines", SwingConstants.RIGHT, 45, Match.LINES_COMPARATOR), }; static { - for (int i = 0; i < LANGUAGE_SETS.length; i++) { - LANGUAGE_CONFIGS_BY_LABEL.put((String) LANGUAGE_SETS[i][0], (LanguageConfig) LANGUAGE_SETS[i][1]); + for (Object[] languageSet : LANGUAGE_SETS) { + LANGUAGE_CONFIGS_BY_LABEL.put((String) languageSet[0], (LanguageConfig) languageSet[1]); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index 4cfe7dfd73..faf289453a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -11,7 +11,6 @@ public class Mark implements Comparable { private TokenEntry token; private TokenEntry endToken; private int lineCount; - private TextDocument code; public Mark(TokenEntry token) { this.token = token; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java index 3dbb8e2a9e..6c625ef313 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java @@ -18,39 +18,20 @@ public class Match implements Comparable, Iterable { private Set markSet = new TreeSet<>(); private String label; - public static final Comparator MATCHES_COMPARATOR = new Comparator() { - @Override - public int compare(Match ma, Match mb) { - return mb.getMarkCount() - ma.getMarkCount(); + public static final Comparator MATCHES_COMPARATOR = (ma, mb) -> mb.getMarkCount() - ma.getMarkCount(); + + public static final Comparator LINES_COMPARATOR = (ma, mb) -> mb.getLineCount() - ma.getLineCount(); + + public static final Comparator LABEL_COMPARATOR = (ma, mb) -> { + if (ma.getLabel() == null) { + return 1; } + if (mb.getLabel() == null) { + return -1; + } + return mb.getLabel().compareTo(ma.getLabel()); }; - public static final Comparator LINES_COMPARATOR = new Comparator() { - @Override - public int compare(Match ma, Match mb) { - return mb.getLineCount() - ma.getLineCount(); - } - }; - - public static final Comparator LABEL_COMPARATOR = new Comparator() { - @Override - public int compare(Match ma, Match mb) { - if (ma.getLabel() == null) { - return 1; - } - if (mb.getLabel() == null) { - return -1; - } - return mb.getLabel().compareTo(ma.getLabel()); - } - }; - - public static final Comparator LENGTH_COMPARATOR = new Comparator() { - @Override - public int compare(Match ma, Match mb) { - return mb.getLineCount() - ma.getLineCount(); - } - }; public Match(int tokenCount, Mark first, Mark second) { markSet.add(first); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index bbcc75e051..be2e1bf266 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.cpd; +import java.io.IOException; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; @@ -24,8 +25,20 @@ public class SourceManager implements AutoCloseable { } - TextDocument get(String pathId) { + List getTextFiles() { + return textFiles; + } + TextDocument get(TextFile file) { + return files.computeIfAbsent(file, f -> { + TextDocument doc; + try { + doc = TextDocument.create(f); + return new SoftReference<>(doc); + } catch (IOException e) { + throw new RuntimeException(e); + } + }).get(); } public int size() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java index 96c89cebda..cc6262b1e9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java @@ -46,7 +46,7 @@ public class TokenEntry implements Comparable { this(image, tokenSrcID, beginLine, -1, -1); } - public TokenEntry(String image, String tokenSrcID, int beginLine, int beginColumn, int endColumn) { + public TokenEntry(String image, String tokenSrcID, int beginLine, int beginColumn, int endLine, int endColumn) { assert isOk(beginLine) && isOk(beginColumn) && isOk(endColumn) : "Coordinates are 1-based"; setImage(image); this.tokenSrcID = tokenSrcID; @@ -63,11 +63,6 @@ public class TokenEntry implements Comparable { return coord >= 1 || coord == -1; } - public static TokenEntry getEOF() { - TOKEN_COUNT.get().getAndIncrement(); - return EOF; - } - public static void clearImages() { TOKENS.get().clear(); TOKENS.remove(); @@ -104,7 +99,7 @@ public class TokenEntry implements Comparable { } } - public String getTokenSrcID() { + String getTokenSrcID() { return tokenSrcID; } @@ -130,11 +125,11 @@ public class TokenEntry implements Comparable { return endColumn; // TODO Java 1.8 make optional } - public int getIdentifier() { + int getIdentifier() { return this.identifier; } - public int getIndex() { + int getIndex() { return this.index; } @@ -143,7 +138,7 @@ public class TokenEntry implements Comparable { return hashCode; } - public void setHashCode(int hashCode) { + void setHashCode(int hashCode) { this.hashCode = hashCode; } @@ -183,12 +178,7 @@ public class TokenEntry implements Comparable { return "--unknown--"; } - final void setImage(String image) { - Integer i = TOKENS.get().get(image); - if (i == null) { - i = TOKENS.get().size() + 1; - TOKENS.get().put(image, i); - } - this.identifier = i.intValue(); + final void setImageIdentifier(int identifier) { + this.identifier = identifier; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java index d72821f4ab..796b40258c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.lang.document.FileLocation; -import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.lang.document.TextDocument; public interface TokenFactory { @@ -19,7 +19,7 @@ public interface TokenFactory { TokenEntry peekLastToken(); - static TokenFactory forFile(TextFile file, Tokens sink) { + static TokenFactory forFile(TextDocument file, Tokens sink) { return new TokenFactory() { final String name = file.getPathId(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 1be2d32a5c..3bda7c57d6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -11,8 +11,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import net.sourceforge.pmd.lang.document.FileLocation; - public class Tokens { private final List tokens = new ArrayList<>(); @@ -30,16 +28,21 @@ public class Tokens { } }; - public void add(TokenEntry tokenEntry) { + private void add(TokenEntry tokenEntry) { this.tokens.add(tokenEntry); } - public void addToken(String image, FileLocation location) { - this.tokens.add(new TokenEntry(image, location)); + void addEof() { + add(TokenEntry.EOF); } - public void setImage(TokenEntry entry, String newImage) { - entry.setImage(newImage); + void setImage(TokenEntry entry, String newImage) { + Integer i = TOKENS.get().get(newImage); + if (i == null) { + i = TOKENS.get().size() + 1; + TOKENS.get().put(newImage, i); + } + entry.setImageIdentifier(i); } public TokenEntry peekLastToken() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java index 196b7f4bf9..2577a9d91e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java @@ -23,6 +23,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.util.StringUtil; /** @@ -141,10 +142,10 @@ public final class XMLRenderer implements CPDReportRenderer { } private Element addCodeSnippet(Document doc, Element duplication, Match match) { - String codeSnippet = match.getSourceCodeSlice(); + Chars codeSnippet = match.getSourceCodeSlice(); if (codeSnippet != null) { // the code snippet has normalized line endings - String platformSpecific = codeSnippet.replace("\n", System.lineSeparator()); + String platformSpecific = codeSnippet.toString().replace("\n", System.lineSeparator()); Element codefragment = doc.createElement("codefragment"); // only remove invalid characters, escaping is not necessary in CDATA. // if the string contains the end marker of a CDATA section, then the DOM impl will diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java index d06d1e7c47..b158ab83af 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java @@ -57,8 +57,8 @@ class CSVRendererTest { private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) { Tokens tokens = new Tokens(); - tokens.addToken(image, ); - Mark result = new Mark(new TokenEntry(image, tokenSrcID, beginLine)); + tokens.addToken(image, tokenSrcID, beginLine, beginLine, beginLine, beginLine); + Mark result = new Mark(tokens.peekLastToken()); result.setLineCount(lineCount); result.setSourceCode(new SourceCode(new SourceCode.StringCodeLoader(code))); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java index 55cb82b7ba..20b98ae04f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java @@ -16,7 +16,7 @@ class MarkTest { void testSimple() { String filename = "/var/Foo.java"; int beginLine = 1; - TokenEntry token = new TokenEntry("public", "/var/Foo.java", 1); + TokenEntry token = new TokenEntry("public", "/var/Foo.java", 1, 2, 3, 4); Mark mark = new Mark(token); int lineCount = 10; @@ -40,8 +40,8 @@ class MarkTest { final int beginLine = 1; final int beginColumn = 2; final int endColumn = 3; - final TokenEntry token = new TokenEntry("public", "/var/Foo.java", 1, beginColumn, beginColumn + "public".length()); - final TokenEntry endToken = new TokenEntry("}", "/var/Foo.java", 5, endColumn - 1, endColumn); + final TokenEntry token = new TokenEntry("public", "/var/Foo.java", 1, beginColumn, 1, beginColumn + "public".length()); + final TokenEntry endToken = new TokenEntry("}", "/var/Foo.java", 5, 1, endColumn - 1, endColumn); final Mark mark = new Mark(token); final int lineCount = 10; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java index bd77cbd2e3..633d725691 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java @@ -47,14 +47,15 @@ class MatchTest { @Test void testCompareTo() { - Match m1 = new Match(1, new TokenEntry("public", "/var/Foo.java", 1), - new TokenEntry("class", "/var/Foo.java", 1)); - Match m2 = new Match(2, new TokenEntry("Foo", "/var/Foo.java", 1), new TokenEntry("{", "/var/Foo.java", 1)); + Match m1 = new Match(1, new TokenEntry("public", "/var/Foo.java", 1, 2, 3, 4), + new TokenEntry("class", "/var/Foo.java", 1, 2, 3, 4)); + Match m2 = new Match(2, new TokenEntry("Foo", "/var/Foo.java", 1, 2, 3, 4), + new TokenEntry("{", "/var/Foo.java", 1, 2, 3, 4)); assertTrue(m2.compareTo(m1) < 0); } private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) { - Mark result = new Mark(new TokenEntry(image, tokenSrcID, beginLine)); + Mark result = new Mark(new TokenEntry(image, tokenSrcID, beginLine, 1, beginLine, 1 + image.length())); result.setLineCount(lineCount); result.setSourceCode(new SourceCode(new SourceCode.StringCodeLoader(code))); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java new file mode 100644 index 0000000000..aa379e3a23 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java @@ -0,0 +1,11 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +/** + * @author Clรฉment Fournier + */ +public class TestTokenFactory { +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java index eb50dc973a..b393b0957f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java @@ -12,23 +12,14 @@ class TokenEntryTest { @Test void testSimple() { - TokenEntry.clearImages(); - TokenEntry mark = new TokenEntry("public", "/var/Foo.java", 1); - assertEquals(1, mark.getBeginLine()); - assertEquals("/var/Foo.java", mark.getTokenSrcID()); - assertEquals(0, mark.getIndex()); - assertEquals(-1, mark.getBeginColumn()); - assertEquals(-1, mark.getEndColumn()); - } - - @Test - void testColumns() { - TokenEntry.clearImages(); - TokenEntry mark = new TokenEntry("public", "/var/Foo.java", 1, 2, 3); + Tokens tokens = new Tokens(); + tokens.addToken("public", "/var/Foo.java", 1, 2, 3, 4); + TokenEntry mark = tokens.peekLastToken(); assertEquals(1, mark.getBeginLine()); assertEquals("/var/Foo.java", mark.getTokenSrcID()); assertEquals(0, mark.getIndex()); assertEquals(2, mark.getBeginColumn()); - assertEquals(3, mark.getEndColumn()); + assertEquals(4, mark.getEndColumn()); } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index 41a1d74c2f..6ef6b4b6d4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -217,7 +217,6 @@ class XMLRendererTest { @Test void testGetDuplicationStartEnd() throws IOException, ParserConfigurationException, SAXException { - TokenEntry.clearImages(); final CPDReportRenderer renderer = new XMLRenderer(); final List matches = new ArrayList<>(); final String filename = "/var/Foo.java"; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaLanguage.java b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaLanguage.java deleted file mode 100644 index 0ff3638c99..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaLanguage.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; - -public class JavaLanguage extends AbstractLanguage { - public JavaLanguage() { - this(System.getProperties()); - } - - public JavaLanguage(Properties properties) { - super("Java", "java", new JavaTokenizer(), ".java"); - setProperties(properties); - } - - @Override - public final void setProperties(Properties properties) { - JavaTokenizer tokenizer = (JavaTokenizer) getTokenizer(); - tokenizer.setProperties(properties); - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java index fdec888007..1fb0182046 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java @@ -22,11 +22,11 @@ public class JavaTokenizer extends JavaCCTokenizer { public static final String CPD_START = "\"CPD-START\""; public static final String CPD_END = "\"CPD-END\""; - private boolean ignoreAnnotations; - private boolean ignoreLiterals; - private boolean ignoreIdentifiers; + private final boolean ignoreAnnotations; + private final boolean ignoreLiterals; + private final boolean ignoreIdentifiers; - private ConstructorDetector constructorDetector; + private final ConstructorDetector constructorDetector; public JavaTokenizer(JavaLanguageProperties properties) { ignoreAnnotations = properties.getProperty(Tokenizer.CPD_IGNORE_METADATA); @@ -66,18 +66,6 @@ public class JavaTokenizer extends JavaCCTokenizer { tokenEntries.recordToken(image, javaToken.getReportLocation()); } - public void setIgnoreLiterals(boolean ignore) { - this.ignoreLiterals = ignore; - } - - public void setIgnoreIdentifiers(boolean ignore) { - this.ignoreIdentifiers = ignore; - } - - public void setIgnoreAnnotations(boolean ignoreAnnotations) { - this.ignoreAnnotations = ignoreAnnotations; - } - /** * The {@link JavaTokenFilter} extends the {@link JavaCCTokenFilter} to discard * Java-specific tokens. @@ -188,9 +176,9 @@ public class JavaTokenizer extends JavaCCTokenizer { * ignoreIdentifiers. */ private static class ConstructorDetector { - private boolean ignoreIdentifiers; + private final boolean ignoreIdentifiers; - private Deque classMembersIndentations; + private final Deque classMembersIndentations; private int currentNestingLevel; private boolean storeNextIdentifier; private String prevIdentifier; diff --git a/pmd-java/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-java/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index d5a4b32578..0000000000 --- a/pmd-java/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.JavaLanguage diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index 7920f70b7b..3cf9f6ade0 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -4,16 +4,21 @@ package net.sourceforge.pmd.cpd; +import static net.sourceforge.pmd.util.CollectionUtil.mapOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import java.io.IOException; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.java.JavaLanguageModule; + class MatchAlgorithmTest { private static final String LINE_1 = "public class Foo { "; @@ -32,15 +37,14 @@ class MatchAlgorithmTest { @Test void testSimple() throws IOException { - JavaTokenizer tokenizer = new JavaTokenizer(); - SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader(getSampleCode(), "Foo.java")); + Language java = JavaLanguageModule.getInstance(); + Tokenizer tokenizer = java.createCpdTokenizer(java.newPropertyBundle()); + TextDocument sourceCode = TextDocument.readOnlyString(getSampleCode(), "Foo.java", java.getDefaultVersion()); Tokens tokens = new Tokens(); - TokenEntry.clearImages(); - tokenizer.tokenize(sourceCode, tokens); + tokenizer.tokenize(sourceCode, TokenFactory.forFile(sourceCode, tokens)); assertEquals(41, tokens.size()); - Map codeMap = new HashMap<>(); - codeMap.put("Foo.java", sourceCode); + Map codeMap = mapOf(sourceCode.getPathId(), sourceCode); MatchAlgorithm matchAlgorithm = new MatchAlgorithm(codeMap, tokens, 5); matchAlgorithm.findMatches(); Iterator matches = matchAlgorithm.matches(); @@ -63,16 +67,17 @@ class MatchAlgorithmTest { @Test void testIgnore() throws IOException { - JavaTokenizer tokenizer = new JavaTokenizer(); - tokenizer.setIgnoreLiterals(true); - tokenizer.setIgnoreIdentifiers(true); - SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader(getSampleCode(), "Foo.java")); + Language java = JavaLanguageModule.getInstance(); + LanguagePropertyBundle bundle = java.newPropertyBundle(); + bundle.setProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS, true); + bundle.setProperty(Tokenizer.CPD_ANONYMiZE_LITERALS, true); + Tokenizer tokenizer = java.createCpdTokenizer(bundle); + TextDocument sourceCode = TextDocument.readOnlyString(getSampleCode(), "Foo.java", java.getDefaultVersion()); Tokens tokens = new Tokens(); TokenEntry.clearImages(); - tokenizer.tokenize(sourceCode, tokens); - Map codeMap = new HashMap<>(); - codeMap.put("Foo.java", sourceCode); + tokenizer.tokenize(sourceCode, TokenFactory.forFile(sourceCode, tokens)); + Map codeMap = mapOf(sourceCode.getPathId(), sourceCode); MatchAlgorithm matchAlgorithm = new MatchAlgorithm(codeMap, tokens, 5); matchAlgorithm.findMatches(); Iterator matches = matchAlgorithm.matches(); From 65d953bfb4239589c46721968d6fb2f8a172b454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 18:10:35 +0100 Subject: [PATCH 095/347] Progress --- .../java/net/sourceforge/pmd/ant/CPDTask.java | 42 +++--- .../sourceforge/pmd/ant}/ReportException.java | 6 +- .../net/sourceforge/pmd/cpd/ApexCpdTest.java | 4 +- .../pmd/cli/commands/internal/CpdCommand.java | 25 ++-- .../internal/CpdLanguageTypeSupport.java | 24 +-- .../internal/LanguageTypeSupport.java | 42 ++++++ .../internal/PmdLanguageTypeSupport.java | 19 +-- .../pmd/AbstractConfiguration.java | 139 +++++++++++++++++- .../net/sourceforge/pmd/PMDConfiguration.java | 131 +---------------- .../java/net/sourceforge/pmd/cpd/CPD.java | 6 +- .../sourceforge/pmd/cpd/CPDConfiguration.java | 105 +------------ .../net/sourceforge/pmd/cpd/CPDReport.java | 15 +- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 72 ++++++--- .../java/net/sourceforge/pmd/cpd/Mark.java | 33 +---- .../java/net/sourceforge/pmd/cpd/Match.java | 5 - .../sourceforge/pmd/cpd/MatchAlgorithm.java | 21 +-- .../sourceforge/pmd/cpd/SimpleRenderer.java | 10 +- .../sourceforge/pmd/cpd/SourceManager.java | 15 +- .../net/sourceforge/pmd/cpd/TokenEntry.java | 87 ++--------- .../java/net/sourceforge/pmd/cpd/Tokens.java | 61 +++++--- .../net/sourceforge/pmd/cpd/XMLRenderer.java | 6 +- .../treeexport/TreeExportConfiguration.java | 14 +- .../sourceforge/pmd/cpd/CSVRendererTest.java | 7 +- .../net/sourceforge/pmd/cpd/MarkTest.java | 33 ++--- .../net/sourceforge/pmd/cpd/MatchTest.java | 50 ++++--- .../sourceforge/pmd/cpd/XMLRendererTest.java | 41 +++--- .../pmd/cpd/MatchAlgorithmTest.java | 25 ++-- 27 files changed, 467 insertions(+), 571 deletions(-) rename {pmd-core/src/main/java/net/sourceforge/pmd/cpd => pmd-ant/src/main/java/net/sourceforge/pmd/ant}/ReportException.java (74%) create mode 100644 pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/LanguageTypeSupport.java diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index 955254fc04..fdb8253d1b 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -11,6 +11,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -23,13 +24,12 @@ import org.apache.tools.ant.Task; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.types.FileSet; -import net.sourceforge.pmd.cpd.CPD; import net.sourceforge.pmd.cpd.CPDConfiguration; import net.sourceforge.pmd.cpd.CPDReport; import net.sourceforge.pmd.cpd.CSVRenderer; +import net.sourceforge.pmd.cpd.CpdAnalysis; import net.sourceforge.pmd.cpd.Language; import net.sourceforge.pmd.cpd.LanguageFactory; -import net.sourceforge.pmd.cpd.ReportException; import net.sourceforge.pmd.cpd.SimpleRenderer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.XMLRenderer; @@ -97,15 +97,16 @@ public class CPDTask extends Task { config.setSkipDuplicates(skipDuplicateFiles); config.setSkipLexicalErrors(skipLexicalErrors); - CPD cpd = new CPD(config); - tokenizeFiles(cpd); + try (CpdAnalysis cpd = new CpdAnalysis(config)) { + addFiles(cpd); - log("Starting to analyze code", Project.MSG_INFO); - long timeTaken = analyzeCode(cpd); - log("Done analyzing code; that took " + timeTaken + " milliseconds"); + log("Starting to analyze code", Project.MSG_INFO); + long start = System.currentTimeMillis(); + cpd.performAnalysis(this::report); + long timeTaken = System.currentTimeMillis() - start; + log("Done analyzing code; that took " + timeTaken + " milliseconds"); - log("Generating report", Project.MSG_INFO); - report(cpd); + } } catch (IOException ioe) { log(ioe.toString(), Project.MSG_ERR); throw new BuildException("IOException during task execution", ioe); @@ -137,12 +138,12 @@ public class CPDTask extends Task { return LanguageFactory.createLanguage(language, p); } - private void report(CPD cpd) throws ReportException { - if (!cpd.getMatches().hasNext()) { + private void report(CPDReport report) throws ReportException { + if (report.getMatches().isEmpty()) { log("No duplicates over " + minimumTokenCount + " tokens found", Project.MSG_INFO); } + log("Generating report", Project.MSG_INFO); CPDReportRenderer renderer = createRenderer(); - CPDReport report = cpd.toReport(); try { // will be closed via BufferedWriter/OutputStreamWriter chain down below @@ -167,26 +168,17 @@ public class CPDTask extends Task { } } - private void tokenizeFiles(CPD cpd) throws IOException { + private void addFiles(CpdAnalysis cpd) throws IOException { for (FileSet fileSet : filesets) { DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject()); String[] includedFiles = directoryScanner.getIncludedFiles(); - for (int i = 0; i < includedFiles.length; i++) { - File file = new File( - directoryScanner.getBasedir() + System.getProperty("file.separator") + includedFiles[i]); - log("Tokenizing " + file.getAbsolutePath(), Project.MSG_VERBOSE); - cpd.add(file); + for (String includedFile : includedFiles) { + Path file = directoryScanner.getBasedir().toPath().resolve(includedFile); + cpd.files().addFile(file); } } } - private long analyzeCode(CPD cpd) { - long start = System.currentTimeMillis(); - cpd.go(); - long stop = System.currentTimeMillis(); - return stop - start; - } - private CPDReportRenderer createRenderer() { if (TEXT_FORMAT.equals(format)) { return new SimpleRenderer(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/ReportException.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/ReportException.java similarity index 74% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/ReportException.java rename to pmd-ant/src/main/java/net/sourceforge/pmd/ant/ReportException.java index 6b866638f9..258dcfae86 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/ReportException.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/ReportException.java @@ -1,13 +1,13 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.ant; /** * @author Philippe T'Seyen */ -public class ReportException extends Exception { +public class ReportException extends RuntimeException { private static final long serialVersionUID = 6043174086675858209L; public ReportException(Throwable cause) { diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java index 9cc3cd00ad..3976feb551 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java @@ -30,7 +30,7 @@ class ApexCpdTest { void testIssue427() throws Exception { CPDConfiguration configuration = new CPDConfiguration(); configuration.setMinimumTileSize(10); - configuration.setLanguage(LanguageFactory.createLanguage(ApexLanguageModule.TERSE_NAME)); + configuration.setLanguage(ApexLanguageModule.getInstance()); try (CpdAnalysis cpd = new CpdAnalysis(configuration)) { cpd.files().addFile(testdir.resolve("SFDCEncoder.cls")); cpd.files().addFile(testdir.resolve("SFDCEncoderConstants.cls")); @@ -38,7 +38,7 @@ class ApexCpdTest { cpd.performAnalysis(matches -> { assertEquals(1, matches.getMatches().size()); Match firstDuplication = matches.getMatches().get(0); - assertTrue(firstDuplication.getSourceCodeSlice().startsWith("global with sharing class SFDCEncoder")); + assertTrue(matches.getSourceCodeSlice(firstDuplication).startsWith("global with sharing class SFDCEncoder")); }); } } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index b228f2ee92..af9de892b1 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -13,18 +13,18 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.lang3.mutable.MutableBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.cli.commands.typesupport.internal.CpdLanguageTypeSupport; import net.sourceforge.pmd.cli.internal.CliExitCode; -import net.sourceforge.pmd.cpd.CPD; import net.sourceforge.pmd.cpd.CPDConfiguration; -import net.sourceforge.pmd.cpd.CPDReport; -import net.sourceforge.pmd.cpd.Language; +import net.sourceforge.pmd.cpd.CpdAnalysis; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.internal.LogMessages; import net.sourceforge.pmd.internal.util.IOUtil; +import net.sourceforge.pmd.lang.Language; import picocli.CommandLine.Command; import picocli.CommandLine.Option; @@ -130,17 +130,22 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { protected CliExitCode execute() { final Logger logger = LoggerFactory.getLogger(CpdCommand.class); - // TODO : Create a new CpdAnalysis to match PmdAnalysis final CPDConfiguration configuration = toConfiguration(); - final CPD cpd = new CPD(configuration); - try { - cpd.go(); + try (CpdAnalysis cpd = new CpdAnalysis(configuration)){ - final CPDReport report = cpd.toReport(); - configuration.getCPDReportRenderer().render(report, IOUtil.createWriter(Charset.defaultCharset(), null)); + MutableBoolean hasViolations = new MutableBoolean(); + cpd.performAnalysis(report -> { + try { + configuration.getCPDReportRenderer().render(report, IOUtil.createWriter(Charset.defaultCharset(), null)); + hasViolations.setValue(!report.getMatches().isEmpty()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); - if (cpd.getMatches().hasNext() && configuration.isFailOnViolation()) { + + if (hasViolations.booleanValue() && configuration.isFailOnViolation()) { return CliExitCode.VIOLATIONS_FOUND; } } catch (IOException | RuntimeException e) { diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/CpdLanguageTypeSupport.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/CpdLanguageTypeSupport.java index 29868b8da7..4f4a7fa3af 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/CpdLanguageTypeSupport.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/CpdLanguageTypeSupport.java @@ -4,30 +4,18 @@ package net.sourceforge.pmd.cli.commands.typesupport.internal; -import java.util.Arrays; -import java.util.Iterator; - -import net.sourceforge.pmd.cpd.Language; -import net.sourceforge.pmd.cpd.LanguageFactory; - -import picocli.CommandLine.ITypeConverter; +import net.sourceforge.pmd.lang.LanguageRegistry; /** - * Provider of candidates / conversion support for supported CPD languages. + * Provider of candidates / conversion support for supported PMD languages. * * Beware, the help will report this on runtime, and be accurate to available * modules in the classpath, but autocomplete will include all available at build time. */ -public class CpdLanguageTypeSupport implements ITypeConverter, Iterable { +public class CpdLanguageTypeSupport extends LanguageTypeSupport { - @Override - public Iterator iterator() { - return Arrays.stream(LanguageFactory.supportedLanguages).iterator(); - } - - @Override - public Language convert(final String languageString) { - // TODO : If an unknown value is passed, AnyLanguage is returned silentlyโ€ฆ - return LanguageFactory.createLanguage(languageString); + public CpdLanguageTypeSupport() { + super(LanguageRegistry.CPD); } + } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/LanguageTypeSupport.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/LanguageTypeSupport.java new file mode 100644 index 0000000000..4cce4844b2 --- /dev/null +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/LanguageTypeSupport.java @@ -0,0 +1,42 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cli.commands.typesupport.internal; + +import java.util.Iterator; + +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; + +import picocli.CommandLine.ITypeConverter; +import picocli.CommandLine.TypeConversionException; + +/** + * Provider of candidates / conversion support for supported PMD languages. + *

+ * Beware, the help will report this on runtime, and be accurate to available + * modules in the classpath, but autocomplete will include all available at build time. + */ +public class LanguageTypeSupport implements ITypeConverter, Iterable { + + private final LanguageRegistry languageRegistry; + + public LanguageTypeSupport(LanguageRegistry languageRegistry) { + this.languageRegistry = languageRegistry; + } + + @Override + public Language convert(final String value) { + Language lang = languageRegistry.getLanguageById(value); + if (lang == null) { + throw new TypeConversionException("Unknown language: " + value); + } + return lang; + } + + @Override + public Iterator iterator() { + return languageRegistry.getLanguages().stream().map(Language::getId).iterator(); + } +} diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageTypeSupport.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageTypeSupport.java index a84dfcff24..05fcc49133 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageTypeSupport.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageTypeSupport.java @@ -4,31 +4,18 @@ package net.sourceforge.pmd.cli.commands.typesupport.internal; -import java.util.Iterator; - -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; -import picocli.CommandLine.ITypeConverter; -import picocli.CommandLine.TypeConversionException; - /** * Provider of candidates / conversion support for supported PMD languages. * * Beware, the help will report this on runtime, and be accurate to available * modules in the classpath, but autocomplete will include all available at build time. */ -public class PmdLanguageTypeSupport implements ITypeConverter, Iterable { +public class PmdLanguageTypeSupport extends LanguageTypeSupport { - @Override - public Language convert(final String value) throws Exception { - return LanguageRegistry.PMD.getLanguages().stream() - .filter(l -> l.getTerseName().equals(value)).findFirst() - .orElseThrow(() -> new TypeConversionException("Unknown language: " + value)); + public PmdLanguageTypeSupport() { + super(LanguageRegistry.PMD); } - @Override - public Iterator iterator() { - return LanguageRegistry.PMD.getLanguages().stream().map(Language::getTerseName).iterator(); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java index 9620e109cb..2763d8dd62 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java @@ -6,13 +6,20 @@ package net.sourceforge.pmd; import java.nio.charset.Charset; import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; +import net.sourceforge.pmd.util.AssertionUtil; +import net.sourceforge.pmd.util.log.MessageReporter; /** * Base configuration class for both PMD and CPD. @@ -25,16 +32,15 @@ public abstract class AbstractConfiguration { private boolean debug; private final Map langProperties = new HashMap<>(); private final LanguageRegistry langRegistry; + private MessageReporter reporter; + private final LanguageVersionDiscoverer languageVersionDiscoverer; + private LanguageVersion forceLanguageVersion; - /** - * Create a new abstract configuration. - */ - protected AbstractConfiguration() { - super(); - } - protected AbstractConfiguration(LanguageRegistry languageRegistry) { + protected AbstractConfiguration(LanguageRegistry languageRegistry, MessageReporter messageReporter) { this.langRegistry = Objects.requireNonNull(languageRegistry); + this.languageVersionDiscoverer = new LanguageVersionDiscoverer(languageRegistry); + this.reporter = Objects.requireNonNull(messageReporter); } /** @@ -96,4 +102,123 @@ public abstract class AbstractConfiguration { "Language '" + language.getId() + "' is not registered in " + getLanguageRegistry()); } } + + public LanguageRegistry getLanguageRegistry() { + return langRegistry; + } + + /** + * Returns the message reporter that is to be used while running + * the analysis. + */ + public @NonNull MessageReporter getReporter() { + return reporter; + } + + /** + * Sets the message reporter that is to be used while running + * the analysis. + * + * @param reporter A non-null message reporter + */ + public void setReporter(@NonNull MessageReporter reporter) { + AssertionUtil.requireParamNotNull("reporter", reporter); + this.reporter = reporter; + } + + /** + * Get the LanguageVersionDiscoverer, used to determine the LanguageVersion + * of a source file. + * + * @return The LanguageVersionDiscoverer. + */ + public LanguageVersionDiscoverer getLanguageVersionDiscoverer() { + return languageVersionDiscoverer; + } + + /** + * Get the LanguageVersion specified by the force-language parameter. This overrides detection based on file + * extensions + * + * @return The LanguageVersion. + */ + public LanguageVersion getForceLanguageVersion() { + return forceLanguageVersion; + } + + /** + * Is the force-language parameter set to anything? + * + * @return true if ${@link #getForceLanguageVersion()} is not null + */ + public boolean isForceLanguageVersion() { + return forceLanguageVersion != null; + } + + /** + * Set the LanguageVersion specified by the force-language parameter. This overrides detection based on file + * extensions + * + * @param forceLanguageVersion the language version + */ + public void setForceLanguageVersion(@Nullable LanguageVersion forceLanguageVersion) { + if (forceLanguageVersion != null) { + checkLanguageIsRegistered(forceLanguageVersion.getLanguage()); + } + this.forceLanguageVersion = forceLanguageVersion; + languageVersionDiscoverer.setForcedVersion(forceLanguageVersion); + } + + /** + * Set the given LanguageVersion as the current default for it's Language. + * + * @param languageVersion + * the LanguageVersion + */ + public void setDefaultLanguageVersion(LanguageVersion languageVersion) { + Objects.requireNonNull(languageVersion); + languageVersionDiscoverer.setDefaultLanguageVersion(languageVersion); + getLanguageProperties(languageVersion.getLanguage()).setLanguageVersion(languageVersion.getVersion()); + } + + /** + * Set the given LanguageVersions as the current default for their + * Languages. + * + * @param languageVersions + * The LanguageVersions. + */ + public void setDefaultLanguageVersions(List languageVersions) { + for (LanguageVersion languageVersion : languageVersions) { + setDefaultLanguageVersion(languageVersion); + } + } + + /** + * Get the LanguageVersion of the source file with given name. This depends + * on the fileName extension, and the java version. + *

+ * For compatibility with older code that does not always pass in a correct + * filename, unrecognized files are assumed to be java files. + *

+ * + * @param fileName + * Name of the file, can be absolute, or simple. + * @return the LanguageVersion + */ + // FUTURE Delete this? I can't think of a good reason to keep it around. + // Failure to determine the LanguageVersion for a file should be a hard + // error, or simply cause the file to be skipped? + public @Nullable LanguageVersion getLanguageVersionOfFile(String fileName) { + LanguageVersion forcedVersion = getForceLanguageVersion(); + if (forcedVersion != null) { + // use force language if given + return forcedVersion; + } + + // otherwise determine by file extension + return languageVersionDiscoverer.getDefaultLanguageVersionForFile(fileName); + } + + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 58130a1ea4..1f461353d0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -13,9 +13,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Properties; @@ -29,15 +27,10 @@ import net.sourceforge.pmd.cache.FileAnalysisCache; import net.sourceforge.pmd.cache.NoopAnalysisCache; import net.sourceforge.pmd.cli.PmdParametersParseResult; import net.sourceforge.pmd.internal.util.ClasspathClassLoader; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.renderers.RendererFactory; import net.sourceforge.pmd.util.AssertionUtil; -import net.sourceforge.pmd.util.log.MessageReporter; import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; /** @@ -111,9 +104,6 @@ public class PMDConfiguration extends AbstractConfiguration { private String suppressMarker = DEFAULT_SUPPRESS_MARKER; private int threads = Runtime.getRuntime().availableProcessors(); private ClassLoader classLoader = getClass().getClassLoader(); - private final LanguageVersionDiscoverer languageVersionDiscoverer; - private LanguageVersion forceLanguageVersion; - private MessageReporter reporter = new SimpleMessageReporter(LoggerFactory.getLogger(PMD.class)); // Rule and source file options private List ruleSets = new ArrayList<>(); @@ -138,15 +128,13 @@ public class PMDConfiguration extends AbstractConfiguration { private AnalysisCache analysisCache = new NoopAnalysisCache(); private boolean ignoreIncrementalAnalysis; private final List relativizeRoots = new ArrayList<>(); - private final Map langProperties = new HashMap<>(); public PMDConfiguration() { this(DEFAULT_REGISTRY); } public PMDConfiguration(@NonNull LanguageRegistry languageRegistry) { - super(languageRegistry); - this.languageVersionDiscoverer = new LanguageVersionDiscoverer(languageRegistry); + super(languageRegistry, new SimpleMessageReporter(LoggerFactory.getLogger(PMD.class))); } /** @@ -272,123 +260,6 @@ public class PMDConfiguration extends AbstractConfiguration { } } - /** - * Returns the message reporter that is to be used while running - * the analysis. - */ - public @NonNull MessageReporter getReporter() { - return reporter; - } - - /** - * Sets the message reporter that is to be used while running - * the analysis. - * - * @param reporter A non-null message reporter - */ - public void setReporter(@NonNull MessageReporter reporter) { - AssertionUtil.requireParamNotNull("reporter", reporter); - this.reporter = reporter; - } - - /** - * Get the LanguageVersionDiscoverer, used to determine the LanguageVersion - * of a source file. - * - * @return The LanguageVersionDiscoverer. - */ - public LanguageVersionDiscoverer getLanguageVersionDiscoverer() { - return languageVersionDiscoverer; - } - - /** - * Get the LanguageVersion specified by the force-language parameter. This overrides detection based on file - * extensions - * - * @return The LanguageVersion. - */ - public LanguageVersion getForceLanguageVersion() { - return forceLanguageVersion; - } - - /** - * Is the force-language parameter set to anything? - * - * @return true if ${@link #getForceLanguageVersion()} is not null - */ - public boolean isForceLanguageVersion() { - return forceLanguageVersion != null; - } - - /** - * Set the LanguageVersion specified by the force-language parameter. This overrides detection based on file - * extensions - * - * @param forceLanguageVersion the language version - */ - public void setForceLanguageVersion(@Nullable LanguageVersion forceLanguageVersion) { - if (forceLanguageVersion != null) { - checkLanguageIsRegistered(forceLanguageVersion.getLanguage()); - } - this.forceLanguageVersion = forceLanguageVersion; - languageVersionDiscoverer.setForcedVersion(forceLanguageVersion); - } - - /** - * Set the given LanguageVersion as the current default for it's Language. - * - * @param languageVersion - * the LanguageVersion - */ - public void setDefaultLanguageVersion(LanguageVersion languageVersion) { - Objects.requireNonNull(languageVersion); - languageVersionDiscoverer.setDefaultLanguageVersion(languageVersion); - getLanguageProperties(languageVersion.getLanguage()).setLanguageVersion(languageVersion.getVersion()); - } - - /** - * Set the given LanguageVersions as the current default for their - * Languages. - * - * @param languageVersions - * The LanguageVersions. - */ - public void setDefaultLanguageVersions(List languageVersions) { - for (LanguageVersion languageVersion : languageVersions) { - setDefaultLanguageVersion(languageVersion); - } - } - - /** - * Get the LanguageVersion of the source file with given name. This depends - * on the fileName extension, and the java version. - *

- * For compatibility with older code that does not always pass in a correct - * filename, unrecognized files are assumed to be java files. - *

- * - * @param fileName - * Name of the file, can be absolute, or simple. - * @return the LanguageVersion - */ - // FUTURE Delete this? I can't think of a good reason to keep it around. - // Failure to determine the LanguageVersion for a file should be a hard - // error, or simply cause the file to be skipped? - public @Nullable LanguageVersion getLanguageVersionOfFile(String fileName) { - LanguageVersion forcedVersion = getForceLanguageVersion(); - if (forcedVersion != null) { - // use force language if given - return forcedVersion; - } - - // otherwise determine by file extension - return languageVersionDiscoverer.getDefaultLanguageVersionForFile(fileName); - } - - LanguageRegistry getLanguageRegistry() { - return langRegistry; - } - /** * Get the comma separated list of RuleSet URIs. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index 47e0ee7791..64a6554628 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -21,6 +21,7 @@ import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.cpd.Tokens.State; import net.sourceforge.pmd.internal.util.FileFinder; import net.sourceforge.pmd.internal.util.FileUtil; import net.sourceforge.pmd.internal.util.IOUtil; @@ -40,7 +41,6 @@ public class CPD { private CPDConfiguration configuration; - private final SourceManager sourceManager = new SourceManager(); private CPDListener listener = new CPDNullListener(); private Tokens tokens = new Tokens(); private MatchAlgorithm matchAlgorithm; @@ -125,7 +125,7 @@ public class CPD { public void go() { log.debug("Running match algorithm on {} files...", sourceManager.size()); - matchAlgorithm = new MatchAlgorithm(sourceManager, tokens, configuration.getMinimumTileSize(), listener); + matchAlgorithm = new MatchAlgorithm(tokens, configuration.getMinimumTileSize(), listener); matchAlgorithm.findMatches(); log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); } @@ -232,7 +232,7 @@ public class CPD { } private void addAndSkipLexicalErrors(SourceCode sourceCode) throws IOException { - final TokenEntry.State savedState = new TokenEntry.State(); + final Tokens.State savedState = new State(); try { addAndThrowLexicalError(sourceCode); } catch (TokenMgrError e) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 1c1a3a1a7a..30d8b2a9df 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -7,24 +7,21 @@ package net.sourceforge.pmd.cpd; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.io.File; -import java.io.FilenameFilter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Properties; -import java.util.Set; + +import org.slf4j.LoggerFactory; import net.sourceforge.pmd.AbstractConfiguration; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; -import net.sourceforge.pmd.internal.util.FileFinder; -import net.sourceforge.pmd.internal.util.FileUtil; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; /** * @@ -48,8 +45,6 @@ public class CPDConfiguration extends AbstractConfiguration { } - private Language language = CPDConfiguration.getLanguageFromString(DEFAULT_LANGUAGE); - private int minimumTileSize; private boolean skipDuplicates; @@ -88,14 +83,13 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean failOnViolation = true; - private boolean debug = false; public CPDConfiguration() { - super(LanguageRegistry.CPD); + this(LanguageRegistry.CPD); } public CPDConfiguration(LanguageRegistry languageRegistry) { - super(languageRegistry); + super(languageRegistry, new SimpleMessageReporter(LoggerFactory.getLogger(CpdAnalysis.class))); } public void postContruct() { @@ -159,49 +153,15 @@ public class CPDConfiguration extends AbstractConfiguration { return result; } - public static Language getLanguageFromString(String languageString) { - return LanguageFactory.createLanguage(languageString); - } public static void setSystemProperties(CPDConfiguration configuration) { - Properties properties = new Properties(); - if (configuration.isIgnoreLiterals()) { - properties.setProperty(Tokenizer.IGNORE_LITERALS, "true"); - } else { - properties.remove(Tokenizer.IGNORE_LITERALS); - } - if (configuration.isIgnoreIdentifiers()) { - properties.setProperty(Tokenizer.IGNORE_IDENTIFIERS, "true"); - } else { - properties.remove(Tokenizer.IGNORE_IDENTIFIERS); - } - if (configuration.isIgnoreAnnotations()) { - properties.setProperty(Tokenizer.IGNORE_ANNOTATIONS, "true"); - } else { - properties.remove(Tokenizer.IGNORE_ANNOTATIONS); - } - if (configuration.isIgnoreUsings()) { - properties.setProperty(Tokenizer.IGNORE_USINGS, "true"); - } else { - properties.remove(Tokenizer.IGNORE_USINGS); - } - if (configuration.isIgnoreLiteralSequences()) { - properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, "true"); - } else { - properties.remove(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES); - } - properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS, Boolean.toString(!configuration.isNoSkipBlocks())); - properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS_PATTERN, configuration.getSkipBlocksPattern()); - configuration.getLanguage().setProperties(properties); + } - public Language getLanguage() { - return language; - } - public void setLanguage(Language language) { - this.language = language; + public void setLanguage(net.sourceforge.pmd.lang.Language language) { + setForceLanguageVersion(language.getDefaultVersion()); } public int getMinimumTileSize() { @@ -233,46 +193,6 @@ public class CPDConfiguration extends AbstractConfiguration { return cpdReportRenderer; } - public Tokenizer tokenizer() { - if (language == null) { - throw new IllegalStateException("Language is null."); - } - return language.getTokenizer(); - } - - public FilenameFilter filenameFilter() { - if (language == null) { - throw new IllegalStateException("Language is null."); - } - - final FilenameFilter languageFilter = language.getFileFilter(); - final Set exclusions = new HashSet<>(); - - if (excludes != null) { - FileFinder finder = new FileFinder(); - for (File excludedFile : excludes) { - if (excludedFile.isDirectory()) { - List files = finder.findFilesFrom(excludedFile, languageFilter, true); - for (File f : files) { - exclusions.add(FileUtil.normalizeFilename(f.getAbsolutePath())); - } - } else { - exclusions.add(FileUtil.normalizeFilename(excludedFile.getAbsolutePath())); - } - } - } - - return (dir, name) -> { - File f = new File(dir, name); - if (exclusions.contains(FileUtil.normalizeFilename(f.getAbsolutePath()))) { - System.err.println("Excluding " + f.getAbsolutePath()); - return false; - } - return languageFilter.accept(dir, name); - }; - } - - void setRenderer(CPDReportRenderer renderer) { this.cpdReportRenderer = renderer; } @@ -397,13 +317,4 @@ public class CPDConfiguration extends AbstractConfiguration { this.failOnViolation = failOnViolation; } - @Override - public boolean isDebug() { - return debug; - } - - @Override - public void setDebug(boolean debug) { - this.debug = debug; - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java index 9022192ca8..3c1ba5a562 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java @@ -11,16 +11,22 @@ import java.util.Map; import java.util.TreeMap; import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.util.Predicate; /** * @since 6.48.0 */ public class CPDReport { + + private final SourceManager sourceManager; private final List matches; private final Map numberOfTokensPerFile; - CPDReport(final List matches, final Map numberOfTokensPerFile) { + CPDReport(SourceManager sourceManager, + List matches, + Map numberOfTokensPerFile) { + this.sourceManager = sourceManager; this.matches = Collections.unmodifiableList(matches); this.numberOfTokensPerFile = Collections.unmodifiableMap(new TreeMap<>(numberOfTokensPerFile)); } @@ -33,11 +39,16 @@ public class CPDReport { return numberOfTokensPerFile; } + public Chars getSourceCodeSlice(Match match) { + return sourceManager.getSlice(match.getFirstMark()); + } + /** * Creates a new CPD report taking all the information from this report, * but filtering the matches. * * @param filter when true, the match will be kept. + * * @return copy of this report */ @Experimental @@ -49,6 +60,6 @@ public class CPDReport { } } - return new CPDReport(filtered, this.getNumberOfTokensPerFile()); + return new CPDReport(sourceManager, filtered, this.getNumberOfTokensPerFile()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index bc4e5541a7..720b8fbbc6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -18,26 +18,57 @@ import org.slf4j.LoggerFactory; import net.sourceforge.pmd.internal.util.FileCollectionUtil; import net.sourceforge.pmd.internal.util.FileUtil; import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.util.log.MessageReporter; public final class CpdAnalysis implements AutoCloseable { - private static Logger log = LoggerFactory.getLogger(CpdAnalysis.class); - private CPDConfiguration configuration; - private FileCollector files; - private MessageReporter reporter; + private static final Logger LOGGER = LoggerFactory.getLogger(CpdAnalysis.class); + private final CPDConfiguration configuration; + private final FileCollector files; + private final MessageReporter reporter; private CPDListener listener; - public CpdAnalysis(CPDConfiguration theConfiguration) throws IOException { - configuration = theConfiguration; + public CpdAnalysis(CPDConfiguration config) throws IOException { + configuration = config; + this.reporter = config.getReporter(); + this.files = FileCollector.newCollector( + config.getLanguageVersionDiscoverer(), + reporter + ); // Add all sources extractAllSources(); + + for (Language language : config.getLanguageRegistry()) { + setLanguageProperties(language, config); + } + } + + private static void setPropertyIfMissing(PropertyDescriptor prop, LanguagePropertyBundle sink, T value) { + if (!sink.isPropertyOverridden(prop)) { + sink.setProperty(prop, value); + } + } + + private void setLanguageProperties(Language language, CPDConfiguration configuration) { + LanguagePropertyBundle props = configuration.getLanguageProperties(language); + + setPropertyIfMissing(Tokenizer.CPD_ANONYMiZE_LITERALS, props, configuration.isIgnoreLiterals()); + setPropertyIfMissing(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS, props, configuration.isIgnoreIdentifiers()); + setPropertyIfMissing(Tokenizer.CPD_IGNORE_METADATA, props, configuration.isIgnoreAnnotations()); + setPropertyIfMissing(Tokenizer.CPD_IGNORE_IMPORTS, props, configuration.isIgnoreUsings()); + setPropertyIfMissing(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES, props, configuration.isIgnoreLiteralSequences()); + if (!configuration.isNoSkipBlocks()) { + PropertyDescriptor skipBlocks = (PropertyDescriptor) props.getPropertyDescriptor("cpdSkipBlocksPattern"); + setPropertyIfMissing(skipBlocks, props, configuration.getSkipBlocksPattern()); + } } public FileCollector files() { @@ -70,8 +101,8 @@ public final class CpdAnalysis implements AutoCloseable { this.listener = cpdListener; } - private int doTokenize(TextDocument document, Tokenizer tokenizer, Tokens tokens) throws IOException { - log.trace("Tokenizing {}", document.getPathId()); + private int doTokenize(TextDocument document, Tokenizer tokenizer, Tokens tokens) { + LOGGER.trace("Tokenizing {}", document.getPathId()); int lastTokenSize = tokens.size(); try { tokenizer.tokenize(document, TokenFactory.forFile(document, tokens)); @@ -80,6 +111,7 @@ public final class CpdAnalysis implements AutoCloseable { } catch (TokenMgrError e) { e.setFileName(document.getDisplayName()); reporter.errorEx("Error while lexing.", e); + throw e; } finally { tokens.addEof(); } @@ -98,25 +130,31 @@ public final class CpdAnalysis implements AutoCloseable { Tokens tokens = new Tokens(); for (TextFile textFile : sourceManager.getTextFiles()) { - TextDocument textDocument = sourceManager.get(textFile); - - int newTokens = doTokenize(textDocument, tokenizers.get(textFile.getLanguageVersion().getLanguage()), tokens); - numberOfTokensPerFile.put(textDocument.getPathId(), newTokens); - listener.addedFile(1); + Tokens.State savedState = tokens.savePoint(); + try { + int newTokens = doTokenize(textDocument, tokenizers.get(textFile.getLanguageVersion().getLanguage()), tokens); + numberOfTokensPerFile.put(textDocument.getPathId(), newTokens); + listener.addedFile(1); + } catch (TokenMgrError e) { + // already reported + savedState.restore(tokens); + } } - log.debug("Running match algorithm on {} files...", sourceManager.size()); - MatchAlgorithm matchAlgorithm = new MatchAlgorithm(sourceManager, tokens, configuration.getMinimumTileSize(), listener); + LOGGER.debug("Running match algorithm on {} files...", sourceManager.size()); + MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, configuration.getMinimumTileSize(), listener); matchAlgorithm.findMatches(); - log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); + LOGGER.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); - new CPDReport(matchAlgorithm.getMatches(), matchAlgorithm.to) + CPDReport cpdReport = new CPDReport(sourceManager, matchAlgorithm.getMatches(), numberOfTokensPerFile); + consumer.accept(cpdReport); } catch (Exception e) { reporter.errorEx("Exception while running CPD", e); } + // source manager is closed and closes all text files now. } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index faf289453a..6e9c92213f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -4,13 +4,11 @@ package net.sourceforge.pmd.cpd; -import net.sourceforge.pmd.lang.document.Chars; -import net.sourceforge.pmd.lang.document.TextDocument; +import java.util.Objects; public class Mark implements Comparable { - private TokenEntry token; + private final TokenEntry token; private TokenEntry endToken; - private int lineCount; public Mark(TokenEntry token) { this.token = token; @@ -59,27 +57,13 @@ public class Mark implements Comparable { } public int getLineCount() { - return this.lineCount; + return this.endToken == null ? 1 : this.endToken.getBeginLine(); } - public void setLineCount(int lineCount) { - this.lineCount = lineCount; - } - - public void setEndToken(TokenEntry endToken) { + void setEndToken(TokenEntry endToken) { this.endToken = endToken; } - /** Newlines are normalized to \n. */ - public Chars getSourceCodeSlice() { - return this.code.sliceOriginalText( - this.code.createLineRange(getBeginLine(), getEndLine()) - ); - } - - public void setSourceCode(TextDocument code) { - this.code = code; - } @Override public int hashCode() { @@ -101,14 +85,7 @@ public class Mark implements Comparable { return false; } Mark other = (Mark) obj; - if (token == null) { - if (other.token != null) { - return false; - } - } else if (!token.equals(other.token)) { - return false; - } - return true; + return Objects.equals(token, other.token); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java index 6c625ef313..38bcce41ce 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java @@ -10,7 +10,6 @@ import java.util.Set; import java.util.TreeSet; import net.sourceforge.pmd.PMD; -import net.sourceforge.pmd.lang.document.Chars; public class Match implements Comparable, Iterable { @@ -55,10 +54,6 @@ public class Match implements Comparable, Iterable { return this.tokenCount; } - /** Newlines are normalized to \n. */ - public Chars getSourceCodeSlice() { - return this.getMark(0).getSourceCodeSlice(); - } @Override public Iterator iterator() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java index ca198f032f..93cf8184ef 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java @@ -11,26 +11,22 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import net.sourceforge.pmd.lang.document.TextDocument; - class MatchAlgorithm { private static final int MOD = 37; private int lastMod = 1; private List matches; - private Map source; - private Tokens tokens; - private List code; + private final Tokens tokens; + private final List code; private CPDListener cpdListener; - private int min; + private final int min; - public MatchAlgorithm(Map sourceCode, Tokens tokens, int min) { - this(sourceCode, tokens, min, new CPDNullListener()); + public MatchAlgorithm(Tokens tokens, int min) { + this(tokens, min, new CPDNullListener()); } - public MatchAlgorithm(SourceManager sourceCode, Tokens tokens, int min, CPDListener listener) { - this.source = sourceCode; + public MatchAlgorithm(Tokens tokens, int min, CPDListener listener) { this.tokens = tokens; this.code = tokens.getTokens(); this.min = min; @@ -82,14 +78,9 @@ class MatchAlgorithm { for (Match match : matches) { for (Mark mark : match) { TokenEntry token = mark.getToken(); - int lineCount = tokens.getLineCount(token, match); TokenEntry endToken = tokens.getEndToken(token, match); - mark.setLineCount(lineCount); mark.setEndToken(endToken); - TextDocument sourceCode = source.get(token.getTokenSrcID()); - assert sourceCode != null : token.getTokenSrcID() + " is not registered in " + source.keySet(); - mark.setSourceCode(sourceCode); } } cpdListener.phaseUpdate(CPDListener.DONE); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java index 9ec4b99bc7..dae0dff7ab 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java @@ -37,21 +37,21 @@ public class SimpleRenderer implements CPDReportRenderer { public void render(CPDReport report, Writer writer) throws IOException { Iterator matches = report.getMatches().iterator(); if (matches.hasNext()) { - renderOn(writer, matches.next()); + renderOn(report, writer, matches.next()); } while (matches.hasNext()) { Match match = matches.next(); writer.append(separator).append(PMD.EOL); - renderOn(writer, match); + renderOn(report, writer, match); } writer.flush(); } - private void renderOn(Writer writer, Match match) throws IOException { + private void renderOn(CPDReport report, Writer writer, Match match) throws IOException { writer.append("Found a ").append(String.valueOf(match.getLineCount())).append(" line (").append(String.valueOf(match.getTokenCount())) - .append(" tokens) duplication in the following files: ").append(PMD.EOL); + .append(" tokens) duplication in the following files: ").append(PMD.EOL); for (Mark mark : match) { writer.append("Starting at line ").append(String.valueOf(mark.getBeginLine())).append(" of ").append(mark.getFilename()) @@ -60,7 +60,7 @@ public class SimpleRenderer implements CPDReportRenderer { writer.append(PMD.EOL); // add a line to separate the source from the desc above - Chars source = match.getSourceCodeSlice(); + Chars source = report.getSourceCodeSlice(match); if (trimLeadingWhitespace) { for (Chars line : StringUtil.linesWithTrimIndent(source)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index be2e1bf266..5ae331cfe1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -7,21 +7,26 @@ package net.sourceforge.pmd.cpd; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import net.sourceforge.pmd.internal.util.IOUtil; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.lang.document.TextRegion; -public class SourceManager implements AutoCloseable { +class SourceManager implements AutoCloseable { private final Map> files = new ConcurrentHashMap<>(); + private final Map fileByName = new HashMap<>(); private final List textFiles; public SourceManager(List files) { textFiles = new ArrayList<>(files); + files.forEach(f -> fileByName.put(f.getPathId(), f)); } @@ -53,4 +58,12 @@ public class SourceManager implements AutoCloseable { throw exception; } } + + public Chars getSlice(Mark mark) { + TextFile textFile = fileByName.get(mark.getFilename()); + assert textFile != null; + TextDocument doc = get(textFile); + TextRegion lineRange = doc.createLineRange(mark.getBeginLine(), mark.getEndLine()); + return doc.sliceOriginalText(lineRange); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java index cc6262b1e9..6c4ef2c9b8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java @@ -4,20 +4,11 @@ package net.sourceforge.pmd.cpd; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.document.FileLocation; - public class TokenEntry implements Comparable { public static final TokenEntry EOF = new TokenEntry(); - private String tokenSrcID; + private final String tokenSrcID; private final int beginLine; private final int beginColumn; private final int endColumn; @@ -33,73 +24,23 @@ public class TokenEntry implements Comparable { this.endColumn = -1; } - /** - * Creates a new token entry with the given informations. - * @param image - * @param tokenSrcID - * @param beginLine the linenumber, 1-based. - * - * @deprecated Use {@link #TokenEntry(String, String, int, int, int)}, don't be lazy - */ - @Deprecated - public TokenEntry(String image, String tokenSrcID, int beginLine) { - this(image, tokenSrcID, beginLine, -1, -1); - } - - public TokenEntry(String image, String tokenSrcID, int beginLine, int beginColumn, int endLine, int endColumn) { - assert isOk(beginLine) && isOk(beginColumn) && isOk(endColumn) : "Coordinates are 1-based"; - setImage(image); + TokenEntry(int imageId, String tokenSrcID, int beginLine, int beginColumn, int endLine, int endColumn, int index) { + assert isOk(beginLine) && isOk(beginColumn) && isOk(endLine) && isOk(endColumn) : "Coordinates are 1-based"; this.tokenSrcID = tokenSrcID; this.beginLine = beginLine; this.beginColumn = beginColumn; this.endColumn = endColumn; + this.identifier = imageId; + this.index = index; } - public TokenEntry(String image, FileLocation location) { - this(image, location.getFileName(), location.getStartLine(), location.getStartColumn(), location.getEndColumn()); - } private boolean isOk(int coord) { return coord >= 1 || coord == -1; } - public static void clearImages() { - TOKENS.get().clear(); - TOKENS.remove(); - TOKEN_COUNT.remove(); - } - /** - * Helper class to preserve and restore the current state of the token - * entries. - * - * @deprecated This is internal API. - */ - @InternalApi - @Deprecated - public static class State { - private final int tokenCount; - private final int tokensMapSize; - - public State() { - this.tokenCount = TokenEntry.TOKEN_COUNT.get().intValue(); - this.tokensMapSize = TokenEntry.TOKENS.get().size(); - } - - public void restore(Tokens tokens) { - final List entries = tokens.getTokens(); - TokenEntry.TOKEN_COUNT.get().set(tokenCount); - final Iterator> it = TOKENS.get().entrySet().iterator(); - while (it.hasNext()) { - if (it.next().getValue() > tokensMapSize) { - it.remove(); - } - } - entries.subList(tokenCount, entries.size()).clear(); - } - } - - String getTokenSrcID() { + String getTokenSrcID() { return tokenSrcID; } @@ -146,11 +87,10 @@ public class TokenEntry implements Comparable { @Override public boolean equals(Object o) { // make sure to recognize EOF regardless of hashCode (hashCode is irrelevant for EOF) - if (this == EOF) { - return o == EOF; - } - if (o == EOF) { - return this == EOF; + if (this == o) { + return true; + } else if (o == EOF || this == EOF) { + return false; } // any token except EOF if (!(o instanceof TokenEntry)) { @@ -170,12 +110,7 @@ public class TokenEntry implements Comparable { if (EOF.equals(this)) { return "EOF"; } - for (Map.Entry e : TOKENS.get().entrySet()) { - if (e.getValue().intValue() == identifier) { - return e.getKey(); - } - } - return "--unknown--"; + return Integer.toString(identifier); } final void setImageIdentifier(int identifier) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 3bda7c57d6..1e0841a125 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -9,24 +9,11 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; public class Tokens { private final List tokens = new ArrayList<>(); - - private static final ThreadLocal> TOKENS = new ThreadLocal>() { - @Override - protected Map initialValue() { - return new HashMap<>(); - } - }; - private static final ThreadLocal TOKEN_COUNT = new ThreadLocal() { - @Override - protected AtomicInteger initialValue() { - return new AtomicInteger(0); - } - }; + private final Map images = new HashMap<>(); private void add(TokenEntry tokenEntry) { this.tokens.add(tokenEntry); @@ -37,14 +24,14 @@ public class Tokens { } void setImage(TokenEntry entry, String newImage) { - Integer i = TOKENS.get().get(newImage); - if (i == null) { - i = TOKENS.get().size() + 1; - TOKENS.get().put(newImage, i); - } + int i = getImageId(newImage); entry.setImageIdentifier(i); } + private int getImageId(String newImage) { + return images.computeIfAbsent(newImage, k -> images.size() + 1); + } + public TokenEntry peekLastToken() { return get(size() - 1); } @@ -77,7 +64,39 @@ public class Tokens { return tokens; } - void addToken(String image, String fileName, int startLine, int startCol, int endLine, int endCol) { - + TokenEntry addToken(String image, String fileName, int startLine, int startCol, int endLine, int endCol) { + TokenEntry newToken = new TokenEntry(getImageId(image), fileName, + startLine, startCol, + endLine, endCol, + tokens.size()); + add(newToken); + return newToken; } + + public State savePoint() { + return new State(this); + } + + /** + * Helper class to preserve and restore the current state of the token + * entries. + */ + static final class State { + + private final int tokenCount; + private final int tokensMapSize; + + State(Tokens tokens) { + this.tokenCount = tokens.tokens.size(); + this.tokensMapSize = tokens.images.size(); + } + + public void restore(Tokens tokens) { + tokens.images.entrySet().removeIf(e -> e.getValue() > tokensMapSize); + + final List entries = tokens.getTokens(); + entries.subList(tokenCount, entries.size()).clear(); + } + } + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java index 2577a9d91e..b2738ec871 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java @@ -108,7 +108,7 @@ public final class XMLRenderer implements CPDReportRenderer { for (Match match : report.getMatches()) { root.appendChild(addCodeSnippet(doc, - addFilesToDuplicationElement(doc, createDuplicationElement(doc, match), match), match)); + addFilesToDuplicationElement(doc, createDuplicationElement(doc, match), match), match, report)); } dumpDocToWriter(doc, writer); writer.flush(); @@ -141,8 +141,8 @@ public final class XMLRenderer implements CPDReportRenderer { return duplication; } - private Element addCodeSnippet(Document doc, Element duplication, Match match) { - Chars codeSnippet = match.getSourceCodeSlice(); + private Element addCodeSnippet(Document doc, Element duplication, Match match, CPDReport report) { + Chars codeSnippet = report.getSourceCodeSlice(match); if (codeSnippet != null) { // the code snippet has normalized line endings String platformSpecific = codeSnippet.toString().replace("\n", System.lineSeparator()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java index 1dbcf6b82c..903818f7e1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java @@ -17,6 +17,7 @@ import net.sourceforge.pmd.util.log.MessageReporter; import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; public class TreeExportConfiguration extends AbstractConfiguration { + private static final Logger LOG = LoggerFactory.getLogger(TreeExportConfiguration.class); private String format = "xml"; @@ -27,15 +28,22 @@ public class TreeExportConfiguration extends AbstractConfiguration { private boolean readStdin; private MessageReporter messageReporter = new SimpleMessageReporter(LOG); - + public TreeExportConfiguration(LanguageRegistry registry) { + super(registry, new SimpleMessageReporter(LoggerFactory.getLogger(TreeExporter.class))); + } + + public TreeExportConfiguration() { + this(LanguageRegistry.PMD); + } + public String getFormat() { return format; } - + public Language getLanguage() { return language; } - + public Properties getProperties() { return properties; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java index b158ab83af..8915a95624 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java @@ -18,6 +18,7 @@ import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; class CSVRendererTest { + private final Tokens tokens = new Tokens(); @Test void testLineCountPerFile() throws IOException { CPDReportRenderer renderer = new CSVRenderer(true); @@ -56,12 +57,8 @@ class CSVRendererTest { } private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) { - Tokens tokens = new Tokens(); tokens.addToken(image, tokenSrcID, beginLine, beginLine, beginLine, beginLine); - Mark result = new Mark(tokens.peekLastToken()); - result.setLineCount(lineCount); - result.setSourceCode(new SourceCode(new SourceCode.StringCodeLoader(code))); - return result; + return new Mark(tokens.peekLastToken()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java index 20b98ae04f..c2c0869c7b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java @@ -8,47 +8,41 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.SourceCode.StringCodeLoader; - class MarkTest { @Test void testSimple() { String filename = "/var/Foo.java"; + Tokens tokens = new Tokens(); int beginLine = 1; - TokenEntry token = new TokenEntry("public", "/var/Foo.java", 1, 2, 3, 4); + TokenEntry token = tokens.addToken("public", filename, beginLine, 2, 3, 4); Mark mark = new Mark(token); - int lineCount = 10; - mark.setLineCount(lineCount); - String codeFragment = "code fragment"; - mark.setSourceCode(new SourceCode(new StringCodeLoader(codeFragment))); assertEquals(token, mark.getToken()); assertEquals(filename, mark.getFilename()); assertEquals(beginLine, mark.getBeginLine()); - assertEquals(lineCount, mark.getLineCount()); - assertEquals(beginLine + lineCount - 1, mark.getEndLine()); - assertEquals(-1, mark.getBeginColumn()); - assertEquals(-1, mark.getEndColumn()); - assertEquals(codeFragment, mark.getSourceCodeSlice()); + assertEquals(1, mark.getLineCount()); + assertEquals(beginLine, mark.getEndLine()); + assertEquals(2, mark.getBeginColumn()); + assertEquals(4, mark.getEndColumn()); } @Test void testColumns() { final String filename = "/var/Foo.java"; + Tokens tokens = new Tokens(); final int beginLine = 1; final int beginColumn = 2; - final int endColumn = 3; - final TokenEntry token = new TokenEntry("public", "/var/Foo.java", 1, beginColumn, 1, beginColumn + "public".length()); - final TokenEntry endToken = new TokenEntry("}", "/var/Foo.java", 5, 1, endColumn - 1, endColumn); + final int endColumn = 2; + final int lineCount = 10; + TokenEntry token = tokens.addToken("public", filename, beginLine, beginColumn, beginLine, + beginColumn + "public".length()); + TokenEntry endToken = tokens.addToken("}", filename, + beginLine + lineCount, 1, beginLine + lineCount, endColumn); final Mark mark = new Mark(token); - final int lineCount = 10; - mark.setLineCount(lineCount); mark.setEndToken(endToken); - final String codeFragment = "code fragment"; - mark.setSourceCode(new SourceCode(new StringCodeLoader(codeFragment))); assertEquals(token, mark.getToken()); assertEquals(filename, mark.getFilename()); @@ -57,6 +51,5 @@ class MarkTest { assertEquals(beginLine + lineCount - 1, mark.getEndLine()); assertEquals(beginColumn, mark.getBeginColumn()); assertEquals(endColumn, mark.getEndColumn()); - assertEquals(codeFragment, mark.getSourceCodeSlice()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java index 633d725691..6f3c5df07a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.cpd; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -12,24 +13,29 @@ import java.util.Iterator; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.TextFile; + class MatchTest { @Test void testSimple() { - int lineCount1 = 10; - String codeFragment1 = "code fragment"; - Mark mark1 = createMark("public", "/var/Foo.java", 1, lineCount1, codeFragment1); + String codeFragment1 = "1234567890"; + String fileName = "/var/Foo.java"; + TextFile tf = TextFile.forCharSeq(codeFragment1, fileName, DummyLanguageModule.getInstance().getDefaultVersion()); + SourceManager sourceManager = new SourceManager(listOf(tf)); + Tokens tokens = new Tokens(); + Mark mark1 = new Mark(tokens.addToken("public", fileName, 1, 1, 1, 1 + "public".length())); - int lineCount2 = 20; - String codeFragment2 = "code fragment 2"; - Mark mark2 = createMark("class", "/var/Foo.java", 1, lineCount2, codeFragment2); + Mark mark2 = new Mark(tokens.addToken("public", fileName, 1, 1, 1, 1 + "public".length())); Match match = new Match(1, mark1, mark2); assertEquals(1, match.getTokenCount()); // Returns the line count of the first mark - assertEquals(lineCount1, match.getLineCount()); + assertEquals(1, match.getLineCount()); // Returns the source code of the first mark - assertEquals(codeFragment1, match.getSourceCodeSlice()); + assertEquals(Chars.wrap("123456"), sourceManager.getSlice(match.getFirstMark())); Iterator i = match.iterator(); Mark occurrence1 = i.next(); Mark occurrence2 = i.next(); @@ -37,28 +43,24 @@ class MatchTest { assertFalse(i.hasNext()); assertEquals(mark1, occurrence1); - assertEquals(lineCount1, occurrence1.getLineCount()); - assertEquals(codeFragment1, occurrence1.getSourceCodeSlice()); + assertEquals(1, occurrence1.getLineCount()); + assertEquals(Chars.wrap("123456"), sourceManager.getSlice(mark1)); assertEquals(mark2, occurrence2); - assertEquals(lineCount2, occurrence2.getLineCount()); - assertEquals(codeFragment2, occurrence2.getSourceCodeSlice()); + assertEquals(1, occurrence2.getLineCount()); + assertEquals(Chars.wrap("123456"), sourceManager.getSlice(mark2)); } @Test void testCompareTo() { - Match m1 = new Match(1, new TokenEntry("public", "/var/Foo.java", 1, 2, 3, 4), - new TokenEntry("class", "/var/Foo.java", 1, 2, 3, 4)); - Match m2 = new Match(2, new TokenEntry("Foo", "/var/Foo.java", 1, 2, 3, 4), - new TokenEntry("{", "/var/Foo.java", 1, 2, 3, 4)); + Tokens tokens = new Tokens(); + + String fileName = "/var/Foo.java"; + Match m1 = new Match(1, + tokens.addToken("public", fileName, 1, 2, 3, 4), + tokens.addToken("class", fileName, 1, 2, 3, 4)); + Match m2 = new Match(2, tokens.addToken("Foo", fileName, 1, 2, 3, 4), + tokens.addToken("{", fileName, 1, 2, 3, 4)); assertTrue(m2.compareTo(m1) < 0); } - - private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) { - Mark result = new Mark(new TokenEntry(image, tokenSrcID, beginLine, 1, beginLine, 1 + image.length())); - - result.setLineCount(lineCount); - result.setSourceCode(new SourceCode(new SourceCode.StringCodeLoader(code))); - return result; - } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index 6ef6b4b6d4..7f15face57 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -39,6 +39,7 @@ class XMLRendererTest { private static final String FORM_FEED = "\u000C"; // this character is invalid in XML 1.0 documents private static final String FORM_FEED_ENTITY = " "; // this is also not allowed in XML 1.0 documents + Tokens tokens = new Tokens(); @Test void testWithNoDuplication() throws IOException, ParserConfigurationException, SAXException { @@ -61,8 +62,8 @@ class XMLRendererTest { List list = new ArrayList<>(); int lineCount = 6; String codeFragment = "code\nfragment"; - Mark mark1 = createMark("public", "/var/Foo.java", 1, lineCount, codeFragment); - Mark mark2 = createMark("stuff", "/var/Foo.java", 73, lineCount, codeFragment); + Mark mark1 = createMark("public", "/var/Foo.java", 1, lineCount); + Mark mark2 = createMark("stuff", "/var/Foo.java", 73, lineCount); Match match = new Match(75, mark1, mark2); list.add(match); @@ -104,15 +105,13 @@ class XMLRendererTest { CPDReportRenderer renderer = new XMLRenderer(); List list = new ArrayList<>(); int lineCount1 = 6; - String codeFragment1 = "code fragment"; - Mark mark1 = createMark("public", "/var/Foo.java", 48, lineCount1, codeFragment1); - Mark mark2 = createMark("void", "/var/Foo.java", 73, lineCount1, codeFragment1); + Mark mark1 = createMark("public", "/var/Foo.java", 48, lineCount1); + Mark mark2 = createMark("void", "/var/Foo.java", 73, lineCount1); Match match1 = new Match(75, mark1, mark2); int lineCount2 = 7; - String codeFragment2 = "code fragment 2"; - Mark mark3 = createMark("void", "/var/Foo2.java", 49, lineCount2, codeFragment2); - Mark mark4 = createMark("stuff", "/var/Foo2.java", 74, lineCount2, codeFragment2); + Mark mark3 = createMark("void", "/var/Foo2.java", 49, lineCount2); + Mark mark4 = createMark("stuff", "/var/Foo2.java", 74, lineCount2); Match match2 = new Match(76, mark3, mark4); list.add(match1); @@ -176,8 +175,8 @@ class XMLRendererTest { CPDReportRenderer renderer = new XMLRenderer(); List list = new ArrayList<>(); final String espaceChar = "<"; - Mark mark1 = createMark("public", "/var/A\";"; CPDReportRenderer renderer = new XMLRenderer(); List list = new ArrayList<>(); - Mark mark1 = createMark("public", "file1", 1, 5, codefragment); - Mark mark2 = createMark("public", "file2", 5, 5, codefragment); + Mark mark1 = createMark("public", "file1", 1, 5); + Mark mark2 = createMark("public", "file2", 5, 5); Match match1 = new Match(75, mark1, mark2); list.add(match1); @@ -266,22 +264,19 @@ class XMLRendererTest { assertTrue(report.contains("x=\"]]]]>\";")); } - private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) { - Mark result = new Mark(new TokenEntry(image, tokenSrcID, beginLine)); - - result.setLineCount(lineCount); - result.setSourceCode(new SourceCode(new SourceCode.StringCodeLoader(code))); - return result; + private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount) { + return new Mark(tokens.addToken(image, tokenSrcID, beginLine, 1, beginLine + lineCount, 1)); } private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code, int beginColumn, int endColumn) { - final TokenEntry beginToken = new TokenEntry(image, tokenSrcID, beginLine, beginColumn, beginColumn + 1); - final TokenEntry endToken = new TokenEntry(image, tokenSrcID, beginLine + lineCount, endColumn - 1, endColumn); + final TokenEntry beginToken = tokens.addToken(image, tokenSrcID, beginLine, beginColumn, beginLine, + beginColumn + image.length()); + final TokenEntry endToken = tokens.addToken(image, tokenSrcID, + beginLine + lineCount, beginColumn, + beginLine + lineCount, endColumn); final Mark result = new Mark(beginToken); - result.setLineCount(lineCount); result.setEndToken(endToken); - result.setSourceCode(new SourceCode(new SourceCode.StringCodeLoader(code))); return result; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index 3cf9f6ade0..9e680c83c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -4,19 +4,20 @@ package net.sourceforge.pmd.cpd; -import static net.sourceforge.pmd.util.CollectionUtil.mapOf; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import java.io.IOException; import java.util.Iterator; -import java.util.Map; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.java.JavaLanguageModule; class MatchAlgorithmTest { @@ -39,13 +40,15 @@ class MatchAlgorithmTest { void testSimple() throws IOException { Language java = JavaLanguageModule.getInstance(); Tokenizer tokenizer = java.createCpdTokenizer(java.newPropertyBundle()); - TextDocument sourceCode = TextDocument.readOnlyString(getSampleCode(), "Foo.java", java.getDefaultVersion()); + String fileName = "Foo.java"; + TextFile textFile = TextFile.forCharSeq(getSampleCode(), fileName, java.getDefaultVersion()); + SourceManager sourceManager = new SourceManager(listOf(textFile)); Tokens tokens = new Tokens(); + TextDocument sourceCode = sourceManager.get(textFile); tokenizer.tokenize(sourceCode, TokenFactory.forFile(sourceCode, tokens)); assertEquals(41, tokens.size()); - Map codeMap = mapOf(sourceCode.getPathId(), sourceCode); - MatchAlgorithm matchAlgorithm = new MatchAlgorithm(codeMap, tokens, 5); + MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, 5); matchAlgorithm.findMatches(); Iterator matches = matchAlgorithm.matches(); Match match = matches.next(); @@ -57,12 +60,12 @@ class MatchAlgorithmTest { assertFalse(marks.hasNext()); assertEquals(3, mark1.getBeginLine()); - assertEquals("Foo.java", mark1.getFilename()); - assertEquals(LINE_3, mark1.getSourceCodeSlice()); + assertEquals(fileName, mark1.getFilename()); + assertEquals(Chars.wrap(LINE_3), sourceManager.getSlice(mark1)); assertEquals(4, mark2.getBeginLine()); - assertEquals("Foo.java", mark2.getFilename()); - assertEquals(LINE_4, mark2.getSourceCodeSlice()); + assertEquals(fileName, mark2.getFilename()); + assertEquals(Chars.wrap(LINE_4), sourceManager.getSlice(mark2)); } @Test @@ -74,11 +77,9 @@ class MatchAlgorithmTest { Tokenizer tokenizer = java.createCpdTokenizer(bundle); TextDocument sourceCode = TextDocument.readOnlyString(getSampleCode(), "Foo.java", java.getDefaultVersion()); Tokens tokens = new Tokens(); - TokenEntry.clearImages(); tokenizer.tokenize(sourceCode, TokenFactory.forFile(sourceCode, tokens)); - Map codeMap = mapOf(sourceCode.getPathId(), sourceCode); - MatchAlgorithm matchAlgorithm = new MatchAlgorithm(codeMap, tokens, 5); + MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, 5); matchAlgorithm.findMatches(); Iterator matches = matchAlgorithm.matches(); Match match = matches.next(); From 0cab976fc505a28c6d96a1801b778a2041dcd018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 18:22:30 +0100 Subject: [PATCH 096/347] Remove SourceCode --- .../sourceforge/pmd/cpd/MatchAlgorithm.java | 8 +- .../net/sourceforge/pmd/cpd/SourceCode.java | 74 ------------------- .../pmd/lang/document/CpdCompat.java | 40 ---------- .../pmd/lang/document/TextDocument.java | 4 +- .../pmd/lang/document/TextFile.java | 3 +- .../pmd/util/database/SourceObject.java | 3 +- .../sourceforge/pmd/cpd/AnyTokenizerTest.java | 11 ++- .../sourceforge/pmd/cpd/SourceCodeTest.java | 52 ------------- .../pmd/cpd/test/CpdTextComparisonTest.kt | 30 +++----- 9 files changed, 24 insertions(+), 201 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/cpd/SourceCodeTest.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java index 93cf8184ef..593e0eb5c5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java @@ -22,11 +22,11 @@ class MatchAlgorithm { private CPDListener cpdListener; private final int min; - public MatchAlgorithm(Tokens tokens, int min) { - this(tokens, min, new CPDNullListener()); - } + MatchAlgorithm(Tokens tokens, int min) { + this(tokens, min, new CPDNullListener()); + } - public MatchAlgorithm(Tokens tokens, int min, CPDListener listener) { + MatchAlgorithm(Tokens tokens, int min, CPDListener listener) { this.tokens = tokens; this.code = tokens.getTokens(); this.min = min; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java deleted file mode 100644 index 34288552cd..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceCode.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.io.IOException; -import java.io.Reader; -import java.lang.ref.SoftReference; -import java.util.List; - -import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.lang.document.TextFile; - -public class SourceCode { - - private SoftReference softRef; - private final TextFile textFile; - - public SourceCode(TextFile textFile) { - this.textFile = textFile; - } - - public TextDocument load() throws IOException { - if (softRef != null && softRef.get() != null) { - return softRef.get(); - } - TextDocument doc = TextDocument.create(textFile); - softRef = new SoftReference<>(doc); - return doc; - } - - - public List getCode() { - return cl.getCode(); - } - - /** Newlines are normalized to \n. */ - public StringBuilder getCodeBuffer() { - StringBuilder sb = new StringBuilder(); - List lines = cl.getCode(); - for (String line : lines) { - sb.append(line).append('\n'); - } - return sb; - } - - /** - * Loads a range of lines. Newlines are normalized to \n - * - * @param startLine Start line (inclusive, 1-based) - * @param endLine End line (inclusive, 1-based) - */ - public String getSlice(int startLine, int endLine) { - List lines = cl.getCodeSlice(startLine, endLine); - - StringBuilder sb = new StringBuilder(); - for (String line : lines) { - if (sb.length() != 0) { - sb.append('\n'); - } - sb.append(line); - } - return sb.toString(); - } - - public String getFileName() { - return cl.getFileName(); - } - - public Reader getReader() throws Exception { - return cl.getReader(); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java deleted file mode 100644 index 9b0a19f12c..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/CpdCompat.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.document; - -import net.sourceforge.pmd.cpd.SourceCode; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.PlainTextLanguage; - -/** - * Compatibility APIs, to be removed before PMD 7 is out. - */ -@Deprecated -public final class CpdCompat { - - private CpdCompat() { - // utility class - } - - @Deprecated - public static LanguageVersion dummyVersion() { - return PlainTextLanguage.getInstance().getDefaultVersion(); - } - - /** - * Bridges {@link SourceCode} with {@link TextFile}. This allows - * javacc tokenizers to work on text documents. - * - * @deprecated This is only a transitional API for the PMD 7 branch - */ - @Deprecated - public static TextFile cpdCompat(SourceCode sourceCode) { - return TextFile.forCharSeq( - sourceCode.getCodeBuffer(), - sourceCode.getFileName(), - dummyVersion() - ); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java index 153f43e360..82cf9fb57c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextDocument.java @@ -10,7 +10,6 @@ import java.io.Reader; import org.checkerframework.checker.nullness.qual.NonNull; -import net.sourceforge.pmd.cpd.SourceCode; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.util.datasource.DataSource; @@ -20,7 +19,7 @@ import net.sourceforge.pmd.util.datasource.DataSource; * to a {@link TextFile}. It reflects some in-memory snapshot of the file, * though the file may still be edited externally. * - *

TextDocument is meant to replace CPD's {@link SourceCode} and PMD's + *

TextDocument is meant to replace CPD's SourceCode and PMD's * {@link DataSource}, though the abstraction level of {@link DataSource} * is the {@link TextFile}. * @@ -168,7 +167,6 @@ public interface TextDocument extends Closeable { /** * Returns a region that spans the text of all the given lines. - * This is intended to provide a replacement for {@link SourceCode#getSlice(int, int)}. * *

Note that, as line numbers may only be obtained from {@link #toLocation(TextRegion)}, * and hence are line numbers of the original source, both parameters diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java index a4c2bfacdf..be972a3d17 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFile.java @@ -19,7 +19,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.annotation.DeprecatedUntil700; -import net.sourceforge.pmd.cpd.SourceCode; import net.sourceforge.pmd.internal.util.BaseCloseable; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.LanguageVersion; @@ -38,7 +37,7 @@ import net.sourceforge.pmd.util.datasource.DataSource; * This interface only provides block IO operations, while {@link TextDocument} adds logic * about incremental edition (eg replacing a single region of text). * - *

This interface is meant to replace {@link DataSource} and {@link SourceCode.CodeLoader}. + *

This interface is meant to replace {@link DataSource} and SourceCode. * "DataSource" is not an appropriate name for a file which can be written * to, also, the "data" it provides is text, not bytes. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/database/SourceObject.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/database/SourceObject.java index 1a112c9ee1..1e2112c315 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/database/SourceObject.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/database/SourceObject.java @@ -9,11 +9,10 @@ import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sourceforge.pmd.cpd.SourceCode; import net.sourceforge.pmd.lang.Language; /** - * Instantiate the fields required to retrieve {@link SourceCode}. + * Instantiate the fields required to retrieve the source code. * * @author sturton */ diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java index ba9654f2c4..05013bc139 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java @@ -12,6 +12,9 @@ import java.util.List; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.document.TextDocument; + class AnyTokenizerTest { @Test @@ -49,9 +52,9 @@ class AnyTokenizerTest { @Test void testTokenPosition() { AnyTokenizer tokenizer = new AnyTokenizer(); - SourceCode code = new SourceCode(new SourceCode.StringCodeLoader("a;\nbbbb\n;")); + TextDocument code = TextDocument.readOnlyString("a;\nbbbb\n;", "Foo.dummy", DummyLanguageModule.getInstance().getDefaultVersion()); Tokens tokens = new Tokens(); - tokenizer.tokenize(code, tokens); + tokenizer.tokenize(code, TokenFactory.forFile(code, tokens)); TokenEntry bbbbToken = tokens.getTokens().get(2); assertEquals(2, bbbbToken.getBeginLine()); assertEquals(1, bbbbToken.getBeginColumn()); @@ -60,9 +63,9 @@ class AnyTokenizerTest { private Tokens compareResult(AnyTokenizer tokenizer, String source, List expectedImages) { - SourceCode code = new SourceCode(new SourceCode.StringCodeLoader(source)); + TextDocument code = TextDocument.readOnlyString(source, "Foo.dummy", DummyLanguageModule.getInstance().getDefaultVersion()); Tokens tokens = new Tokens(); - tokenizer.tokenize(code, tokens); + tokenizer.tokenize(code, TokenFactory.forFile(code, tokens)); List tokenStrings = new ArrayList<>(); for (TokenEntry token : tokens.getTokens()) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/SourceCodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/SourceCodeTest.java deleted file mode 100644 index 714acaac7c..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/SourceCodeTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.File; - -import org.junit.jupiter.api.Test; - -import net.sourceforge.pmd.cpd.SourceCode.FileCodeLoader; - -class SourceCodeTest { - private static final String BASE_RESOURCE_PATH = "src/test/resources/net/sourceforge/pmd/cpd/files/"; - - private static final String SAMPLE_CODE = "Line 1\n" + "Line 2\n" + "Line 3\n" + "Line 4\n"; - - @Test - void testSlice() { - SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader(SAMPLE_CODE, "Foo.java")); - assertEquals("Foo.java", sourceCode.getFileName()); - - assertEquals("Line 1", sourceCode.getSlice(1, 1)); - assertEquals("Line 2", sourceCode.getSlice(2, 2)); - assertEquals("Line 1\nLine 2", sourceCode.getSlice(1, 2)); - - sourceCode.getCodeBuffer(); // load into soft reference, must not change behavior - assertEquals("Line 1\nLine 2", sourceCode.getSlice(1, 2)); - } - - @Test - void testEncodingDetectionFromBOM() throws Exception { - FileCodeLoader loader = new SourceCode.FileCodeLoader(new File(BASE_RESOURCE_PATH + "file_with_utf8_bom.java"), - "ISO-8859-1"); - - // The encoding detection is done when the reader is created - loader.getReader(); - assertEquals("UTF-8", loader.getEncoding()); - } - - @Test - void testEncodingIsNotChangedWhenThereIsNoBOM() throws Exception { - FileCodeLoader loader = new SourceCode.FileCodeLoader( - new File(BASE_RESOURCE_PATH + "file_with_ISO-8859-1_encoding.java"), "ISO-8859-1"); - - // The encoding detection is done when the reader is created - loader.getReader(); - assertEquals("ISO-8859-1", loader.getEncoding()); - } -} diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index 611caa3781..c3524a27a2 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -5,10 +5,7 @@ package net.sourceforge.pmd.cpd.test import io.kotest.assertions.throwables.shouldThrow -import net.sourceforge.pmd.cpd.SourceCode -import net.sourceforge.pmd.cpd.TokenEntry -import net.sourceforge.pmd.cpd.Tokenizer -import net.sourceforge.pmd.cpd.Tokens +import net.sourceforge.pmd.cpd.* import net.sourceforge.pmd.lang.Language import net.sourceforge.pmd.lang.LanguagePropertyBundle import net.sourceforge.pmd.lang.ast.TokenMgrError @@ -63,13 +60,8 @@ abstract class CpdTextComparisonTest( expectedSuffix: String = "", config: LanguagePropertyConfig = defaultProperties() ) { - super.doTest(fileBaseName, expectedSuffix) { fileData -> - val sourceCode = TextDocument.readOnlyString(fileBaseName, fileBaseName, language.defaultVersion) - val tokens = Tokens().also { - val tokenizer = newTokenizer(config) - tokenizer.tokenize(sourceCode, it) - } - + super.doTest(fileBaseName, expectedSuffix) { fdata -> + val tokens = tokenize(newTokenizer(config), fdata) buildString { format(tokens) } } } @@ -77,7 +69,7 @@ abstract class CpdTextComparisonTest( @JvmOverloads fun expectTokenMgrError( source: String, - fileName: String = SourceCode.StringCodeLoader.DEFAULT_NAME, + fileName: String = TextFile.UNKNOWN_FILENAME, properties: LanguagePropertyConfig = defaultProperties() ): TokenMgrError = expectTokenMgrError(FileData(fileName, source), properties) @@ -88,8 +80,7 @@ abstract class CpdTextComparisonTest( config: LanguagePropertyConfig = defaultProperties() ): TokenMgrError = shouldThrow { - val tokenizer = newTokenizer(config) - tokenizer.tokenize(sourceCodeOf(fileData), Tokens()) + tokenize(newTokenizer(config), fileData) } @@ -168,15 +159,14 @@ abstract class CpdTextComparisonTest( } - fun sourceCodeOf(str: String): TextDocument = - sourceCodeOf(FileData(fileName = TextFile.UNKNOWN_FILENAME, fileText = str)) - - fun sourceCodeOf(fileData: FileData): TextDocument = + private fun sourceCodeOf(fileData: FileData): TextDocument = TextDocument.readOnlyString(fileData.fileText, fileData.fileName, language.defaultVersion) - fun tokenize(tokenizer: Tokenizer, str: String): Tokens = + fun tokenize(tokenizer: Tokenizer, fileData: FileData): Tokens = Tokens().also { - tokenizer.tokenize(sourceCodeOf(str), it) + val tokens = Tokens() + val source = sourceCodeOf(fileData) + tokenizer.tokenize(source, TokenFactory.forFile(source, tokens)) } private companion object { From e7503d9d9869cb48f04df321bd1a9330c2c8d12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 18:33:21 +0100 Subject: [PATCH 097/347] Fix CPDReport --- .../sourceforge/pmd/cpd/CPDReportTest.java | 55 +++++++++++++------ .../sourceforge/pmd/cpd/CSVRendererTest.java | 22 +++----- .../sourceforge/pmd/cpd/XMLRendererTest.java | 18 +++--- 3 files changed, 55 insertions(+), 40 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java index 744bdfdd18..1502d5f991 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java @@ -7,7 +7,9 @@ package net.sourceforge.pmd.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -16,37 +18,37 @@ import java.util.Set; import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.util.Predicate; +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.document.TextFile; class CPDReportTest { + private Tokens tokens = new Tokens(); + @Test void testFilterMatches() { List originalMatches = Arrays.asList( - createMatch("file1.java", "file2.java", 1), - createMatch("file1.java", "file3.java", 2), - createMatch("file2.java", "file3.java", 3)); + createMatch("file1.java", "file2.java", 1), + createMatch("file1.java", "file3.java", 2), + createMatch("file2.java", "file3.java", 3)); Map numberOfTokensPerFile = new HashMap<>(); numberOfTokensPerFile.put("file1.java", 10); numberOfTokensPerFile.put("file2.java", 15); numberOfTokensPerFile.put("file3.java", 20); - CPDReport original = new CPDReport(originalMatches, numberOfTokensPerFile); + CPDReport original = makeReport(originalMatches, numberOfTokensPerFile); assertEquals(3, original.getMatches().size()); CPDReport filtered = original.filterMatches( - new Predicate() { - @Override - public boolean test(Match match) { - // only keep file1.java - for (Mark mark : match.getMarkSet()) { - if (mark.getFilename().equals("file1.java")) { - return true; - } - } - return false; + match -> { + // only keep file1.java + for (Mark mark : match.getMarkSet()) { + if (mark.getFilename().equals("file1.java")) { + return true; } - }); + } + return false; + }); assertEquals(2, filtered.getMatches().size()); for (Match match : filtered.getMatches()) { Set filenames = new HashSet<>(); @@ -62,7 +64,24 @@ class CPDReportTest { private Match createMatch(String file1, String file2, int line) { return new Match(5, - new TokenEntry("firstToken", file1, 1, 1, 1), - new TokenEntry("secondToken", file2, 1, 2, 2)); + tokens.addToken("firstToken", file1, line, 1, line, 1), + tokens.addToken("secondToken", file2, line, 2, line, 2)); + } + + static CPDReport makeReport(List matches) { + return makeReport(matches, Collections.emptyMap()); + } + + static CPDReport makeReport(List matches, Map numTokensPerFile) { + Set textFiles = new HashSet<>(); + for (Match match : matches) { + match.iterator().forEachRemaining( + mark -> textFiles.add(TextFile.forCharSeq("dummy content", mark.getFilename(), DummyLanguageModule.getInstance().getDefaultVersion()))); + } + return new CPDReport( + new SourceManager(new ArrayList<>(textFiles)), + matches, + numTokensPerFile + ); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java index 8915a95624..818fb9bdb6 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java @@ -9,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; @@ -23,14 +22,13 @@ class CSVRendererTest { void testLineCountPerFile() throws IOException { CPDReportRenderer renderer = new CSVRenderer(true); List list = new ArrayList<>(); - String codeFragment = "code\nfragment"; - Mark mark1 = createMark("public", "/var/Foo.java", 48, 10, codeFragment); - Mark mark2 = createMark("stuff", "/var/Bar.java", 73, 20, codeFragment); + Mark mark1 = createMark("public", "/var/Foo.java", 48); + Mark mark2 = createMark("stuff", "/var/Bar.java", 73); Match match = new Match(75, mark1, mark2); list.add(match); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(list, Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(list), sw); String report = sw.toString(); String expectedReport = "tokens,occurrences" + PMD.EOL + "75,2,48,10,/var/Foo.java,73,20,/var/Bar.java" + PMD.EOL; @@ -42,23 +40,21 @@ class CSVRendererTest { void testFilenameEscapes() throws IOException { CPDReportRenderer renderer = new CSVRenderer(); List list = new ArrayList<>(); - String codeFragment = "code\nfragment"; - Mark mark1 = createMark("public", "/var,with,commas/Foo.java", 48, 10, codeFragment); - Mark mark2 = createMark("stuff", "/var,with,commas/Bar.java", 73, 20, codeFragment); + Mark mark1 = createMark("public", "/var,with,commas/Foo.java", 48); + Mark mark2 = createMark("stuff", "/var,with,commas/Bar.java", 73); Match match = new Match(75, mark1, mark2); list.add(match); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(list, Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(list), sw); String report = sw.toString(); String expectedReport = "lines,tokens,occurrences" + PMD.EOL + "10,75,2,48,\"/var,with,commas/Foo.java\",73,\"/var,with,commas/Bar.java\"" + PMD.EOL; assertEquals(expectedReport, report); } - private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) { - tokens.addToken(image, tokenSrcID, beginLine, beginLine, beginLine, beginLine); - - return new Mark(tokens.peekLastToken()); + private Mark createMark(String image, String tokenSrcID, int beginLine) { + TokenEntry tok = tokens.addToken(image, tokenSrcID, beginLine, beginLine, beginLine, beginLine); + return new Mark(tok); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index 7f15face57..1dacc25ca9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -45,11 +45,11 @@ class XMLRendererTest { void testWithNoDuplication() throws IOException, ParserConfigurationException, SAXException { CPDReportRenderer renderer = new XMLRenderer(); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(Collections.emptyList(), Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(Collections.emptyList()), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); + .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); NodeList nodes = doc.getChildNodes(); Node n = nodes.item(0); assertEquals("pmd-cpd", n.getNodeName()); @@ -68,7 +68,7 @@ class XMLRendererTest { list.add(match); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(list, Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(list), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() @@ -117,7 +117,7 @@ class XMLRendererTest { list.add(match1); list.add(match2); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(list, Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(list), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() @@ -138,7 +138,7 @@ class XMLRendererTest { list.add(match); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(list, Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(list), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() @@ -181,7 +181,7 @@ class XMLRendererTest { list.add(match1); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(list, Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(list), sw); String report = sw.toString(); assertTrue(report.contains(espaceChar)); assertFalse(report.contains(FORM_FEED)); @@ -201,7 +201,7 @@ class XMLRendererTest { matches.add(match); final Map numberOfTokensPerFile = new HashMap<>(); numberOfTokensPerFile.put(filename, 888); - final CPDReport report = new CPDReport(matches, numberOfTokensPerFile); + final CPDReport report = CPDReportTest.makeReport(matches, numberOfTokensPerFile); final StringWriter writer = new StringWriter(); renderer.render(report, writer); final String xmlOutput = writer.toString(); @@ -227,7 +227,7 @@ class XMLRendererTest { matches.add(match); final Map numberOfTokensPerFile = new HashMap<>(); numberOfTokensPerFile.put(filename, 888); - final CPDReport report = new CPDReport(matches, numberOfTokensPerFile); + final CPDReport report = CPDReportTest.makeReport(matches, numberOfTokensPerFile); final StringWriter writer = new StringWriter(); renderer.render(report, writer); final String xmlOutput = writer.toString(); @@ -255,7 +255,7 @@ class XMLRendererTest { list.add(match1); StringWriter sw = new StringWriter(); - renderer.render(new CPDReport(list, Collections.emptyMap()), sw); + renderer.render(CPDReportTest.makeReport(list), sw); String report = sw.toString(); assertFalse(report.contains(FORM_FEED)); assertFalse(report.contains(FORM_FEED_ENTITY)); From f2cfd8f5a6688367deb451ef8a921853fe50ba77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 18:43:37 +0100 Subject: [PATCH 098/347] More refactorings --- .../pmd/cli/commands/internal/CpdCommand.java | 12 +--- .../sourceforge/pmd/cpd/CPDConfiguration.java | 2 +- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 21 +++++- .../pmd/lang/PlainTextLanguage.java | 7 ++ .../{CPDTest.java => CpdAnalysisTest.java} | 64 +++++++++++-------- 5 files changed, 68 insertions(+), 38 deletions(-) rename pmd-core/src/test/java/net/sourceforge/pmd/cpd/{CPDTest.java => CpdAnalysisTest.java} (69%) diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index af9de892b1..46f1a8335c 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.cli.commands.internal; import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; import java.nio.file.Path; import java.util.Arrays; import java.util.Iterator; @@ -23,7 +22,6 @@ import net.sourceforge.pmd.cpd.CPDConfiguration; import net.sourceforge.pmd.cpd.CpdAnalysis; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.internal.LogMessages; -import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.Language; import picocli.CommandLine.Command; @@ -135,15 +133,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { try (CpdAnalysis cpd = new CpdAnalysis(configuration)){ MutableBoolean hasViolations = new MutableBoolean(); - cpd.performAnalysis(report -> { - try { - configuration.getCPDReportRenderer().render(report, IOUtil.createWriter(Charset.defaultCharset(), null)); - hasViolations.setValue(!report.getMatches().isEmpty()); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - + cpd.performAnalysis(report -> hasViolations.setValue(!report.getMatches().isEmpty())); if (hasViolations.booleanValue() && configuration.isFailOnViolation()) { return CliExitCode.VIOLATIONS_FOUND; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 30d8b2a9df..576d460c4c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -51,7 +51,7 @@ public class CPDConfiguration extends AbstractConfiguration { private String rendererName; - private CPDReportRenderer cpdReportRenderer; + CPDReportRenderer cpdReportRenderer; private boolean ignoreLiterals; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 720b8fbbc6..4d5c4eeccd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cpd; import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -15,8 +16,10 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.internal.util.FileCollectionUtil; import net.sourceforge.pmd.internal.util.FileUtil; +import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.ast.TokenMgrError; @@ -43,6 +46,14 @@ public final class CpdAnalysis implements AutoCloseable { reporter ); + if (config.getRendererName() == null) { + config.setRendererName(CPDConfiguration.DEFAULT_RENDERER); + } + if (config.cpdReportRenderer == null) { + //may throw + CPDReportRenderer renderer = CPDConfiguration.createRendererByName(config.getRendererName(), config.getSourceEncoding().name()); + config.setRenderer(renderer); + } // Add all sources extractAllSources(); @@ -118,6 +129,10 @@ public final class CpdAnalysis implements AutoCloseable { return tokens.size() - lastTokenSize - 1; /* EOF */ } + public void performAnalysis() { + performAnalysis(r -> { }); + } + public void performAnalysis(Consumer consumer) { try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) { @@ -149,8 +164,12 @@ public final class CpdAnalysis implements AutoCloseable { LOGGER.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); CPDReport cpdReport = new CPDReport(sourceManager, matchAlgorithm.getMatches(), numberOfTokensPerFile); - consumer.accept(cpdReport); + if (configuration.getCPDReportRenderer() != null) { + configuration.getCPDReportRenderer().render(cpdReport, IOUtil.createWriter(Charset.defaultCharset(), null)); + } + + consumer.accept(cpdReport); } catch (Exception e) { reporter.errorEx("Exception while running CPD", e); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java index 29afc7d45d..07945c453a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.lang; import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.cpd.AnyTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; @@ -42,6 +44,11 @@ public final class PlainTextLanguage extends SimpleLanguageModuleBase { return INSTANCE; } + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer(); + } + private static final class TextLvh implements LanguageVersionHandler { @Override public Parser getParser() { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java similarity index 69% rename from pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDTest.java rename to pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java index 8ac89bcf74..4ee0b61541 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java @@ -10,33 +10,34 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.io.File; -import java.util.Iterator; +import java.nio.file.Paths; +import java.util.List; import org.apache.commons.lang3.SystemUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.PlainTextLanguage; + /** - * Unit test for {@link CPD} + * Unit test for {@link CpdAnalysis} */ -class CPDTest { +class CpdAnalysisTest { private static final String BASE_TEST_RESOURCE_PATH = "src/test/resources/net/sourceforge/pmd/cpd/files/"; private static final String TARGET_TEST_RESOURCE_PATH = "target/classes/net/sourceforge/pmd/cpd/files/"; - private CPD cpd; // Symlinks are not well supported under Windows - so the tests are // simply executed only on linux. private boolean canTestSymLinks = SystemUtils.IS_OS_UNIX; + CPDConfiguration config = new CPDConfiguration(); @BeforeEach void setup() throws Exception { - CPDConfiguration theConfiguration = new CPDConfiguration(); - theConfiguration.setLanguage(new AnyLanguage("any")); - theConfiguration.setMinimumTileSize(10); - theConfiguration.postContruct(); - cpd = new CPD(theConfiguration); + config.setLanguage(PlainTextLanguage.getInstance()); + config.setMinimumTileSize(10); + config.postContruct(); } /** @@ -72,9 +73,12 @@ class CPDTest { prepareSymLinks(); NoFileAssertListener listener = new NoFileAssertListener(0); - cpd.setCpdListener(listener); + try (CpdAnalysis cpd = new CpdAnalysis(config)) { + cpd.setCpdListener(listener); + cpd.files().addFile(Paths.get(BASE_TEST_RESOURCE_PATH, "this-is-a-broken-sym-link-for-test")); + cpd.performAnalysis(); + } - cpd.add(new File(BASE_TEST_RESOURCE_PATH, "this-is-a-broken-sym-link-for-test")); listener.verify(); } @@ -90,10 +94,13 @@ class CPDTest { prepareSymLinks(); NoFileAssertListener listener = new NoFileAssertListener(1); - cpd.setCpdListener(listener); + try (CpdAnalysis cpd = new CpdAnalysis(config)) { + cpd.setCpdListener(listener); + cpd.files().addFile(Paths.get(BASE_TEST_RESOURCE_PATH, "real-file.txt")); + cpd.files().addFile(Paths.get(BASE_TEST_RESOURCE_PATH, "symlink-for-real-file.txt")); + cpd.performAnalysis(); + } - cpd.add(new File(BASE_TEST_RESOURCE_PATH, "real-file.txt")); - cpd.add(new File(BASE_TEST_RESOURCE_PATH, "symlink-for-real-file.txt")); listener.verify(); } @@ -107,9 +114,12 @@ class CPDTest { @Test void testFileAddedWithRelativePath() throws Exception { NoFileAssertListener listener = new NoFileAssertListener(1); - cpd.setCpdListener(listener); + try (CpdAnalysis cpd = new CpdAnalysis(config)) { + cpd.setCpdListener(listener); + cpd.files().addFile(Paths.get("./" + BASE_TEST_RESOURCE_PATH, "real-file.txt")); + cpd.performAnalysis(); + } - cpd.add(new File("./" + BASE_TEST_RESOURCE_PATH, "real-file.txt")); listener.verify(); } @@ -120,17 +130,21 @@ class CPDTest { */ @Test void testFileOrderRelevance() throws Exception { - cpd.add(new File("./" + BASE_TEST_RESOURCE_PATH, "dup2.java")); - cpd.add(new File("./" + BASE_TEST_RESOURCE_PATH, "dup1.java")); - cpd.go(); + try (CpdAnalysis cpd = new CpdAnalysis(config)) { + cpd.files().addFile(Paths.get("./" + BASE_TEST_RESOURCE_PATH, "dup2.java")); + cpd.files().addFile(Paths.get("./" + BASE_TEST_RESOURCE_PATH, "dup1.java")); + cpd.performAnalysis(report -> { - Iterator matches = cpd.getMatches(); - while (matches.hasNext()) { - Match match = matches.next(); - // the file added first was dup2. - assertTrue(match.getFirstMark().getFilename().endsWith("dup2.java")); - assertTrue(match.getSecondMark().getFilename().endsWith("dup1.java")); + + List matches = report.getMatches(); + for (Match match : matches) { + // the file added first was dup2. + assertTrue(match.getFirstMark().getFilename().endsWith("dup2.java")); + assertTrue(match.getSecondMark().getFilename().endsWith("dup1.java")); + } + }); } + } /** From d972e4aabe81fa0436a283df04bc4e9282f4c5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 18:46:58 +0100 Subject: [PATCH 099/347] Fix scala module --- .../sourceforge/pmd/cpd/ScalaLanguage.java | 18 ------------ .../sourceforge/pmd/cpd/ScalaTokenizer.java | 28 ++----------------- .../pmd/lang/scala/ScalaLanguageModule.java | 8 ++++++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../pmd/cpd/ScalaTokenizerTest.java | 3 +- 5 files changed, 13 insertions(+), 45 deletions(-) delete mode 100644 pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaLanguage.java delete mode 100644 pmd-scala-modules/pmd-scala-common/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaLanguage.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaLanguage.java deleted file mode 100644 index fd1f1b6da9..0000000000 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaLanguage.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for Scala. - */ -public class ScalaLanguage extends AbstractLanguage { - - /** - * Creates a new Scala Language instance. - */ - public ScalaLanguage() { - super("Scala", "scala", new ScalaTokenizer(), ".scala"); - } -} diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 0fca0e0d6a..227988325e 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -4,11 +4,10 @@ package net.sourceforge.pmd.cpd; -import java.util.Properties; - import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.TokenMgrError; @@ -28,34 +27,13 @@ import scala.meta.tokens.Token; */ public class ScalaTokenizer implements Tokenizer { - /** - * Denotes the version of the scala dialect to use. Based on the values in - * {@linkplain ScalaLanguageModule#getVersions()} - */ - public static final String SCALA_VERSION_PROPERTY = "net.sourceforge.pmd.scala.version"; private final Dialect dialect; /** * Create the Tokenizer using properties from the system environment. */ - public ScalaTokenizer() { - this(System.getProperties()); - } - - /** - * Create the Tokenizer given a set of properties. - * - * @param properties - * the {@linkplain Properties} object to use - */ - public ScalaTokenizer(Properties properties) { - String scalaVersion = properties.getProperty(SCALA_VERSION_PROPERTY); - LanguageVersion langVer; - if (scalaVersion == null) { - langVer = ScalaLanguageModule.getInstance().getDefaultVersion(); - } else { - langVer = ScalaLanguageModule.getInstance().getVersion(scalaVersion); - } + public ScalaTokenizer(LanguagePropertyBundle bundle) { + LanguageVersion langVer = bundle.getLanguageVersion(); dialect = ScalaLanguageModule.dialectOf(langVer); } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java index 1825eec5c6..b76fa580a9 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageModule.java @@ -7,6 +7,9 @@ package net.sourceforge.pmd.lang.scala; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.cpd.ScalaTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; @@ -48,6 +51,11 @@ public class ScalaLanguageModule extends SimpleLanguageModuleBase { } } + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new ScalaTokenizer(bundle); + } + public static ScalaLanguageModule getInstance() { return (ScalaLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME); } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-scala-modules/pmd-scala-common/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index d1e82718f5..0000000000 --- a/pmd-scala-modules/pmd-scala-common/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.ScalaLanguage diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java index 8bda7ffea0..fc48791566 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java @@ -10,11 +10,12 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; import net.sourceforge.pmd.lang.ast.TokenMgrError; +import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; class ScalaTokenizerTest extends CpdTextComparisonTest { ScalaTokenizerTest() { - super(".scala"); + super(ScalaLanguageModule.getInstance(), ".scala"); } @Override From 9e9de580a2fb953e56ed94b5a223e2f592052e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 19:13:17 +0100 Subject: [PATCH 100/347] Delete CPD --- .../java/net/sourceforge/pmd/cpd/CPD.java | 270 ------------------ .../java/net/sourceforge/pmd/cpd/GUI.java | 263 +++++++---------- .../pmd/lang/CpdOnlyLanguageModuleBase.java | 5 + .../pmd/lang/LanguageModuleBase.java | 2 +- .../sourceforge/pmd/cpd/CPDFilelistTest.java | 25 +- 5 files changed, 125 insertions(+), 440 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java deleted file mode 100644 index 64a6554628..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ /dev/null @@ -1,270 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.sourceforge.pmd.cpd.Tokens.State; -import net.sourceforge.pmd.internal.util.FileFinder; -import net.sourceforge.pmd.internal.util.FileUtil; -import net.sourceforge.pmd.internal.util.IOUtil; -import net.sourceforge.pmd.lang.ast.TokenMgrError; -import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.util.database.DBMSMetadata; -import net.sourceforge.pmd.util.database.DBURI; -import net.sourceforge.pmd.util.database.SourceObject; - -/** - * @deprecated Use the module pmd-cli for CLI support. - */ -@Deprecated -public class CPD { - // not final, in order to re-initialize logging - private static Logger log = LoggerFactory.getLogger(CPD.class); - - private CPDConfiguration configuration; - - private CPDListener listener = new CPDNullListener(); - private Tokens tokens = new Tokens(); - private MatchAlgorithm matchAlgorithm; - private Set current = new HashSet<>(); - private final Map numberOfTokensPerFile = new HashMap<>(); - private int lastTokenSize = 0; - - public CPD(CPDConfiguration theConfiguration) { - configuration = theConfiguration; - - // Add all sources - extractAllSources(); - } - - private void extractAllSources() { - // Add files - if (null != configuration.getFiles() && !configuration.getFiles().isEmpty()) { - addSourcesFilesToCPD(configuration.getFiles()); - } - - // Add Database URIS - if (null != configuration.getURI() && !"".equals(configuration.getURI())) { - addSourceURIToCPD(configuration.getURI()); - } - - if (null != configuration.getFileListPath() && !"".equals(configuration.getFileListPath())) { - addFilesFromFilelist(configuration.getFileListPath()); - } - } - - private void addSourcesFilesToCPD(List files) { - try { - for (File file : files) { - if (!file.exists()) { - throw new FileNotFoundException("Could not find directory/file '" + file + "'"); - } else if (file.isDirectory()) { - if (configuration.isNonRecursive()) { - addAllInDirectory(file); - } else { - addRecursively(file); - } - } else { - add(file); - } - } - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - private void addFilesFromFilelist(String inputFilePath) { - List files = new ArrayList<>(); - try { - Path file = FileUtil.toExistingPath(inputFilePath); - for (Path fileToAdd : FileUtil.readFilelistEntries(file)) { - if (!Files.exists(fileToAdd)) { - throw new RuntimeException("No such file " + fileToAdd); - } - files.add(fileToAdd.toFile()); - } - addSourcesFilesToCPD(files); - } catch (IOException ex) { - throw new IllegalStateException(ex); - } - } - - private void addSourceURIToCPD(String uri) { - try { - log.debug("Attempting DBURI={}", uri); - DBURI dburi = new DBURI(uri); - log.debug("Initialised DBURI={}", dburi); - log.debug("Adding DBURI={} with DBType={}", dburi, dburi.getDbType()); - add(dburi); - } catch (IOException | URISyntaxException e) { - throw new IllegalStateException("uri=" + uri, e); - } - } - - public void setCpdListener(CPDListener cpdListener) { - this.listener = cpdListener; - } - - public void go() { - log.debug("Running match algorithm on {} files...", sourceManager.size()); - matchAlgorithm = new MatchAlgorithm(tokens, configuration.getMinimumTileSize(), listener); - matchAlgorithm.findMatches(); - log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); - } - - /** - * @deprecated Use {@link #toReport()}. - */ - @Deprecated - public Iterator getMatches() { - return matchAlgorithm.matches(); - } - - public void addAllInDirectory(File dir) throws IOException { - addDirectory(dir, false); - } - - public void addRecursively(File dir) throws IOException { - addDirectory(dir, true); - } - - public void add(List files) throws IOException { - for (File f : files) { - add(f); - } - } - - private void addDirectory(File dir, boolean recurse) throws IOException { - if (!dir.exists()) { - throw new FileNotFoundException("Couldn't find directory " + dir); - } - log.debug("Searching directory " + dir + " for files"); - FileFinder finder = new FileFinder(); - // TODO - could use SourceFileSelector here - add(finder.findFilesFrom(dir, configuration.filenameFilter(), recurse)); - } - - public void add(File file) throws IOException { - - if (configuration.isSkipDuplicates()) { - // TODO refactor this thing into a separate class - String signature = file.getName() + '_' + file.length(); - if (current.contains(signature)) { - System.err.println("Skipping " + file.getAbsolutePath() - + " since it appears to be a duplicate file and --skip-duplicate-files is set"); - return; - } - current.add(signature); - } - - if (!IOUtil.equalsNormalizedPaths(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) { - System.err.println("Skipping " + file + " since it appears to be a symlink"); - return; - } - - if (!file.exists()) { - System.err.println("Skipping " + file + " since it doesn't exist (broken symlink?)"); - return; - } - - SourceCode sourceCode = configuration.sourceCodeFor(file); - add(sourceCode); - } - - public void add(DBURI dburi) { - - try { - DBMSMetadata dbmsmetadata = new DBMSMetadata(dburi); - - List sourceObjectList = dbmsmetadata.getSourceObjectList(); - log.debug("Located {} database source objects", sourceObjectList.size()); - - for (SourceObject sourceObject : sourceObjectList) { - // Add DBURI as a faux-file - String falseFilePath = sourceObject.getPseudoFileName(); - log.trace("Adding database source object {}", falseFilePath); - - SourceCode sourceCode = configuration.sourceCodeFor(dbmsmetadata.getSourceCode(sourceObject), - falseFilePath); - add(sourceCode); - } - } catch (Exception sqlException) { - log.error("Problem with Input URI", sqlException); - throw new RuntimeException("Problem with DBURI: " + dburi, sqlException); - } - } - - private void add(SourceCode sourceCode) throws IOException { - if (configuration.isSkipLexicalErrors()) { - addAndSkipLexicalErrors(sourceCode); - } else { - addAndThrowLexicalError(sourceCode); - } - } - - private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException { - log.debug("Tokenizing {}", sourceCode.getPathId()); - try (TextDocument doc = sourceCode.load()) { - configuration.tokenizer().tokenize(doc, tokens); - } - listener.addedFile(1); - source.put(sourceCode.getPathId(), sourceCode); - numberOfTokensPerFile.put(sourceCode.getPathId(), tokens.size() - lastTokenSize - 1 /*EOF*/); - lastTokenSize = tokens.size(); - } - - private void addAndSkipLexicalErrors(SourceCode sourceCode) throws IOException { - final Tokens.State savedState = new State(); - try { - addAndThrowLexicalError(sourceCode); - } catch (TokenMgrError e) { - System.err.println("Skipping " + sourceCode.getDisplayName() + ". Reason: " + e.getMessage()); - savedState.restore(tokens); - } - } - - /** - * List names/paths of each source to be processed. - * - * @return names of sources to be processed - */ - public List getSourcePaths() { - return new ArrayList<>(source.keySet()); - } - - /** - * Entry to invoke CPD as command line tool. Note that this will - * invoke {@link System#exit(int)}. - * - * @param args command line arguments - * - * @deprecated Use module pmd-cli -- to be removed before 7.0.0 is out. - */ - @Deprecated - public static void main(String[] args) { - throw new UnsupportedOperationException("Use the pmd-cli module."); - } - - public CPDReport toReport() { - return new CPDReport(matchAlgorithm.getMatches(), numberOfTokensPerFile); - } - -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index ca694ad209..fc99bc3373 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -26,7 +26,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; @@ -55,8 +54,6 @@ import javax.swing.KeyStroke; import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; import javax.swing.Timer; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; @@ -65,7 +62,12 @@ import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; +import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.LanguageRegistry; public class GUI implements CPDListener { @@ -76,116 +78,80 @@ public class GUI implements CPDListener { { "CSV (tab)", new CSVRenderer('\t'), }, }; private abstract static class LanguageConfig { - public abstract Language languageFor(Properties p); + + public abstract String getLabel(); + + public abstract Language getLanguage(); public boolean canIgnoreIdentifiers() { - return false; + return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); } public boolean canIgnoreLiterals() { - return false; + return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_ANONYMiZE_LITERALS); } public boolean canIgnoreAnnotations() { - return false; + return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_IGNORE_METADATA); } public boolean canIgnoreUsings() { - return false; + return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_IGNORE_IMPORTS); } public boolean canIgnoreLiteralSequences() { - return false; + return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES); } - public abstract String[] extensions(); } - private static final Object[][] LANGUAGE_SETS; + private static final List LANGUAGE_SETS; + + + public static final String CUSTOM_EXTENSION_SENTINEL = "custom"; + static { - LANGUAGE_SETS = new Object[LanguageFactory.supportedLanguages.length + 1][2]; + LANGUAGE_SETS = new ArrayList<>(); - int index; - for (index = 0; index < LanguageFactory.supportedLanguages.length; index++) { - final String terseName = LanguageFactory.supportedLanguages[index]; - final Language lang = LanguageFactory.createLanguage(terseName); - LANGUAGE_SETS[index][0] = lang.getName(); - LANGUAGE_SETS[index][1] = new LanguageConfig() { + for (Language lang : LanguageRegistry.CPD) { + LanguageConfig config = new LanguageConfig() { @Override - public Language languageFor(Properties p) { - lang.setProperties(p); + public String getLabel() { + return lang.getName(); + } + + @Override + public Language getLanguage() { return lang; } - - @Override - public String[] extensions() { - List exts = lang.getExtensions(); - return exts.toArray(new String[0]); - } - - @Override - public boolean canIgnoreAnnotations() { - if (terseName == null) { - return false; - } - switch (terseName) { - case "cs": - case "java": - return true; - default: - return false; - } - } - - @Override - public boolean canIgnoreIdentifiers() { - return "java".equals(terseName); - } - - @Override - public boolean canIgnoreLiterals() { - return "java".equals(terseName); - } - - @Override - public boolean canIgnoreUsings() { - return "cs".equals(terseName); - } - - @Override - public boolean canIgnoreLiteralSequences() { - if (terseName == null) { - return false; - } - switch (terseName) { - case "cpp": - case "cs": - return true; - default: - return false; - } - } }; + LANGUAGE_SETS.add(config); } - LANGUAGE_SETS[index][0] = "by extension..."; - LANGUAGE_SETS[index][1] = new LanguageConfig() { + LanguageConfig last = new LanguageConfig() { @Override - public Language languageFor(Properties p) { - return LanguageFactory.createLanguage(LanguageFactory.BY_EXTENSION, p); + public String getLabel() { + return "By extension..."; } @Override - public String[] extensions() { - return new String[] { "" }; + public Language getLanguage() { + return new CpdOnlyLanguageModuleBase(LanguageMetadata.withId("custom_extension").extensions(CUSTOM_EXTENSION_SENTINEL).name("By extension...")) { + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer(); + } + }; } }; + LANGUAGE_SETS.add(last); } + private static final int DEFAULT_CPD_MINIMUM_LENGTH = 75; - private static final Map LANGUAGE_CONFIGS_BY_LABEL = new HashMap<>(LANGUAGE_SETS.length); + private static final Map LANGUAGE_CONFIGS_BY_LABEL = new HashMap<>(LANGUAGE_SETS.size()); private static final KeyStroke COPY_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK, - false); + false); private static final KeyStroke DELETE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0); private class ColumnSpec { @@ -224,8 +190,8 @@ public class GUI implements CPDListener { new ColumnSpec("Lines", SwingConstants.RIGHT, 45, Match.LINES_COMPARATOR), }; static { - for (Object[] languageSet : LANGUAGE_SETS) { - LANGUAGE_CONFIGS_BY_LABEL.put((String) languageSet[0], (LanguageConfig) languageSet[1]); + for (LanguageConfig lconf : LANGUAGE_SETS) { + LANGUAGE_CONFIGS_BY_LABEL.put(lconf.getLabel(), lconf); } } @@ -243,16 +209,13 @@ public class GUI implements CPDListener { private final class GoListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { - new Thread(new Runnable() { - @Override - public void run() { - tokenizingFilesBar.setValue(0); - tokenizingFilesBar.setString(""); - resultsTextArea.setText(""); - phaseLabel.setText(""); - timeField.setText(""); - go(); - } + new Thread(() -> { + tokenizingFilesBar.setValue(0); + tokenizingFilesBar.setString(""); + resultsTextArea.setText(""); + phaseLabel.setText(""); + timeField.setText(""); + go(); }).start(); } } @@ -275,7 +238,7 @@ public class GUI implements CPDListener { } if (!f.canWrite()) { - final CPDReport report = new CPDReport(matches, numberOfTokensPerFile); + final CPDReport report = new CPDReport(new SourceManager(Collections.emptyList()), matches, numberOfTokensPerFile); try (PrintWriter pw = new PrintWriter(Files.newOutputStream(f.toPath()))) { renderer.render(report, pw); pw.flush(); @@ -409,7 +372,7 @@ public class GUI implements CPDListener { progressPanel = makeProgressPanel(); JPanel resultsPanel = makeResultsPanel(); - adjustLanguageControlsFor((LanguageConfig) LANGUAGE_SETS[0][1]); + adjustLanguageControlsFor(LANGUAGE_SETS.get(0)); frame.getContentPane().setLayout(new BorderLayout()); JPanel topPanel = new JPanel(); @@ -430,8 +393,13 @@ public class GUI implements CPDListener { ignoreAnnotationsCheckbox.setEnabled(current.canIgnoreAnnotations()); ignoreUsingsCheckbox.setEnabled(current.canIgnoreUsings()); ignoreLiteralSequencesCheckbox.setEnabled(current.canIgnoreLiteralSequences()); - extensionField.setText(current.extensions()[0]); - boolean enableExtension = current.extensions()[0].isEmpty(); + String firstExt = current.getLanguage().getExtensions().get(0); + boolean enableExtension = CUSTOM_EXTENSION_SENTINEL.equals(firstExt); + if (enableExtension) { + extensionField.setText(""); + } else { + extensionField.setText(firstExt); + } extensionField.setEnabled(enableExtension); extensionLabel.setEnabled(enableExtension); } @@ -447,15 +415,10 @@ public class GUI implements CPDListener { minimumLengthField.setColumns(4); helper.add(minimumLengthField); helper.addLabel("Language:"); - for (int i = 0; i < LANGUAGE_SETS.length; i++) { - languageBox.addItem(String.valueOf(LANGUAGE_SETS[i][0])); + for (LanguageConfig lconf : LANGUAGE_SETS) { + languageBox.addItem(lconf.getLabel()); } - languageBox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - adjustLanguageControlsFor(languageConfigFor((String) languageBox.getSelectedItem())); - } - }); + languageBox.addActionListener(e -> adjustLanguageControlsFor(languageConfigFor((String) languageBox.getSelectedItem()))); helper.add(languageBox); helper.nextRow(); helper.addLabel("Also scan subdirectories?"); @@ -545,7 +508,7 @@ public class GUI implements CPDListener { for (int selectionIndex : selectionIndices) { selections.add((Match) model.getValueAt(selectionIndex, 99)); } - CPDReport toRender = new CPDReport(selections, Collections.emptyMap()); + CPDReport toRender = new CPDReport(new SourceManager(Collections.emptyList()), selections, Collections.emptyMap()); String report = new SimpleRenderer(trimLeadingWhitespace).renderToString(toRender); resultsTextArea.setText(report); resultsTextArea.setCaretPosition(0); // move to the top @@ -587,26 +550,11 @@ public class GUI implements CPDListener { private JComponent makeMatchList() { - resultsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - populateResultArea(); - } - }); + resultsTable.getSelectionModel().addListSelectionListener(e -> populateResultArea()); - resultsTable.registerKeyboardAction(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - copyMatchListSelectionsToClipboard(); - } - }, "Copy", COPY_KEY_STROKE, JComponent.WHEN_FOCUSED); + resultsTable.registerKeyboardAction(e -> copyMatchListSelectionsToClipboard(), "Copy", COPY_KEY_STROKE, JComponent.WHEN_FOCUSED); - resultsTable.registerKeyboardAction(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - deleteMatchlistSelections(); - } - }, "Del", DELETE_KEY_STROKE, JComponent.WHEN_FOCUSED); + resultsTable.registerKeyboardAction(e -> deleteMatchlistSelections(), "Del", DELETE_KEY_STROKE, JComponent.WHEN_FOCUSED); int[] alignments = new int[matchColumns.length]; for (int i = 0; i < alignments.length; i++) { @@ -627,9 +575,8 @@ public class GUI implements CPDListener { } private boolean isLegalPath(String path, LanguageConfig config) { - String[] extensions = config.extensions(); - for (int i = 0; i < extensions.length; i++) { - if (path.endsWith(extensions[i]) && !extensions[i].isEmpty()) { + for (String extension : config.getLanguage().getExtensions()) { + if (path.endsWith(extension) && !extension.isEmpty()) { return true; } } @@ -639,8 +586,8 @@ public class GUI implements CPDListener { private String setLabelFor(Match match) { Set sourceIDs = new HashSet<>(match.getMarkCount()); - for (Iterator occurrences = match.iterator(); occurrences.hasNext();) { - sourceIDs.add(occurrences.next().getFilename()); + for (Mark mark : match) { + sourceIDs.add(mark.getFilename()); } String label; @@ -673,7 +620,6 @@ public class GUI implements CPDListener { setProgressControls(true); - Properties p = new Properties(); CPDConfiguration config = new CPDConfiguration(); config.setMinimumTileSize(Integer.parseInt(minimumLengthField.getText())); config.setSourceEncoding(encodingField.getText()); @@ -682,49 +628,44 @@ public class GUI implements CPDListener { config.setIgnoreAnnotations(ignoreAnnotationsCheckbox.isSelected()); config.setIgnoreUsings(ignoreUsingsCheckbox.isSelected()); config.setIgnoreLiteralSequences(ignoreLiteralSequencesCheckbox.isSelected()); - p.setProperty(LanguageFactory.EXTENSION, extensionField.getText()); + // p.setProperty(LanguageFactory.EXTENSION, extensionField.getText()); //FIXME LanguageConfig conf = languageConfigFor((String) languageBox.getSelectedItem()); - Language language = conf.languageFor(p); + Language language = conf.getLanguage(); config.setLanguage(language); CPDConfiguration.setSystemProperties(config); - CPD cpd = new CPD(config); - cpd.setCpdListener(this); - tokenizingFilesBar.setMinimum(0); - phaseLabel.setText(""); - if (isLegalPath(dirPath.getPath(), conf)) { // should use the - // language file filter - // instead? - cpd.add(dirPath); - } else { - if (recurseCheckbox.isSelected()) { - cpd.addRecursively(dirPath); + try (CpdAnalysis cpd = new CpdAnalysis(config)) { + cpd.setCpdListener(this); + + tokenizingFilesBar.setMinimum(0); + phaseLabel.setText(""); + if (isLegalPath(dirPath.getPath(), conf)) { + // should use the + // language file filter + // instead? + // fixme wth + cpd.files().addFileOrDirectory(dirPath.toPath()); } else { - cpd.addAllInDirectory(dirPath); + cpd.files().addFileOrDirectory(dirPath.toPath(), recurseCheckbox.isSelected()); } - } - Timer t = createTimer(); - t.start(); - cpd.go(); - t.stop(); - - CPDReport cpdReport = cpd.toReport(); - numberOfTokensPerFile = cpdReport.getNumberOfTokensPerFile(); - matches = new ArrayList<>(); - for (Match match : cpdReport.getMatches()) { - setLabelFor(match); - matches.add(match); - } - - setListDataFrom(matches); - String report = new SimpleRenderer().renderToString(cpdReport); - if (report.length() == 0) { - JOptionPane.showMessageDialog(frame, - "Done. Couldn't find any duplicates longer than " + minimumLengthField.getText() + " tokens"); - } else { - resultsTextArea.setText(report); + Timer t = createTimer(); + t.start(); + cpd.performAnalysis(report -> { + t.stop(); + numberOfTokensPerFile = report.getNumberOfTokensPerFile(); + matches = new ArrayList<>(report.getMatches()); + matches.forEach(this::setLabelFor); + setListDataFrom(matches); + String reportString = new SimpleRenderer().renderToString(report); + if (reportString.isEmpty()) { + JOptionPane.showMessageDialog(frame, + "Done. Couldn't find any duplicates longer than " + minimumLengthField.getText() + " tokens"); + } else { + resultsTextArea.setText(reportString); + } + }); } } catch (IOException | RuntimeException t) { t.printStackTrace(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java index ade537f08e..1db5c8689f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang; +import net.sourceforge.pmd.cpd.Tokenizer; + /** * Base class for language modules that only support CPD and not PMD. * @@ -25,4 +27,7 @@ public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase { public boolean supportsParsing() { return false; } + + @Override + public abstract Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java index 8133d732fb..4c17ee7510 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java @@ -184,7 +184,7 @@ public abstract class LanguageModuleBase implements Language { * * */ - protected static final class LanguageMetadata { + public static final class LanguageMetadata { /** Language IDs should be conventional Java package names. */ private static final Pattern VALID_LANG_ID = Pattern.compile("[a-z][_a-z0-9]*"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java index c2a5cf4528..ce973783c3 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; import java.nio.file.Paths; import java.util.HashSet; import java.util.List; @@ -14,16 +15,22 @@ import java.util.Set; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.util.CollectionUtil; + class CPDFilelistTest { @Test - void testFilelist() { + void testFilelist() throws IOException { CPDConfiguration arguments = new CPDConfiguration(); - arguments.setLanguage(new CpddummyLanguage()); + arguments.setLanguage(DummyLanguageModule.getInstance()); arguments.setFileListPath("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist.txt"); - CPD cpd = new CPD(arguments); + List paths; + try (CpdAnalysis cpd = new CpdAnalysis(arguments)) { + paths = CollectionUtil.map(cpd.files().getCollectedFiles(), TextFile::getPathId); + } - List paths = cpd.getSourcePaths(); assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { @@ -34,13 +41,15 @@ class CPDFilelistTest { } @Test - void testFilelistMultipleLines() { + void testFilelistMultipleLines() throws IOException { CPDConfiguration arguments = new CPDConfiguration(); - arguments.setLanguage(new CpddummyLanguage()); + arguments.setLanguage(DummyLanguageModule.getInstance()); arguments.setFileListPath("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist2.txt"); - CPD cpd = new CPD(arguments); + List paths; + try (CpdAnalysis cpd = new CpdAnalysis(arguments)) { + paths = CollectionUtil.map(cpd.files().getCollectedFiles(), TextFile::getPathId); + } - List paths = cpd.getSourcePaths(); assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { From 1828faeadc4d7530d2c1f053fdd15df942386c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Feb 2023 19:27:46 +0100 Subject: [PATCH 101/347] Fix some modules --- .../java/net/sourceforge/pmd/ant/CPDTask.java | 31 ++++++------------- .../java/net/sourceforge/pmd/cpd/GUI.java | 3 +- .../pmd/cpd/test/CpdTextComparisonTest.kt | 5 +++ .../sourceforge/pmd/cpd/ModelicaLanguage.java | 13 -------- .../pmd/cpd/ModelicaTokenizer.java | 3 +- .../lang/modelica/ModelicaLanguageModule.java | 7 +++++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../pmd/cpd/ObjectiveCLanguage.java | 19 ------------ .../objectivec/ObjectiveCLanguageModule.java | 29 +++++++++++++++++ .../objectivec}/cpd/ObjectiveCTokenizer.java | 5 +-- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../cpd/ObjectiveCTokenizerTest.java | 8 ++--- .../cpd/testdata/big_sample.m | 0 .../cpd/testdata/big_sample.txt | 0 .../cpd/testdata/tabWidth.m | 0 .../cpd/testdata/tabWidth.txt | 0 .../cpd/testdata/unicodeCharInIdent.m | 0 .../cpd/testdata/unicodeCharInIdent.txt | 0 .../cpd/testdata/unicodeEscapeInString.m | 0 .../cpd/testdata/unicodeEscapeInString.txt | 0 .../net/sourceforge/pmd/cpd/VfLanguage.java | 15 --------- .../net/sourceforge/pmd/cpd/VfTokenizer.java | 6 ++-- .../pmd/lang/vf/VfLanguageModule.java | 7 +++++ .../services/net.sourceforge.pmd.cpd.Language | 1 - 25 files changed, 71 insertions(+), 84 deletions(-) delete mode 100644 pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java delete mode 100644 pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language delete mode 100644 pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCLanguage.java create mode 100644 pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java rename pmd-objectivec/src/main/java/net/sourceforge/pmd/{ => lang/objectivec}/cpd/ObjectiveCTokenizer.java (75%) delete mode 100644 pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-objectivec/src/test/java/net/sourceforge/pmd/{ => lang/objectivec}/cpd/ObjectiveCTokenizerTest.java (83%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/big_sample.m (100%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/big_sample.txt (100%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/tabWidth.m (100%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/tabWidth.txt (100%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/unicodeCharInIdent.m (100%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/unicodeCharInIdent.txt (100%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/unicodeEscapeInString.m (100%) rename pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/{objc => objectivec}/cpd/testdata/unicodeEscapeInString.txt (100%) delete mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfLanguage.java delete mode 100644 pmd-visualforce/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index fdb8253d1b..db05ae2ab7 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -15,7 +15,6 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Properties; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; @@ -28,7 +27,6 @@ import net.sourceforge.pmd.cpd.CPDConfiguration; import net.sourceforge.pmd.cpd.CPDReport; import net.sourceforge.pmd.cpd.CSVRenderer; import net.sourceforge.pmd.cpd.CpdAnalysis; -import net.sourceforge.pmd.cpd.Language; import net.sourceforge.pmd.cpd.LanguageFactory; import net.sourceforge.pmd.cpd.SimpleRenderer; import net.sourceforge.pmd.cpd.Tokenizer; @@ -92,11 +90,19 @@ public class CPDTask extends Task { log("Tokenizing files", Project.MSG_INFO); CPDConfiguration config = new CPDConfiguration(); config.setMinimumTileSize(minimumTokenCount); - config.setLanguage(createLanguage()); + config.setLanguage(config.getLanguageRegistry().getLanguageById(language)); config.setSourceEncoding(encoding); config.setSkipDuplicates(skipDuplicateFiles); config.setSkipLexicalErrors(skipLexicalErrors); + config.setIgnoreAnnotations(ignoreAnnotations); + config.setIgnoreLiterals(ignoreLiterals); + config.setIgnoreIdentifiers(ignoreIdentifiers); + config.setIgnoreUsings(ignoreUsings); + if (skipBlocks) { + config.setSkipBlocksPattern(skipBlocksPattern); + } + try (CpdAnalysis cpd = new CpdAnalysis(config)) { addFiles(cpd); @@ -119,25 +125,6 @@ public class CPDTask extends Task { } } - private Language createLanguage() { - Properties p = new Properties(); - if (ignoreLiterals) { - p.setProperty(Tokenizer.IGNORE_LITERALS, "true"); - } - if (ignoreIdentifiers) { - p.setProperty(Tokenizer.IGNORE_IDENTIFIERS, "true"); - } - if (ignoreAnnotations) { - p.setProperty(Tokenizer.IGNORE_ANNOTATIONS, "true"); - } - if (ignoreUsings) { - p.setProperty(Tokenizer.IGNORE_USINGS, "true"); - } - p.setProperty(Tokenizer.OPTION_SKIP_BLOCKS, Boolean.toString(skipBlocks)); - p.setProperty(Tokenizer.OPTION_SKIP_BLOCKS_PATTERN, skipBlocksPattern); - return LanguageFactory.createLanguage(language, p); - } - private void report(CPDReport report) throws ReportException { if (report.getMatches().isEmpty()) { log("No duplicates over " + minimumTokenCount + " tokens found", Project.MSG_INFO); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index fc99bc3373..e600e74854 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -28,7 +28,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import javax.swing.AbstractButton; import javax.swing.BorderFactory; @@ -62,9 +61,9 @@ import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index c3524a27a2..0f983edf54 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -8,6 +8,7 @@ import io.kotest.assertions.throwables.shouldThrow import net.sourceforge.pmd.cpd.* import net.sourceforge.pmd.lang.Language import net.sourceforge.pmd.lang.LanguagePropertyBundle +import net.sourceforge.pmd.lang.LanguageRegistry import net.sourceforge.pmd.lang.ast.TokenMgrError import net.sourceforge.pmd.lang.document.TextDocument import net.sourceforge.pmd.lang.document.TextFile @@ -27,6 +28,10 @@ abstract class CpdTextComparisonTest( override val extensionIncludingDot: String ) : BaseTextComparisonTest() { + constructor(langId: String, extensionIncludingDot: String) : this( + LanguageRegistry.CPD.getLanguageById(langId)!!, + extensionIncludingDot + ) fun newTokenizer(config: LanguagePropertyConfig): Tokenizer { val properties = language.newPropertyBundle().also { config.setProperties(it) } diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java deleted file mode 100644 index 53852955e5..0000000000 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaLanguage.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import net.sourceforge.pmd.lang.modelica.ModelicaLanguageModule; - -public class ModelicaLanguage extends AbstractLanguage { - public ModelicaLanguage() { - super(ModelicaLanguageModule.NAME, ModelicaLanguageModule.TERSE_NAME, new ModelicaTokenizer(), ".mo"); - } -} diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java index 61bbad3226..140d9a941d 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.modelica.ast.ModelicaTokenKinds; @@ -16,7 +17,7 @@ public class ModelicaTokenizer extends JavaCCTokenizer { @Override protected TokenManager makeLexerImpl(TextDocument doc) { - return ModelicaTokenKinds.newTokenManager(doc); + return ModelicaTokenKinds.newTokenManager(CharStream.create(doc)); } @Override diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java index 2746c21363..d147e23957 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java @@ -4,6 +4,9 @@ package net.sourceforge.pmd.lang.modelica; +import net.sourceforge.pmd.cpd.ModelicaTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; public class ModelicaLanguageModule extends SimpleLanguageModuleBase { @@ -15,4 +18,8 @@ public class ModelicaLanguageModule extends SimpleLanguageModuleBase { new ModelicaHandler()); } + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new ModelicaTokenizer(); + } } diff --git a/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 6a4b040fe1..0000000000 --- a/pmd-modelica/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.ModelicaLanguage \ No newline at end of file diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCLanguage.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCLanguage.java deleted file mode 100644 index 8ba0695511..0000000000 --- a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCLanguage.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Defines the Language module for Objective-C - */ -public class ObjectiveCLanguage extends AbstractLanguage { - - /** - * Creates a new instance of {@link ObjectiveCLanguage} with the default - * extensions for Objective-C files. - */ - public ObjectiveCLanguage() { - super("Objective-C", "objectivec", new ObjectiveCTokenizer(), ".h", ".m"); - } -} diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java new file mode 100644 index 0000000000..a0bb56045a --- /dev/null +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.objectivec; + +import net.sourceforge.pmd.lang.objectivec.cpd.ObjectiveCTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * Defines the Language module for Objective-C + */ +public class ObjectiveCLanguageModule extends CpdOnlyLanguageModuleBase { + + /** + * Creates a new instance of {@link ObjectiveCLanguageModule} with the default + * extensions for Objective-C files. + */ + public ObjectiveCLanguageModule() { + super(LanguageMetadata.withId("objectivec").name("Objective-C").extensions("h", "m")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new ObjectiveCTokenizer(); + } +} diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java similarity index 75% rename from pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java rename to pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java index 6c338b4067..2e21fdc7c8 100644 --- a/pmd-objectivec/src/main/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizer.java +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java @@ -2,10 +2,11 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.objectivec.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.objectivec.ast.ObjectiveCTokenKinds; @@ -17,6 +18,6 @@ public class ObjectiveCTokenizer extends JavaCCTokenizer { @Override protected TokenManager makeLexerImpl(TextDocument doc) { - return ObjectiveCTokenKinds.newTokenManager(doc); + return ObjectiveCTokenKinds.newTokenManager(CharStream.create(doc)); } } diff --git a/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 6154027550..0000000000 --- a/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.ObjectiveCLanguage diff --git a/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..65daa818c8 --- /dev/null +++ b/pmd-objectivec/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.objectivec.ObjectiveCLanguageModule diff --git a/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java b/pmd-objectivec/src/test/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizerTest.java similarity index 83% rename from pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java rename to pmd-objectivec/src/test/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizerTest.java index edb8979a1c..720a369fce 100644 --- a/pmd-objectivec/src/test/java/net/sourceforge/pmd/cpd/ObjectiveCTokenizerTest.java +++ b/pmd-objectivec/src/test/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.objectivec.cpd; import org.junit.jupiter.api.Test; @@ -12,12 +12,12 @@ class ObjectiveCTokenizerTest extends CpdTextComparisonTest { ObjectiveCTokenizerTest() { - super(".m"); + super("objectivec", ".m"); } @Override protected String getResourcePrefix() { - return "../lang/objc/cpd/testdata"; + return "../lang/objectivec/cpd/testdata"; } @Test diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/big_sample.m b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/big_sample.m similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/big_sample.m rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/big_sample.m diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/big_sample.txt b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/big_sample.txt similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/big_sample.txt rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/big_sample.txt diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/tabWidth.m b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/tabWidth.m similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/tabWidth.m rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/tabWidth.m diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/tabWidth.txt b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/tabWidth.txt similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/tabWidth.txt rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/tabWidth.txt diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeCharInIdent.m b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeCharInIdent.m similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeCharInIdent.m rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeCharInIdent.m diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeCharInIdent.txt b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeCharInIdent.txt similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeCharInIdent.txt rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeCharInIdent.txt diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeEscapeInString.m b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeEscapeInString.m similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeEscapeInString.m rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeEscapeInString.m diff --git a/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeEscapeInString.txt b/pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeEscapeInString.txt similarity index 100% rename from pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objc/cpd/testdata/unicodeEscapeInString.txt rename to pmd-objectivec/src/test/resources/net/sourceforge/pmd/lang/objectivec/cpd/testdata/unicodeEscapeInString.txt diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfLanguage.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfLanguage.java deleted file mode 100644 index 301bfc15c3..0000000000 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfLanguage.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * @author sergey.gorbaty - * - */ -public class VfLanguage extends AbstractLanguage { - public VfLanguage() { - super("VisualForce", "vf", new VfTokenizer(), ".page", ".component"); - } -} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java index d831ee0f7d..72d16b342c 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaEscapeTranslator; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; @@ -21,11 +22,10 @@ public class VfTokenizer extends JavaCCTokenizer { @Override protected TokenManager makeLexerImpl(TextDocument doc) { - return VfTokenKinds.newTokenManager(doc); + return VfTokenKinds.newTokenManager(CharStream.create(doc, tokenBehavior())); } - @Override - protected TokenDocumentBehavior tokenBehavior() { + private TokenDocumentBehavior tokenBehavior() { return new JavaccTokenDocument.TokenDocumentBehavior(VfTokenKinds.TOKEN_NAMES) { @Override public TextDocument translate(TextDocument text) throws MalformedSourceException { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java index b75d9ffba6..3504d228e2 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.vf; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.cpd.VfTokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -26,6 +28,11 @@ public class VfLanguageModule extends SimpleLanguageModuleBase { p -> new VfHandler((VfLanguageProperties) p)); } + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new VfTokenizer(); + } + @Override public LanguagePropertyBundle newPropertyBundle() { return new VfLanguageProperties(); diff --git a/pmd-visualforce/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-visualforce/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index f3f1512dda..0000000000 --- a/pmd-visualforce/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.VfLanguage From add597026cc3d6c2fb2885433aed0d61633f0772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 15:28:34 +0100 Subject: [PATCH 102/347] Don't forget EOF token --- .../net/sourceforge/pmd/cpd/AnyTokenizer.java | 4 +-- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 20 ++++------- .../java/net/sourceforge/pmd/cpd/Mark.java | 2 +- .../net/sourceforge/pmd/cpd/TokenEntry.java | 35 ++++++++++++------- .../net/sourceforge/pmd/cpd/TokenFactory.java | 10 +++++- .../net/sourceforge/pmd/cpd/Tokenizer.java | 8 ++++- .../java/net/sourceforge/pmd/cpd/Tokens.java | 5 +++ .../pmd/cpd/internal/TokenizerBase.java | 4 +-- .../sourceforge/pmd/cpd/AnyTokenizerTest.java | 15 ++++---- .../sourceforge/pmd/cpd/TokenEntryTest.java | 2 +- .../sourceforge/pmd/cpd/CPPTokenizerTest.java | 2 +- .../sourceforge/pmd/cpd/GroovyTokenizer.java | 4 +-- .../pmd/lang/html/ast/HtmlTokenizer.java | 4 +-- .../pmd/cpd/MatchAlgorithmTest.java | 4 +-- .../pmd/cpd/test/CpdTextComparisonTest.kt | 15 ++++---- .../sourceforge/pmd/cpd/MatlabLanguage.java | 19 ---------- .../pmd/lang/matlab/MatlabLanguageModule.java | 29 +++++++++++++++ .../matlab}/cpd/MatlabTokenizer.java | 7 ++-- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../matlab}/cpd/MatlabTokenizerTest.java | 11 ++---- 21 files changed, 118 insertions(+), 84 deletions(-) delete mode 100644 pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabLanguage.java create mode 100644 pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java rename pmd-matlab/src/main/java/net/sourceforge/pmd/{ => lang/matlab}/cpd/MatlabTokenizer.java (75%) delete mode 100644 pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-matlab/src/test/java/net/sourceforge/pmd/{ => lang/matlab}/cpd/MatlabTokenizerTest.java (83%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java index 671644eae6..49000eaadd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java @@ -62,7 +62,7 @@ public class AnyTokenizer implements Tokenizer { } @Override - public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { + public void tokenize(TextDocument sourceCode, TokenFactory tokens) { Chars text = sourceCode.getText(); Matcher matcher = pattern.matcher(text); int lineNo = 1; @@ -87,7 +87,7 @@ public class AnyTokenizer implements Tokenizer { lineNo += StringUtil.lineNumberAt(image, image.length()) - 1; lastLineStart = matcher.start() + image.length() - ecol + 1; } - tokenEntries.recordToken(image, bline, bcol, lineNo, ecol); + tokens.recordToken(image, bline, bcol, lineNo, ecol); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 4d5c4eeccd..5b5cf439ac 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -112,20 +112,10 @@ public final class CpdAnalysis implements AutoCloseable { this.listener = cpdListener; } - private int doTokenize(TextDocument document, Tokenizer tokenizer, Tokens tokens) { + private int doTokenize(TextDocument document, Tokenizer tokenizer, Tokens tokens) throws IOException, TokenMgrError { LOGGER.trace("Tokenizing {}", document.getPathId()); int lastTokenSize = tokens.size(); - try { - tokenizer.tokenize(document, TokenFactory.forFile(document, tokens)); - } catch (IOException ioe) { - reporter.errorEx("Error while lexing.", ioe); - } catch (TokenMgrError e) { - e.setFileName(document.getDisplayName()); - reporter.errorEx("Error while lexing.", e); - throw e; - } finally { - tokens.addEof(); - } + Tokenizer.tokenize(tokenizer, document, tokens); return tokens.size() - lastTokenSize - 1; /* EOF */ } @@ -151,7 +141,11 @@ public final class CpdAnalysis implements AutoCloseable { int newTokens = doTokenize(textDocument, tokenizers.get(textFile.getLanguageVersion().getLanguage()), tokens); numberOfTokensPerFile.put(textDocument.getPathId(), newTokens); listener.addedFile(1); - } catch (TokenMgrError e) { + } catch (TokenMgrError | IOException e) { + if (e instanceof TokenMgrError) { + ((TokenMgrError) e).setFileName(textFile.getDisplayName()); + } + reporter.errorEx("Error while lexing.", e); // already reported savedState.restore(tokens); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index 6e9c92213f..cc3519ed48 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -19,7 +19,7 @@ public class Mark implements Comparable { } public String getFilename() { - return this.token.getTokenSrcID(); + return this.token.getFileName(); } public int getBeginLine() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java index 6c4ef2c9b8..ccfdbb11dc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java @@ -8,7 +8,7 @@ public class TokenEntry implements Comparable { public static final TokenEntry EOF = new TokenEntry(); - private final String tokenSrcID; + private final String fileName; private final int beginLine; private final int beginColumn; private final int endColumn; @@ -18,15 +18,15 @@ public class TokenEntry implements Comparable { private TokenEntry() { this.identifier = 0; - this.tokenSrcID = "EOFMarker"; + this.fileName = "EOFMarker"; this.beginLine = -1; this.beginColumn = -1; this.endColumn = -1; } - TokenEntry(int imageId, String tokenSrcID, int beginLine, int beginColumn, int endLine, int endColumn, int index) { + TokenEntry(int imageId, String fileName, int beginLine, int beginColumn, int endLine, int endColumn, int index) { assert isOk(beginLine) && isOk(beginColumn) && isOk(endLine) && isOk(endColumn) : "Coordinates are 1-based"; - this.tokenSrcID = tokenSrcID; + this.fileName = fileName; this.beginLine = beginLine; this.beginColumn = beginColumn; this.endColumn = endColumn; @@ -40,8 +40,8 @@ public class TokenEntry implements Comparable { } - String getTokenSrcID() { - return tokenSrcID; + String getFileName() { + return fileName; } public int getBeginLine() { @@ -51,6 +51,7 @@ public class TokenEntry implements Comparable { /** * The column number where this token begins. * returns -1 if not available + * * @return the begin column number */ public int getBeginColumn() { @@ -60,17 +61,18 @@ public class TokenEntry implements Comparable { /** * The column number where this token ends. * returns -1 if not available + * * @return the end column number */ public int getEndColumn() { return endColumn; // TODO Java 1.8 make optional } - int getIdentifier() { + int getIdentifier() { return this.identifier; } - int getIndex() { + int getIndex() { return this.index; } @@ -79,7 +81,7 @@ public class TokenEntry implements Comparable { return hashCode; } - void setHashCode(int hashCode) { + void setHashCode(int hashCode) { this.hashCode = hashCode; } @@ -105,6 +107,18 @@ public class TokenEntry implements Comparable { return getIndex() - other.getIndex(); } + final void setImageIdentifier(int identifier) { + this.identifier = identifier; + } + + public String getImage(Tokens tokens) { + if (EOF.equals(this)) { + return "EOF"; + } + String image = tokens.imageFromId(this.identifier); + return image == null ? "--unknown--" : image; + } + @Override public String toString() { if (EOF.equals(this)) { @@ -113,7 +127,4 @@ public class TokenEntry implements Comparable { return Integer.toString(identifier); } - final void setImageIdentifier(int identifier) { - this.identifier = identifier; - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java index 796b40258c..84d4204901 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.cpd; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextDocument; -public interface TokenFactory { +public interface TokenFactory extends AutoCloseable { void recordToken(String image, int startLine, int startCol, int endLine, int endCol); @@ -19,6 +19,9 @@ public interface TokenFactory { TokenEntry peekLastToken(); + @Override + void close(); + static TokenFactory forFile(TextDocument file, Tokens sink) { return new TokenFactory() { final String name = file.getPathId(); @@ -37,6 +40,11 @@ public interface TokenFactory { public TokenEntry peekLastToken() { return sink.peekLastToken(); } + + @Override + public void close() { + sink.addEof(); + } }; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index 8abe88cd86..be20bf6570 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -82,5 +82,11 @@ public interface Tokenizer { String DEFAULT_SKIP_BLOCKS_PATTERN = "#if 0|#endif"; - void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) throws IOException; + void tokenize(TextDocument sourceCode, TokenFactory tokens) throws IOException; + + static void tokenize(Tokenizer tokenizer, TextDocument textDocument, Tokens tokens) throws IOException { + try (TokenFactory tf = TokenFactory.forFile(textDocument, tokens)) { + tokenizer.tokenize(textDocument, tf); + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 1e0841a125..b9b7fee8b2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; public class Tokens { @@ -32,6 +33,10 @@ public class Tokens { return images.computeIfAbsent(newImage, k -> images.size() + 1); } + String imageFromId(int i) { + return images.entrySet().stream().filter(it -> it.getValue() == i).findFirst().map(Entry::getKey).orElse(null); + } + public TokenEntry peekLastToken() { return get(size() - 1); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java index 6168c2f58b..4d3d409b0f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java @@ -30,11 +30,11 @@ public abstract class TokenizerBase> implements Tokeni } @Override - public void tokenize(TextDocument document, TokenFactory tokenEntries) throws IOException { + public void tokenize(TextDocument document, TokenFactory tokens) throws IOException { TokenManager tokenManager = filterTokenStream(makeLexerImpl(document)); T currentToken = tokenManager.getNextToken(); while (currentToken != null) { - processToken(tokenEntries, currentToken); + processToken(tokens, currentToken); currentToken = tokenManager.getNextToken(); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java index 05013bc139..18f6d2a76b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.cpd; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -18,19 +19,19 @@ import net.sourceforge.pmd.lang.document.TextDocument; class AnyTokenizerTest { @Test - void testMultiLineMacros() { + void testMultiLineMacros() throws IOException { AnyTokenizer tokenizer = new AnyTokenizer("//"); compareResult(tokenizer, TEST1, EXPECTED); } @Test - void testStringEscape() { + void testStringEscape() throws IOException { AnyTokenizer tokenizer = new AnyTokenizer("//"); compareResult(tokenizer, "a = \"oo\\n\"", listOf("a", "=", "\"oo\\n\"", "EOF")); } @Test - void testMultilineString() { + void testMultilineString() throws IOException { AnyTokenizer tokenizer = new AnyTokenizer("//"); Tokens tokens = compareResult(tokenizer, "a = \"oo\n\";", listOf("a", "=", "\"oo\n\"", ";", "EOF")); TokenEntry string = tokens.getTokens().get(2); @@ -50,11 +51,11 @@ class AnyTokenizerTest { * Tests that [core][cpd] AnyTokenizer doesn't count columns correctly #2760 is actually fixed. */ @Test - void testTokenPosition() { + void testTokenPosition() throws IOException { AnyTokenizer tokenizer = new AnyTokenizer(); TextDocument code = TextDocument.readOnlyString("a;\nbbbb\n;", "Foo.dummy", DummyLanguageModule.getInstance().getDefaultVersion()); Tokens tokens = new Tokens(); - tokenizer.tokenize(code, TokenFactory.forFile(code, tokens)); + Tokenizer.tokenize(tokenizer, code, tokens); TokenEntry bbbbToken = tokens.getTokens().get(2); assertEquals(2, bbbbToken.getBeginLine()); assertEquals(1, bbbbToken.getBeginColumn()); @@ -62,10 +63,10 @@ class AnyTokenizerTest { } - private Tokens compareResult(AnyTokenizer tokenizer, String source, List expectedImages) { + private Tokens compareResult(AnyTokenizer tokenizer, String source, List expectedImages) throws IOException { TextDocument code = TextDocument.readOnlyString(source, "Foo.dummy", DummyLanguageModule.getInstance().getDefaultVersion()); Tokens tokens = new Tokens(); - tokenizer.tokenize(code, TokenFactory.forFile(code, tokens)); + Tokenizer.tokenize(tokenizer, code, tokens); List tokenStrings = new ArrayList<>(); for (TokenEntry token : tokens.getTokens()) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java index b393b0957f..c5248c722b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java @@ -16,7 +16,7 @@ class TokenEntryTest { tokens.addToken("public", "/var/Foo.java", 1, 2, 3, 4); TokenEntry mark = tokens.peekLastToken(); assertEquals(1, mark.getBeginLine()); - assertEquals("/var/Foo.java", mark.getTokenSrcID()); + assertEquals("/var/Foo.java", mark.getFileName()); assertEquals(0, mark.getIndex()); assertEquals(2, mark.getBeginColumn()); assertEquals(4, mark.getEndColumn()); diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java index 64cc27eb69..e0f03d28e6 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java @@ -32,7 +32,7 @@ class CPPTokenizerTest extends CpdTextComparisonTest { @Test void testUTFwithBOM() { Tokenizer tokenizer = newTokenizer(dontSkipBlocks()); - Tokens tokens = tokenize(tokenizer, "\ufeffint start()\n{ int ret = 1;\nreturn ret;\n}\n"); + Tokens tokens = tokenize(tokenizer, sourceCodeOf("\ufeffint start()\n{ int ret = 1;\nreturn ret;\n}\n")); assertEquals(15, tokens.size()); } diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java index 1b9283c279..1c39b10da8 100644 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java @@ -20,7 +20,7 @@ import groovyjarjarantlr.TokenStreamException; public class GroovyTokenizer implements Tokenizer { @Override - public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { + public void tokenize(TextDocument sourceCode, TokenFactory tokens) { GroovyLexer lexer = new GroovyLexer(sourceCode.newReader()); TokenStream tokenStream = lexer.plumb(); @@ -42,7 +42,7 @@ public class GroovyTokenizer implements Tokenizer { lastLine = token.getLine(); // todo inaccurate } - tokenEntries.recordToken(tokenText, token.getLine(), token.getColumn(), lastLine, lastCol); + tokens.recordToken(tokenText, token.getLine(), token.getColumn(), lastLine, lastCol); token = tokenStream.nextToken(); } } catch (TokenStreamException err) { diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java index 63fa4e45b6..530b2d4a12 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java @@ -19,7 +19,7 @@ import net.sourceforge.pmd.lang.html.HtmlLanguageModule; public class HtmlTokenizer implements Tokenizer { @Override - public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { + public void tokenize(TextDocument sourceCode, TokenFactory tokens) { HtmlLanguageModule html = HtmlLanguageModule.getInstance(); try (LanguageProcessor processor = html.createProcessor(html.newPropertyBundle())) { @@ -33,7 +33,7 @@ public class HtmlTokenizer implements Tokenizer { HtmlParser parser = new HtmlParser(); ASTHtmlDocument root = parser.parse(task); - traverse(root, tokenEntries); + traverse(root, tokens); } catch (IOException e) { throw new UncheckedIOException(e); } catch (Exception e) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index 9e680c83c6..4776e17029 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -45,7 +45,7 @@ class MatchAlgorithmTest { SourceManager sourceManager = new SourceManager(listOf(textFile)); Tokens tokens = new Tokens(); TextDocument sourceCode = sourceManager.get(textFile); - tokenizer.tokenize(sourceCode, TokenFactory.forFile(sourceCode, tokens)); + Tokenizer.tokenize(tokenizer, sourceCode, tokens); assertEquals(41, tokens.size()); MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, 5); @@ -77,7 +77,7 @@ class MatchAlgorithmTest { Tokenizer tokenizer = java.createCpdTokenizer(bundle); TextDocument sourceCode = TextDocument.readOnlyString(getSampleCode(), "Foo.java", java.getDefaultVersion()); Tokens tokens = new Tokens(); - tokenizer.tokenize(sourceCode, TokenFactory.forFile(sourceCode, tokens)); + Tokenizer.tokenize(tokenizer, sourceCode, tokens); MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, 5); matchAlgorithm.findMatches(); diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index 0f983edf54..0532ae4de8 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -106,7 +106,7 @@ abstract class CpdTextComparisonTest( append('L').append(curLine).appendLine() } - formatLine(token).appendLine() + formatLine(token, tokens).appendLine() } } @@ -119,9 +119,9 @@ abstract class CpdTextComparisonTest( ) - private fun StringBuilder.formatLine(token: TokenEntry) = + private fun StringBuilder.formatLine(token: TokenEntry, tokens: Tokens) = formatLine( - escapedImage = escapeImage(token.toString()), + escapedImage = escapeImage(token.getImage(tokens)), bcol = token.beginColumn, ecol = token.endColumn ) @@ -167,11 +167,14 @@ abstract class CpdTextComparisonTest( private fun sourceCodeOf(fileData: FileData): TextDocument = TextDocument.readOnlyString(fileData.fileText, fileData.fileName, language.defaultVersion) + @JvmOverloads + fun sourceCodeOf(text: String, fileName: String = TextFile.UNKNOWN_FILENAME): FileData = + FileData(fileName = fileName, fileText = text) + fun tokenize(tokenizer: Tokenizer, fileData: FileData): Tokens = - Tokens().also { - val tokens = Tokens() + Tokens().also { tokens -> val source = sourceCodeOf(fileData) - tokenizer.tokenize(source, TokenFactory.forFile(source, tokens)) + Tokenizer.tokenize(tokenizer, source, tokens) } private companion object { diff --git a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabLanguage.java b/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabLanguage.java deleted file mode 100644 index e4d4be1399..0000000000 --- a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabLanguage.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Defines the Language module for Matlab - */ -public class MatlabLanguage extends AbstractLanguage { - - /** - * Creates a new instance of {@link MatlabLanguage} with the default - * extensions for matlab files. - */ - public MatlabLanguage() { - super("Matlab", "matlab", new MatlabTokenizer(), ".m"); - } -} diff --git a/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java new file mode 100644 index 0000000000..dc33c1970d --- /dev/null +++ b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.matlab; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.matlab.cpd.MatlabTokenizer; + +/** + * Defines the Language module for Matlab + */ +public class MatlabLanguageModule extends CpdOnlyLanguageModuleBase { + + /** + * Creates a new instance of {@link MatlabLanguageModule} with the default + * extensions for matlab files. + */ + public MatlabLanguageModule() { + super(LanguageMetadata.withId("matlab").name("Matlab").extensions("m")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new MatlabTokenizer(); + } +} diff --git a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java similarity index 75% rename from pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java rename to pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java index b2233923b3..1a275cd4ef 100644 --- a/pmd-matlab/src/main/java/net/sourceforge/pmd/cpd/MatlabTokenizer.java +++ b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java @@ -1,11 +1,12 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.matlab.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.matlab.ast.MatlabTokenKinds; @@ -17,6 +18,6 @@ public class MatlabTokenizer extends JavaCCTokenizer { @Override protected TokenManager makeLexerImpl(TextDocument doc) { - return MatlabTokenKinds.newTokenManager(doc); + return MatlabTokenKinds.newTokenManager(CharStream.create(doc)); } } diff --git a/pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index cbda640f65..0000000000 --- a/pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.MatlabLanguage diff --git a/pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..1e88740675 --- /dev/null +++ b/pmd-matlab/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.matlab.MatlabLanguageModule diff --git a/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java b/pmd-matlab/src/test/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizerTest.java similarity index 83% rename from pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java rename to pmd-matlab/src/test/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizerTest.java index 73400c993e..68ed5fb0db 100644 --- a/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java +++ b/pmd-matlab/src/test/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.matlab.cpd; import org.junit.jupiter.api.Test; @@ -11,12 +11,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class MatlabTokenizerTest extends CpdTextComparisonTest { MatlabTokenizerTest() { - super(".m"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/matlab/cpd/testdata"; + super("matlab", ".m"); } @Test From 9f35966ec697ff2872f4adcb2e2a6f789d1c7a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 15:56:51 +0100 Subject: [PATCH 103/347] Refactor EOF handling Mandate that no coordinate is missing in a TokenEntry. --- .../pmd/lang/apex/ApexLanguageModule.java | 2 +- .../{ => lang/apex}/cpd/ApexTokenizer.java | 5 +- .../pmd/{ => lang/apex}/cpd/ApexCpdTest.java | 9 ++- .../apex}/cpd/ApexTokenizerTest.java | 11 +-- .../apex}/cpd/issue427/SFDCEncoder.cls | 0 .../cpd/issue427/SFDCEncoderConstants.cls | 0 .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 13 +++- .../java/net/sourceforge/pmd/cpd/Mark.java | 4 +- .../sourceforge/pmd/cpd/MatchAlgorithm.java | 10 +-- .../sourceforge/pmd/cpd/MatchCollector.java | 4 +- .../net/sourceforge/pmd/cpd/TokenEntry.java | 68 ++++++++++--------- .../net/sourceforge/pmd/cpd/TokenFactory.java | 2 +- .../java/net/sourceforge/pmd/cpd/Tokens.java | 22 +++--- .../sourceforge/pmd/cpd/TokenEntryTest.java | 3 +- .../pmd/cpd/test/CpdTextComparisonTest.kt | 2 +- 15 files changed, 84 insertions(+), 71 deletions(-) rename pmd-apex/src/main/java/net/sourceforge/pmd/{ => lang/apex}/cpd/ApexTokenizer.java (91%) rename pmd-apex/src/test/java/net/sourceforge/pmd/{ => lang/apex}/cpd/ApexCpdTest.java (86%) rename pmd-apex/src/test/java/net/sourceforge/pmd/{ => lang/apex}/cpd/ApexTokenizerTest.java (88%) rename pmd-apex/src/test/resources/net/sourceforge/pmd/{ => lang/apex}/cpd/issue427/SFDCEncoder.cls (100%) rename pmd-apex/src/test/resources/net/sourceforge/pmd/{ => lang/apex}/cpd/issue427/SFDCEncoderConstants.cls (100%) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java index 690c228c0b..874da4dd4c 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.apex; -import net.sourceforge.pmd.cpd.ApexTokenizer; +import net.sourceforge.pmd.lang.apex.cpd.ApexTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java similarity index 91% rename from pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java rename to pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java index 37872c2cc2..cea287a50e 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/cpd/ApexTokenizer.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java @@ -1,13 +1,14 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.apex.cpd; import java.util.Locale; import org.antlr.v4.runtime.CharStream; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; import net.sourceforge.pmd.lang.apex.ApexLanguageProperties; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java similarity index 86% rename from pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java rename to pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java index 3976feb551..5944e910c0 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexCpdTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.apex.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -13,6 +13,9 @@ import java.nio.file.Paths; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.cpd.CPDConfiguration; +import net.sourceforge.pmd.cpd.CpdAnalysis; +import net.sourceforge.pmd.cpd.Match; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; @@ -22,7 +25,7 @@ class ApexCpdTest { @BeforeEach void setUp() { - String path = IOUtil.normalizePath("src/test/resources/net/sourceforge/pmd/cpd/issue427"); + String path = IOUtil.normalizePath("src/test/resources/net/sourceforge/pmd/lang/apex/cpd/issue427"); testdir = Paths.get(path); } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexTokenizerTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizerTest.java similarity index 88% rename from pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexTokenizerTest.java rename to pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizerTest.java index 47fd06778b..76467e63b1 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/cpd/ApexTokenizerTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizerTest.java @@ -1,11 +1,12 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.apex.cpd; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; import net.sourceforge.pmd.cpd.test.LanguagePropertyConfig; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; @@ -16,12 +17,6 @@ class ApexTokenizerTest extends CpdTextComparisonTest { super(ApexLanguageModule.getInstance(), ".cls"); } - @Override - protected String getResourcePrefix() { - return "../lang/apex/cpd/testdata"; - } - - @Test void testTokenize() { doTest("Simple"); diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/cpd/issue427/SFDCEncoder.cls b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/issue427/SFDCEncoder.cls similarity index 100% rename from pmd-apex/src/test/resources/net/sourceforge/pmd/cpd/issue427/SFDCEncoder.cls rename to pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/issue427/SFDCEncoder.cls diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/cpd/issue427/SFDCEncoderConstants.cls b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/issue427/SFDCEncoderConstants.cls similarity index 100% rename from pmd-apex/src/test/resources/net/sourceforge/pmd/cpd/issue427/SFDCEncoderConstants.cls rename to pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/cpd/issue427/SFDCEncoderConstants.cls diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 5b5cf439ac..5d65b8aaac 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -13,6 +13,8 @@ import java.util.Map; import java.util.function.Consumer; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +37,7 @@ public final class CpdAnalysis implements AutoCloseable { private final CPDConfiguration configuration; private final FileCollector files; private final MessageReporter reporter; - private CPDListener listener; + private @NonNull CPDListener listener = new CPDNullListener(); public CpdAnalysis(CPDConfiguration config) throws IOException { @@ -63,7 +65,7 @@ public final class CpdAnalysis implements AutoCloseable { } private static void setPropertyIfMissing(PropertyDescriptor prop, LanguagePropertyBundle sink, T value) { - if (!sink.isPropertyOverridden(prop)) { + if (sink.hasDescriptor(prop) && !sink.isPropertyOverridden(prop)) { sink.setProperty(prop, value); } } @@ -108,7 +110,10 @@ public final class CpdAnalysis implements AutoCloseable { } } - public void setCpdListener(CPDListener cpdListener) { + public void setCpdListener(@Nullable CPDListener cpdListener) { + if (cpdListener == null) { + cpdListener = new CPDNullListener(); + } this.listener = cpdListener; } @@ -129,6 +134,7 @@ public final class CpdAnalysis implements AutoCloseable { Map tokenizers = sourceManager.getTextFiles().stream() .map(it -> it.getLanguageVersion().getLanguage()) + .distinct() .collect(Collectors.toMap(lang -> lang, lang -> lang.createCpdTokenizer(configuration.getLanguageProperties(lang)))); Map numberOfTokensPerFile = new HashMap<>(); @@ -165,6 +171,7 @@ public final class CpdAnalysis implements AutoCloseable { consumer.accept(cpdReport); } catch (Exception e) { + e.printStackTrace(); reporter.errorEx("Exception while running CPD", e); } // source manager is closed and closes all text files now. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index cc3519ed48..f9cd4ba93d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -40,7 +40,7 @@ public class Mark implements Comparable { } public int getEndLine() { - return getBeginLine() + getLineCount() - 1; + return endToken == null ? getBeginLine() : endToken.getEndLine(); } /** @@ -57,7 +57,7 @@ public class Mark implements Comparable { } public int getLineCount() { - return this.endToken == null ? 1 : this.endToken.getBeginLine(); + return this.getEndLine() - this.getBeginLine() + 1; } void setEndToken(TokenEntry endToken) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java index 593e0eb5c5..363eec7e46 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java @@ -11,6 +11,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import org.checkerframework.checker.nullness.qual.NonNull; + class MatchAlgorithm { private static final int MOD = 37; @@ -19,14 +21,14 @@ class MatchAlgorithm { private List matches; private final Tokens tokens; private final List code; - private CPDListener cpdListener; + private @NonNull CPDListener cpdListener; private final int min; MatchAlgorithm(Tokens tokens, int min) { this(tokens, min, new CPDNullListener()); } - MatchAlgorithm(Tokens tokens, int min, CPDListener listener) { + MatchAlgorithm(Tokens tokens, int min, @NonNull CPDListener listener) { this.tokens = tokens; this.code = tokens.getTokens(); this.min = min; @@ -92,7 +94,7 @@ class MatchAlgorithm { Map markGroups = new HashMap<>(tokens.size()); for (int i = code.size() - 1; i >= 0; i--) { TokenEntry token = code.get(i); - if (!TokenEntry.EOF.equals(token)) { + if (!token.isEof()) { int last = tokenAt(min, token).getIdentifier(); lastHash = MOD * lastHash + token.getIdentifier() - lastMod * last; token.setHashCode(lastHash); @@ -118,7 +120,7 @@ class MatchAlgorithm { for (int end = Math.max(0, i - min + 1); i > end; i--) { token = code.get(i - 1); lastHash = MOD * lastHash + token.getIdentifier(); - if (TokenEntry.EOF.equals(token)) { + if (token.isEof()) { break; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java index 38c9fdeb67..db49682393 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java @@ -96,7 +96,7 @@ public class MatchCollector { private boolean matchEnded(TokenEntry token1, TokenEntry token2) { return token1.getIdentifier() != token2.getIdentifier() - || TokenEntry.EOF.equals(token1) - || TokenEntry.EOF.equals(token2); + || token1.isEof() + || token2.isEof(); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java index ccfdbb11dc..3146cbb693 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java @@ -6,37 +6,46 @@ package net.sourceforge.pmd.cpd; public class TokenEntry implements Comparable { - public static final TokenEntry EOF = new TokenEntry(); + private static final int EOF = 0; private final String fileName; private final int beginLine; private final int beginColumn; private final int endColumn; + private final int endLine; private int index; private int identifier; private int hashCode; - private TokenEntry() { - this.identifier = 0; - this.fileName = "EOFMarker"; - this.beginLine = -1; - this.beginColumn = -1; - this.endColumn = -1; + /** constructor for EOF entries. */ + TokenEntry(String fileName, int line, int column) { + assert isOk(line) && isOk(column) : "Coordinates are 1-based"; + this.identifier = EOF; + this.fileName = fileName; + this.beginLine = line; + this.beginColumn = column; + this.endLine = line; + this.endColumn = column; } TokenEntry(int imageId, String fileName, int beginLine, int beginColumn, int endLine, int endColumn, int index) { assert isOk(beginLine) && isOk(beginColumn) && isOk(endLine) && isOk(endColumn) : "Coordinates are 1-based"; + assert imageId != EOF; this.fileName = fileName; this.beginLine = beginLine; this.beginColumn = beginColumn; + this.endLine = endLine; this.endColumn = endColumn; this.identifier = imageId; this.index = index; } + public boolean isEof() { + return this.identifier == EOF; + } private boolean isOk(int coord) { - return coord >= 1 || coord == -1; + return coord >= 1; } @@ -44,28 +53,25 @@ public class TokenEntry implements Comparable { return fileName; } + + /** The line number where this token starts. */ public int getBeginLine() { return beginLine; } - /** - * The column number where this token begins. - * returns -1 if not available - * - * @return the begin column number - */ - public int getBeginColumn() { - return beginColumn; // TODO Java 1.8 make optional + /** The line number where this token ends. */ + public int getEndLine() { + return endLine; } - /** - * The column number where this token ends. - * returns -1 if not available - * - * @return the end column number - */ + /** The column number where this token starts, inclusive. */ + public int getBeginColumn() { + return beginColumn; + } + + /** The column number where this token ends, exclusive. */ public int getEndColumn() { - return endColumn; // TODO Java 1.8 make optional + return endColumn; } int getIdentifier() { @@ -88,17 +94,17 @@ public class TokenEntry implements Comparable { @SuppressWarnings("PMD.CompareObjectsWithEquals") @Override public boolean equals(Object o) { - // make sure to recognize EOF regardless of hashCode (hashCode is irrelevant for EOF) if (this == o) { return true; - } else if (o == EOF || this == EOF) { - return false; - } - // any token except EOF - if (!(o instanceof TokenEntry)) { + } else if (!(o instanceof TokenEntry)) { return false; } TokenEntry other = (TokenEntry) o; + if (other.isEof() != this.isEof()) { + return false; + } else if (this.isEof()) { + return other.getFileName().equals(this.getFileName()); + } return other.hashCode == hashCode; } @@ -112,7 +118,7 @@ public class TokenEntry implements Comparable { } public String getImage(Tokens tokens) { - if (EOF.equals(this)) { + if (this.isEof()) { return "EOF"; } String image = tokens.imageFromId(this.identifier); @@ -121,7 +127,7 @@ public class TokenEntry implements Comparable { @Override public String toString() { - if (EOF.equals(this)) { + if (this.isEof()) { return "EOF"; } return Integer.toString(identifier); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java index 84d4204901..18d4a3d674 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java @@ -43,7 +43,7 @@ public interface TokenFactory extends AutoCloseable { @Override public void close() { - sink.addEof(); + sink.addEof(name); } }; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index b9b7fee8b2..ebc86025ca 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -15,13 +15,21 @@ public class Tokens { private final List tokens = new ArrayList<>(); private final Map images = new HashMap<>(); + // the first ID is 1, 0 is the ID of the EOF token. + private int curImageId = 1; private void add(TokenEntry tokenEntry) { this.tokens.add(tokenEntry); } - void addEof() { - add(TokenEntry.EOF); + void addEof(String fileName) { + if (tokens.isEmpty()) { + add(new TokenEntry(fileName, 1, 1)); + return; + } + + TokenEntry tok = peekLastToken(); + add(new TokenEntry(fileName, tok.getEndLine(), tok.getEndColumn())); } void setImage(TokenEntry entry, String newImage) { @@ -30,7 +38,7 @@ public class Tokens { } private int getImageId(String newImage) { - return images.computeIfAbsent(newImage, k -> images.size() + 1); + return images.computeIfAbsent(newImage, k -> curImageId++); } String imageFromId(int i) { @@ -57,14 +65,6 @@ public class Tokens { return get(mark.getIndex() + match.getTokenCount() - 1); } - public int getLineCount(TokenEntry mark, Match match) { - TokenEntry endTok = getEndToken(mark, match); - if (TokenEntry.EOF.equals(endTok)) { - endTok = get(mark.getIndex() + match.getTokenCount() - 2); - } - return endTok.getBeginLine() - mark.getBeginLine() + 1; - } - public List getTokens() { return tokens; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java index c5248c722b..47cf2c65a0 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java @@ -13,8 +13,7 @@ class TokenEntryTest { @Test void testSimple() { Tokens tokens = new Tokens(); - tokens.addToken("public", "/var/Foo.java", 1, 2, 3, 4); - TokenEntry mark = tokens.peekLastToken(); + TokenEntry mark = tokens.addToken("public", "/var/Foo.java", 1, 2, 3, 4); assertEquals(1, mark.getBeginLine()); assertEquals("/var/Foo.java", mark.getFileName()); assertEquals(0, mark.getIndex()); diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index 0532ae4de8..f73386a117 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -96,7 +96,7 @@ abstract class CpdTextComparisonTest( for (token in tokens.iterator()) { - if (token === TokenEntry.EOF) { + if (token.isEof) { append("EOF").appendLine() continue } From 8541fb75fd81197fd836117331c3f2204099bda4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 16:34:44 +0100 Subject: [PATCH 104/347] Fix pmd-core --- .../java/net/sourceforge/pmd/cpd/Mark.java | 10 +- .../sourceforge/pmd/cpd/AnyTokenizerTest.java | 6 +- .../sourceforge/pmd/cpd/CPDReportTest.java | 51 ++---- .../sourceforge/pmd/cpd/CSVRendererTest.java | 36 ++-- .../sourceforge/pmd/cpd/CpdAnalysisTest.java | 4 +- .../net/sourceforge/pmd/cpd/CpdTestUtils.java | 108 ++++++++++++ .../net/sourceforge/pmd/cpd/MarkTest.java | 11 +- .../net/sourceforge/pmd/cpd/MatchTest.java | 8 +- .../sourceforge/pmd/cpd/XMLRendererTest.java | 161 ++++++++---------- 9 files changed, 221 insertions(+), 174 deletions(-) create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index f9cd4ba93d..4de7b63016 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -28,11 +28,9 @@ public class Mark implements Comparable { /** * The column number where this duplication begins. - * returns -1 if not available - * @return the begin column number */ public int getBeginColumn() { - return this.token.getBeginColumn(); // TODO Java 1.8 make optional + return this.token.getBeginColumn(); } public int getBeginTokenIndex() { @@ -40,7 +38,7 @@ public class Mark implements Comparable { } public int getEndLine() { - return endToken == null ? getBeginLine() : endToken.getEndLine(); + return endToken == null ? token.getEndLine() : endToken.getEndLine(); } /** @@ -49,11 +47,11 @@ public class Mark implements Comparable { * @return the end column number */ public int getEndColumn() { - return this.endToken == null ? -1 : this.endToken.getEndColumn(); // TODO Java 1.8 make optional + return this.endToken == null ? token.getEndColumn() : this.endToken.getEndColumn(); } public int getEndTokenIndex() { - return this.endToken == null ? -1 : this.endToken.getIndex(); + return this.endToken == null ? this.token.getIndex() : this.endToken.getIndex(); } public int getLineCount() { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java index 18f6d2a76b..21e69823f3 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerTest.java @@ -35,13 +35,13 @@ class AnyTokenizerTest { AnyTokenizer tokenizer = new AnyTokenizer("//"); Tokens tokens = compareResult(tokenizer, "a = \"oo\n\";", listOf("a", "=", "\"oo\n\"", ";", "EOF")); TokenEntry string = tokens.getTokens().get(2); - assertEquals("\"oo\n\"", getTokenImage(string)); + assertEquals("\"oo\n\"", string.getImage(tokens)); assertEquals(1, string.getBeginLine()); assertEquals(5, string.getBeginColumn()); assertEquals(2, string.getEndColumn()); // ends on line 2 TokenEntry semi = tokens.getTokens().get(3); - assertEquals(";", getTokenImage(semi)); + assertEquals(";", semi.getImage(tokens)); assertEquals(2, semi.getBeginLine()); assertEquals(2, semi.getBeginColumn()); assertEquals(3, semi.getEndColumn()); @@ -70,7 +70,7 @@ class AnyTokenizerTest { List tokenStrings = new ArrayList<>(); for (TokenEntry token : tokens.getTokens()) { - tokenStrings.add(getTokenImage(token)); + tokenStrings.add(token.getImage(tokens)); } assertEquals(expectedImages, tokenStrings); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java index 1502d5f991..2254a41982 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java @@ -7,35 +7,25 @@ package net.sourceforge.pmd.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.cpd.CpdTestUtils.CpdReportBuilder; class CPDReportTest { - private Tokens tokens = new Tokens(); - @Test void testFilterMatches() { - List originalMatches = Arrays.asList( - createMatch("file1.java", "file2.java", 1), - createMatch("file1.java", "file3.java", 2), - createMatch("file2.java", "file3.java", 3)); - Map numberOfTokensPerFile = new HashMap<>(); - numberOfTokensPerFile.put("file1.java", 10); - numberOfTokensPerFile.put("file2.java", 15); - numberOfTokensPerFile.put("file3.java", 20); - CPDReport original = makeReport(originalMatches, numberOfTokensPerFile); + CpdReportBuilder reportBuilder = new CpdReportBuilder(); + reportBuilder.addMatch(createMatch(reportBuilder, "file1.java", "file2.java", 1)); + reportBuilder.addMatch(createMatch(reportBuilder, "file1.java", "file3.java", 2)); + reportBuilder.addMatch(createMatch(reportBuilder, "file2.java", "file3.java", 3)); + reportBuilder.recordNumTokens("file1.java", 10); + reportBuilder.recordNumTokens("file2.java", 15); + reportBuilder.recordNumTokens("file3.java", 20); + CPDReport original = reportBuilder.build(); assertEquals(3, original.getMatches().size()); @@ -62,26 +52,9 @@ class CPDReportTest { assertEquals(original.getNumberOfTokensPerFile(), filtered.getNumberOfTokensPerFile()); } - private Match createMatch(String file1, String file2, int line) { + private Match createMatch(CpdReportBuilder builder, String file1, String file2, int line) { return new Match(5, - tokens.addToken("firstToken", file1, line, 1, line, 1), - tokens.addToken("secondToken", file2, line, 2, line, 2)); - } - - static CPDReport makeReport(List matches) { - return makeReport(matches, Collections.emptyMap()); - } - - static CPDReport makeReport(List matches, Map numTokensPerFile) { - Set textFiles = new HashSet<>(); - for (Match match : matches) { - match.iterator().forEachRemaining( - mark -> textFiles.add(TextFile.forCharSeq("dummy content", mark.getFilename(), DummyLanguageModule.getInstance().getDefaultVersion()))); - } - return new CPDReport( - new SourceManager(new ArrayList<>(textFiles)), - matches, - numTokensPerFile - ); + builder.tokens.addToken("firstToken", file1, line, 1, line, 1), + builder.tokens.addToken("secondToken", file2, line, 2, line, 2)); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java index 818fb9bdb6..e14ef44c56 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java @@ -8,30 +8,27 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.cpd.CpdTestUtils.CpdReportBuilder; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; class CSVRendererTest { - private final Tokens tokens = new Tokens(); @Test void testLineCountPerFile() throws IOException { CPDReportRenderer renderer = new CSVRenderer(true); - List list = new ArrayList<>(); - Mark mark1 = createMark("public", "/var/Foo.java", 48); - Mark mark2 = createMark("stuff", "/var/Bar.java", 73); - Match match = new Match(75, mark1, mark2); + CpdReportBuilder builder = new CpdReportBuilder(); + Mark mark1 = builder.createMark("public", "/var/Foo.java", 48, 10); + Mark mark2 = builder.createMark("stuff", "/var/Bar.java", 73, 20); + builder.addMatch(new Match(75, mark1, mark2)); - list.add(match); StringWriter sw = new StringWriter(); - renderer.render(CPDReportTest.makeReport(list), sw); + renderer.render(builder.build(), sw); String report = sw.toString(); - String expectedReport = "tokens,occurrences" + PMD.EOL + "75,2,48,10,/var/Foo.java,73,20,/var/Bar.java" - + PMD.EOL; + String expectedReport = "tokens,occurrences" + PMD.EOL + + "75,2,48,10,/var/Foo.java,73,20,/var/Bar.java" + PMD.EOL; assertEquals(expectedReport, report); } @@ -39,22 +36,17 @@ class CSVRendererTest { @Test void testFilenameEscapes() throws IOException { CPDReportRenderer renderer = new CSVRenderer(); - List list = new ArrayList<>(); - Mark mark1 = createMark("public", "/var,with,commas/Foo.java", 48); - Mark mark2 = createMark("stuff", "/var,with,commas/Bar.java", 73); - Match match = new Match(75, mark1, mark2); - list.add(match); + CpdReportBuilder builder = new CpdReportBuilder(); + Mark mark1 = builder.createMark("public", "/var,with,commas/Foo.java", 48, 10); + Mark mark2 = builder.createMark("stuff", "/var,with,commas/Bar.java", 73, 20); + builder.addMatch(new Match(75, mark1, mark2)); StringWriter sw = new StringWriter(); - renderer.render(CPDReportTest.makeReport(list), sw); + renderer.render(builder.build(), sw); String report = sw.toString(); String expectedReport = "lines,tokens,occurrences" + PMD.EOL - + "10,75,2,48,\"/var,with,commas/Foo.java\",73,\"/var,with,commas/Bar.java\"" + PMD.EOL; + + "10,75,2,48,\"/var,with,commas/Foo.java\",73,\"/var,with,commas/Bar.java\"" + PMD.EOL; assertEquals(expectedReport, report); } - private Mark createMark(String image, String tokenSrcID, int beginLine) { - TokenEntry tok = tokens.addToken(image, tokenSrcID, beginLine, beginLine, beginLine, beginLine); - return new Mark(tok); - } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java index 4ee0b61541..3149dec90e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.cpd; +import static net.sourceforge.pmd.util.CollectionUtil.setOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -17,6 +18,7 @@ import org.apache.commons.lang3.SystemUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.PlainTextLanguage; /** @@ -31,7 +33,7 @@ class CpdAnalysisTest { // Symlinks are not well supported under Windows - so the tests are // simply executed only on linux. private boolean canTestSymLinks = SystemUtils.IS_OS_UNIX; - CPDConfiguration config = new CPDConfiguration(); + CPDConfiguration config = new CPDConfiguration(new LanguageRegistry(setOf(PlainTextLanguage.getInstance()))); @BeforeEach void setup() throws Exception { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java new file mode 100644 index 0000000000..2c7c83f725 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java @@ -0,0 +1,108 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.document.TextFile; + +class CpdTestUtils { + + static CPDReport makeReport(List matches) { + return makeReport(matches, Collections.emptyMap()); + } + + static CPDReport makeReport(List matches, Map numTokensPerFile) { + Set textFiles = new HashSet<>(); + for (Match match : matches) { + match.iterator().forEachRemaining( + mark -> textFiles.add(TextFile.forCharSeq(DUMMY_FILE_CONTENT, mark.getFilename(), DummyLanguageModule.getInstance().getDefaultVersion()))); + } + return new CPDReport( + new SourceManager(new ArrayList<>(textFiles)), + matches, + numTokensPerFile + ); + } + + static class CpdReportBuilder { + + private final Map fileContents = new HashMap<>(); + final Tokens tokens = new Tokens(); + private final List matches = new ArrayList<>(); + private Map numTokensPerFile = new HashMap<>(); + + CpdReportBuilder setFileContent(String fileName, String content) { + fileContents.put(fileName, content); + return this; + } + + public CpdReportBuilder setNumTokensPerFile(Map numTokensPerFile) { + this.numTokensPerFile = numTokensPerFile; + return this; + } + + public CpdReportBuilder recordNumTokens(String fileName, int numTokens) { + this.numTokensPerFile.put(fileName, numTokens); + return this; + } + + CPDReport build() { + Set textFiles = new HashSet<>(); + fileContents.forEach((fname, contents) -> textFiles.add(TextFile.forCharSeq(contents, fname, DummyLanguageModule.getInstance().getDefaultVersion()))); + return new CPDReport( + new SourceManager(new ArrayList<>(textFiles)), + matches, + numTokensPerFile + ); + + } + + Mark createMark(String image, String fileName, int beginLine, int lineCount) { + fileContents.putIfAbsent(fileName, DUMMY_FILE_CONTENT); + return new Mark(tokens.addToken(image, fileName, beginLine, 1, beginLine + lineCount - 1, 1)); + } + + CpdReportBuilder addMatch(Match match) { + fileContents.putIfAbsent(match.getFirstMark().getFilename(), DUMMY_FILE_CONTENT); + matches.add(match); + return this; + } + + Mark createMark(String image, String fileName, int beginLine, int lineCount, int beginColumn, int endColumn) { + fileContents.putIfAbsent(fileName, DUMMY_FILE_CONTENT); + final TokenEntry beginToken = tokens.addToken(image, fileName, beginLine, beginColumn, beginLine, + beginColumn + image.length()); + final TokenEntry endToken = tokens.addToken(image, fileName, + beginLine + lineCount - 1, beginColumn, + beginLine + lineCount - 1, endColumn); + final Mark result = new Mark(beginToken); + + result.setEndToken(endToken); + return result; + } + + } + + public static final String DUMMY_FILE_CONTENT = generateDummyContent(60); + + static String generateDummyContent(int lengthInLines) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < lengthInLines; i++) { + for (int j = 0; j < 10; j++) { + sb.append(i).append("_"); + } + sb.append("\n"); + } + return sb.toString(); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java index c2c0869c7b..2335d382a4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java @@ -14,16 +14,15 @@ class MarkTest { void testSimple() { String filename = "/var/Foo.java"; Tokens tokens = new Tokens(); - int beginLine = 1; - TokenEntry token = tokens.addToken("public", filename, beginLine, 2, 3, 4); + TokenEntry token = tokens.addToken("public", filename, 1, 2, 3, 4); Mark mark = new Mark(token); assertEquals(token, mark.getToken()); assertEquals(filename, mark.getFilename()); - assertEquals(beginLine, mark.getBeginLine()); - assertEquals(1, mark.getLineCount()); - assertEquals(beginLine, mark.getEndLine()); + assertEquals(1, mark.getBeginLine()); + assertEquals(3, mark.getLineCount()); + assertEquals(3, mark.getEndLine()); assertEquals(2, mark.getBeginColumn()); assertEquals(4, mark.getEndColumn()); } @@ -39,7 +38,7 @@ class MarkTest { TokenEntry token = tokens.addToken("public", filename, beginLine, beginColumn, beginLine, beginColumn + "public".length()); TokenEntry endToken = tokens.addToken("}", filename, - beginLine + lineCount, 1, beginLine + lineCount, endColumn); + beginLine + lineCount, 1, beginLine + lineCount - 1, endColumn); final Mark mark = new Mark(token); mark.setEndToken(endToken); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java index 6f3c5df07a..9b3ff8e06e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java @@ -34,8 +34,8 @@ class MatchTest { assertEquals(1, match.getTokenCount()); // Returns the line count of the first mark assertEquals(1, match.getLineCount()); - // Returns the source code of the first mark - assertEquals(Chars.wrap("123456"), sourceManager.getSlice(match.getFirstMark())); + // Returns the source code of the first mark (the entire line) + assertEquals(Chars.wrap("1234567890"), sourceManager.getSlice(match.getFirstMark())); Iterator i = match.iterator(); Mark occurrence1 = i.next(); Mark occurrence2 = i.next(); @@ -44,11 +44,11 @@ class MatchTest { assertEquals(mark1, occurrence1); assertEquals(1, occurrence1.getLineCount()); - assertEquals(Chars.wrap("123456"), sourceManager.getSlice(mark1)); + assertEquals(Chars.wrap("1234567890"), sourceManager.getSlice(mark1)); assertEquals(mark2, occurrence2); assertEquals(1, occurrence2.getLineCount()); - assertEquals(Chars.wrap("123456"), sourceManager.getSlice(mark2)); + assertEquals(Chars.wrap("1234567890"), sourceManager.getSlice(mark2)); } @Test diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index 1dacc25ca9..1a481f8ecc 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.cpd; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,11 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringWriter; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -26,6 +24,7 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import net.sourceforge.pmd.cpd.CpdTestUtils.CpdReportBuilder; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; /** @@ -39,13 +38,11 @@ class XMLRendererTest { private static final String FORM_FEED = "\u000C"; // this character is invalid in XML 1.0 documents private static final String FORM_FEED_ENTITY = " "; // this is also not allowed in XML 1.0 documents - Tokens tokens = new Tokens(); - @Test void testWithNoDuplication() throws IOException, ParserConfigurationException, SAXException { CPDReportRenderer renderer = new XMLRenderer(); StringWriter sw = new StringWriter(); - renderer.render(CPDReportTest.makeReport(Collections.emptyList()), sw); + renderer.render(CpdTestUtils.makeReport(Collections.emptyList()), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() @@ -59,20 +56,18 @@ class XMLRendererTest { @Test void testWithOneDuplication() throws Exception { CPDReportRenderer renderer = new XMLRenderer(); - List list = new ArrayList<>(); + CpdReportBuilder builder = new CpdReportBuilder(); int lineCount = 6; - String codeFragment = "code\nfragment"; - Mark mark1 = createMark("public", "/var/Foo.java", 1, lineCount); - Mark mark2 = createMark("stuff", "/var/Foo.java", 73, lineCount); - Match match = new Match(75, mark1, mark2); + Mark mark1 = builder.createMark("public", "/var/Foo.java", 1, lineCount); + Mark mark2 = builder.createMark("stuff", "/var/Foo.java", 73, lineCount); + builder.addMatch(new Match(75, mark1, mark2)); - list.add(match); StringWriter sw = new StringWriter(); - renderer.render(CPDReportTest.makeReport(list), sw); + renderer.render(builder.build(), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); + .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); NodeList dupes = doc.getElementsByTagName("duplication"); assertEquals(1, dupes.getLength()); Node file = dupes.item(0).getFirstChild(); @@ -83,8 +78,8 @@ class XMLRendererTest { assertEquals("1", file.getAttributes().getNamedItem("line").getNodeValue()); assertEquals("/var/Foo.java", file.getAttributes().getNamedItem("path").getNodeValue()); assertEquals("6", file.getAttributes().getNamedItem("endline").getNodeValue()); - assertEquals(null, file.getAttributes().getNamedItem("column")); - assertEquals(null, file.getAttributes().getNamedItem("endcolumn")); + assertEquals("1", file.getAttributes().getNamedItem("column").getNodeValue()); + assertEquals("1", file.getAttributes().getNamedItem("endcolumn").getNodeValue()); file = file.getNextSibling(); while (file != null && file.getNodeType() != Node.ELEMENT_NODE) { file = file.getNextSibling(); @@ -93,35 +88,33 @@ class XMLRendererTest { if (file != null) { assertEquals("73", file.getAttributes().getNamedItem("line").getNodeValue()); assertEquals("78", file.getAttributes().getNamedItem("endline").getNodeValue()); - assertEquals(null, file.getAttributes().getNamedItem("column")); - assertEquals(null, file.getAttributes().getNamedItem("endcolumn")); + assertEquals("1", file.getAttributes().getNamedItem("column").getNodeValue()); + assertEquals("1", file.getAttributes().getNamedItem("endcolumn").getNodeValue()); } assertEquals(1, doc.getElementsByTagName("codefragment").getLength()); - assertEquals(codeFragment, doc.getElementsByTagName("codefragment").item(0).getTextContent()); + assertEquals(CpdTestUtils.generateDummyContent(lineCount), doc.getElementsByTagName("codefragment").item(0).getTextContent()); } @Test void testRenderWithMultipleMatch() throws Exception { CPDReportRenderer renderer = new XMLRenderer(); - List list = new ArrayList<>(); + CpdReportBuilder builder = new CpdReportBuilder(); int lineCount1 = 6; - Mark mark1 = createMark("public", "/var/Foo.java", 48, lineCount1); - Mark mark2 = createMark("void", "/var/Foo.java", 73, lineCount1); - Match match1 = new Match(75, mark1, mark2); + Mark mark1 = builder.createMark("public", "/var/Foo.java", 48, lineCount1); + Mark mark2 = builder.createMark("void", "/var/Foo.java", 73, lineCount1); + builder.addMatch(new Match(75, mark1, mark2)); int lineCount2 = 7; - Mark mark3 = createMark("void", "/var/Foo2.java", 49, lineCount2); - Mark mark4 = createMark("stuff", "/var/Foo2.java", 74, lineCount2); - Match match2 = new Match(76, mark3, mark4); + Mark mark3 = builder.createMark("void", "/var/Foo2.java", 49, lineCount2); + Mark mark4 = builder.createMark("stuff", "/var/Foo2.java", 74, lineCount2); + builder.addMatch(new Match(76, mark3, mark4)); - list.add(match1); - list.add(match2); StringWriter sw = new StringWriter(); - renderer.render(CPDReportTest.makeReport(list), sw); + renderer.render(builder.build(), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); + .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); assertEquals(2, doc.getElementsByTagName("duplication").getLength()); assertEquals(4, doc.getElementsByTagName("file").getLength()); } @@ -129,20 +122,18 @@ class XMLRendererTest { @Test void testWithOneDuplicationWithColumns() throws Exception { CPDReportRenderer renderer = new XMLRenderer(); - List list = new ArrayList<>(); - int lineCount = 6; - String codeFragment = "code\nfragment"; - Mark mark1 = createMark("public", "/var/Foo.java", 1, lineCount, codeFragment, 2, 3); - Mark mark2 = createMark("stuff", "/var/Foo.java", 73, lineCount, codeFragment, 4, 5); - Match match = new Match(75, mark1, mark2); + int lineCount = 2; + CpdReportBuilder builder = new CpdReportBuilder(); + Mark mark1 = builder.createMark("public", "/var/Foo.java", 1, lineCount, 2, 3); + Mark mark2 = builder.createMark("stuff", "/var/Foo.java", 24, lineCount, 4, 5); + builder.addMatch(new Match(75, mark1, mark2)); - list.add(match); StringWriter sw = new StringWriter(); - renderer.render(CPDReportTest.makeReport(list), sw); + renderer.render(builder.build(), sw); String report = sw.toString(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); + .parse(new ByteArrayInputStream(report.getBytes(ENCODING))); NodeList dupes = doc.getElementsByTagName("duplication"); assertEquals(1, dupes.getLength()); Node file = dupes.item(0).getFirstChild(); @@ -152,7 +143,7 @@ class XMLRendererTest { if (file != null) { assertEquals("1", file.getAttributes().getNamedItem("line").getNodeValue()); assertEquals("/var/Foo.java", file.getAttributes().getNamedItem("path").getNodeValue()); - assertEquals("6", file.getAttributes().getNamedItem("endline").getNodeValue()); + assertEquals("2", file.getAttributes().getNamedItem("endline").getNodeValue()); assertEquals("2", file.getAttributes().getNamedItem("column").getNodeValue()); assertEquals("3", file.getAttributes().getNamedItem("endcolumn").getNodeValue()); file = file.getNextSibling(); @@ -161,27 +152,26 @@ class XMLRendererTest { } } if (file != null) { - assertEquals("73", file.getAttributes().getNamedItem("line").getNodeValue()); - assertEquals("78", file.getAttributes().getNamedItem("endline").getNodeValue()); + assertEquals("24", file.getAttributes().getNamedItem("line").getNodeValue()); + assertEquals("25", file.getAttributes().getNamedItem("endline").getNodeValue()); assertEquals("4", file.getAttributes().getNamedItem("column").getNodeValue()); assertEquals("5", file.getAttributes().getNamedItem("endcolumn").getNodeValue()); } assertEquals(1, doc.getElementsByTagName("codefragment").getLength()); - assertEquals(codeFragment, doc.getElementsByTagName("codefragment").item(0).getTextContent()); + assertEquals(CpdTestUtils.generateDummyContent(2), doc.getElementsByTagName("codefragment").item(0).getTextContent()); } @Test void testRendererEncodedPath() throws IOException { CPDReportRenderer renderer = new XMLRenderer(); - List list = new ArrayList<>(); + CpdReportBuilder builder = new CpdReportBuilder(); final String espaceChar = "<"; - Mark mark1 = createMark("public", "/var/A matches = new ArrayList<>(); + CpdReportBuilder builder = new CpdReportBuilder(); final String filename = "/var/Foo.java"; - final int lineCount = 6; - final String codeFragment = "code\nfragment"; - final Mark mark1 = createMark("public", filename, 1, lineCount, codeFragment, 2, 3); - final Mark mark2 = createMark("stuff", filename, 73, lineCount, codeFragment, 4, 5); - final Match match = new Match(75, mark1, mark2); - matches.add(match); - final Map numberOfTokensPerFile = new HashMap<>(); - numberOfTokensPerFile.put(filename, 888); - final CPDReport report = CPDReportTest.makeReport(matches, numberOfTokensPerFile); + final int lineCount = 2; + final Mark mark1 =builder. createMark("public", filename, 1, lineCount, 2, 3); + final Mark mark2 =builder. createMark("stuff", filename, 3, lineCount, 4, 5); + builder.addMatch(new Match(75, mark1, mark2)); + builder.recordNumTokens(filename, 888); + + final CPDReport report = builder.build(); + final StringWriter writer = new StringWriter(); renderer.render(report, writer); final String xmlOutput = writer.toString(); @@ -217,22 +206,21 @@ class XMLRendererTest { @Test void testGetDuplicationStartEnd() throws IOException, ParserConfigurationException, SAXException { final CPDReportRenderer renderer = new XMLRenderer(); - final List matches = new ArrayList<>(); + CpdReportBuilder builder = new CpdReportBuilder(); final String filename = "/var/Foo.java"; final int lineCount = 6; - final String codeFragment = "code\nfragment"; - final Mark mark1 = createMark("public", filename, 1, lineCount, codeFragment, 2, 3); - final Mark mark2 = createMark("stuff", filename, 73, lineCount, codeFragment, 4, 5); - final Match match = new Match(75, mark1, mark2); - matches.add(match); - final Map numberOfTokensPerFile = new HashMap<>(); - numberOfTokensPerFile.put(filename, 888); - final CPDReport report = CPDReportTest.makeReport(matches, numberOfTokensPerFile); + final Mark mark1 =builder. createMark("public", filename, 1, lineCount, 2, 3); + final Mark mark2 =builder. createMark("stuff", filename, 73, lineCount, 4, 5); + builder.addMatch(new Match(75, mark1, mark2)); + builder.recordNumTokens(filename, 888); + + final CPDReport report = builder.build(); + final StringWriter writer = new StringWriter(); renderer.render(report, writer); final String xmlOutput = writer.toString(); final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .parse(new ByteArrayInputStream(xmlOutput.getBytes(ENCODING))); + .parse(new ByteArrayInputStream(xmlOutput.getBytes(ENCODING))); final NodeList files = doc.getElementsByTagName("file"); final Node dup_1 = files.item(1); final NamedNodeMap attrs_1 = dup_1.getAttributes(); @@ -247,36 +235,23 @@ class XMLRendererTest { @Test void testRendererXMLEscaping() throws IOException { + String codefragment = "code fragment" + FORM_FEED + + "\nline2\nline3\nno & escaping necessary in CDATA\nx=\"]]>\";"; CPDReportRenderer renderer = new XMLRenderer(); - List list = new ArrayList<>(); - Mark mark1 = createMark("public", "file1", 1, 5); - Mark mark2 = createMark("public", "file2", 5, 5); + CpdReportBuilder builder = new CpdReportBuilder(); + Mark mark1 = builder.createMark("public", "file1", 1, 5); + Mark mark2 = builder.createMark("public", "file2", 5, 5); Match match1 = new Match(75, mark1, mark2); - list.add(match1); + builder.addMatch(match1); + builder.setFileContent("file1", codefragment); StringWriter sw = new StringWriter(); - renderer.render(CPDReportTest.makeReport(list), sw); + renderer.render(builder.build(), sw); String report = sw.toString(); assertFalse(report.contains(FORM_FEED)); assertFalse(report.contains(FORM_FEED_ENTITY)); - assertTrue(report.contains("no & escaping necessary in CDATA")); + assertThat(report, containsString("no & escaping necessary in CDATA")); + assertThat(report, containsString("x=\"]]]]>\";")); assertFalse(report.contains("x=\"]]>\";")); // must be escaped - assertTrue(report.contains("x=\"]]]]>\";")); - } - - private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount) { - return new Mark(tokens.addToken(image, tokenSrcID, beginLine, 1, beginLine + lineCount, 1)); - } - - private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code, int beginColumn, int endColumn) { - final TokenEntry beginToken = tokens.addToken(image, tokenSrcID, beginLine, beginColumn, beginLine, - beginColumn + image.length()); - final TokenEntry endToken = tokens.addToken(image, tokenSrcID, - beginLine + lineCount, beginColumn, - beginLine + lineCount, endColumn); - final Mark result = new Mark(beginToken); - - result.setEndToken(endToken); - return result; } } From 8fbd830daaa03b435ff4fe0eb57368b997fcac9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 17:18:57 +0100 Subject: [PATCH 105/347] Style and renamings --- .../java/net/sourceforge/pmd/ant/CPDTask.java | 8 +- .../pmd/cli/commands/internal/CpdCommand.java | 8 +- .../net/sourceforge/pmd/cpd/AnyLanguage.java | 11 --- .../sourceforge/pmd/cpd/CPDConfiguration.java | 27 +----- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 5 +- .../java/net/sourceforge/pmd/cpd/GUI.java | 4 +- .../sourceforge/pmd/cpd/LanguageFactory.java | 92 ------------------- .../sourceforge/pmd/cpd/MatchAlgorithm.java | 4 +- .../sourceforge/pmd/cpd/SourceManager.java | 3 +- .../net/sourceforge/pmd/cpd/Tokenizer.java | 2 +- .../pmd/cpd/internal/TokenizerBase.java | 4 +- .../sourceforge/pmd/cpd/CpdAnalysisTest.java | 1 - .../net/sourceforge/pmd/cpd/CpdTestUtils.java | 6 +- .../sourceforge/pmd/cpd/CpddummyLanguage.java | 16 ---- .../pmd/cpd/LanguageFactoryTest.java | 18 ---- .../sourceforge/pmd/cpd/XMLRendererTest.java | 11 +-- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net/sourceforge/pmd/cpd/DartLanguage.java | 18 ---- .../pmd/lang/dart/DartLanguageModule.java | 28 ++++++ .../{ => lang/dart}/cpd/DartTokenizer.java | 4 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../dart}/cpd/DartTokenizerTest.java | 4 +- .../{ => lang/dart}/cpd/testdata/comment.dart | 0 .../{ => lang/dart}/cpd/testdata/comment.txt | 0 .../dart}/cpd/testdata/escape_sequences.dart | 0 .../dart}/cpd/testdata/escape_sequences.txt | 0 .../dart}/cpd/testdata/escaped_backslash.dart | 0 .../dart}/cpd/testdata/escaped_backslash.txt | 0 .../dart}/cpd/testdata/escaped_dollar.dart | 0 .../dart}/cpd/testdata/escaped_dollar.txt | 0 .../dart}/cpd/testdata/escaped_string.dart | 0 .../dart}/cpd/testdata/escaped_string.txt | 0 .../{ => lang/dart}/cpd/testdata/imports.dart | 0 .../{ => lang/dart}/cpd/testdata/imports.txt | 0 .../dart}/cpd/testdata/increment.dart | 0 .../dart}/cpd/testdata/increment.txt | 0 .../{ => lang/dart}/cpd/testdata/regex.dart | 0 .../{ => lang/dart}/cpd/testdata/regex.txt | 0 .../{ => lang/dart}/cpd/testdata/regex2.dart | 0 .../{ => lang/dart}/cpd/testdata/regex2.txt | 0 .../{ => lang/dart}/cpd/testdata/regex3.dart | 0 .../{ => lang/dart}/cpd/testdata/regex3.txt | 0 .../cpd/testdata/string_interpolation.dart | 0 .../cpd/testdata/string_interpolation.txt | 0 .../dart}/cpd/testdata/string_multiline.dart | 0 .../dart}/cpd/testdata/string_multiline.txt | 0 .../cpd/testdata/string_with_backslashes.dart | 0 .../cpd/testdata/string_with_backslashes.txt | 0 .../dart}/cpd/testdata/tabWidth.dart | 0 .../{ => lang/dart}/cpd/testdata/tabWidth.txt | 0 .../sourceforge/pmd/cpd/JavaTokenizer.java | 2 +- .../pmd/cpd/MatchAlgorithmTest.java | 2 +- .../pmd/cpd/EcmascriptLanguage.java | 15 --- .../ecmascript/EcmascriptLanguageModule.java | 8 ++ .../ecmascript}/cpd/EcmascriptTokenizer.java | 2 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../cpd/AnyTokenizerForTypescriptTest.java | 5 +- .../cpd/EcmascriptTokenizerTest.java | 12 +-- .../cpd/testdata/ts/SampleTypeScript.ts | 0 .../cpd/testdata/ts/SampleTypeScript.txt | 0 .../sourceforge/pmd/cpd/PLSQLTokenizer.java | 2 +- .../pmd/lang/plsql/PLSQLLanguageModule.java | 2 +- 63 files changed, 83 insertions(+), 245 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyLanguage.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpddummyLanguage.java delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/cpd/LanguageFactoryTest.java delete mode 100644 pmd-core/src/test/resources/META-INF/services/net.sourceforge.pmd.cpd.Language delete mode 100644 pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartLanguage.java create mode 100644 pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java rename pmd-dart/src/main/java/net/sourceforge/pmd/{ => lang/dart}/cpd/DartTokenizer.java (98%) delete mode 100644 pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-dart/src/test/java/net/sourceforge/pmd/{ => lang/dart}/cpd/DartTokenizerTest.java (94%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/comment.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/comment.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escape_sequences.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escape_sequences.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escaped_backslash.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escaped_backslash.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escaped_dollar.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escaped_dollar.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escaped_string.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/escaped_string.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/imports.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/imports.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/increment.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/increment.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/regex.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/regex.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/regex2.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/regex2.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/regex3.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/regex3.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/string_interpolation.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/string_interpolation.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/string_multiline.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/string_multiline.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/string_with_backslashes.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/string_with_backslashes.txt (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/tabWidth.dart (100%) rename pmd-dart/src/test/resources/net/sourceforge/pmd/{ => lang/dart}/cpd/testdata/tabWidth.txt (100%) delete mode 100644 pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptLanguage.java rename pmd-javascript/src/main/java/net/sourceforge/pmd/{ => lang/ecmascript}/cpd/EcmascriptTokenizer.java (95%) delete mode 100644 pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language rename pmd-javascript/src/test/java/net/sourceforge/pmd/{ => lang/ecmascript}/cpd/AnyTokenizerForTypescriptTest.java (72%) rename pmd-javascript/src/test/java/net/sourceforge/pmd/{ => lang/ecmascript}/cpd/EcmascriptTokenizerTest.java (85%) rename pmd-javascript/src/test/resources/net/sourceforge/pmd/{ => lang/ecmascript}/cpd/testdata/ts/SampleTypeScript.ts (100%) rename pmd-javascript/src/test/resources/net/sourceforge/pmd/{ => lang/ecmascript}/cpd/testdata/ts/SampleTypeScript.txt (100%) diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index db05ae2ab7..b18493e011 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -13,7 +13,6 @@ import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.apache.tools.ant.BuildException; @@ -27,11 +26,12 @@ import net.sourceforge.pmd.cpd.CPDConfiguration; import net.sourceforge.pmd.cpd.CPDReport; import net.sourceforge.pmd.cpd.CSVRenderer; import net.sourceforge.pmd.cpd.CpdAnalysis; -import net.sourceforge.pmd.cpd.LanguageFactory; import net.sourceforge.pmd.cpd.SimpleRenderer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.XMLRenderer; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; /** * CPD Ant task. Setters of this class are interpreted by Ant as properties @@ -184,9 +184,9 @@ public class CPDTask extends Task { throw new BuildException("Must include at least one FileSet"); } - if (!Arrays.asList(LanguageFactory.supportedLanguages).contains(language)) { + if (LanguageRegistry.CPD.getLanguageById(language) == null) { throw new BuildException("Language " + language + " is not supported. Available languages: " - + Arrays.toString(LanguageFactory.supportedLanguages)); + + LanguageRegistry.CPD.commaSeparatedList(Language::getId)); } } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index 46f1a8335c..d4d91786bc 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.cli.commands.internal; import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; @@ -116,11 +115,6 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { configuration.setSourceEncoding(encoding.getEncoding().name()); configuration.setURI(uri); - configuration.postContruct(); - // Pass extra parameters as System properties to allow language - // implementation to retrieve their associate values... - CPDConfiguration.setSystemProperties(configuration); - return configuration; } @@ -154,7 +148,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { @Override public Iterator iterator() { - return Arrays.stream(CPDConfiguration.getRenderers()).iterator(); + return CPDConfiguration.getRenderers().stream().sorted().iterator(); } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyLanguage.java deleted file mode 100644 index 234ab1bee1..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyLanguage.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -public class AnyLanguage extends AbstractLanguage { - public AnyLanguage(String... extensions) { - super("Any Language", "any", new AnyTokenizer(), extensions); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 576d460c4c..520b72b2ec 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -10,11 +10,12 @@ import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import org.slf4j.LoggerFactory; @@ -92,17 +93,6 @@ public class CPDConfiguration extends AbstractConfiguration { super(languageRegistry, new SimpleMessageReporter(LoggerFactory.getLogger(CpdAnalysis.class))); } - public void postContruct() { - if (getRendererName() == null) { - setRendererName(DEFAULT_RENDERER); - } - if (this.cpdReportRenderer == null) { - //may throw - CPDReportRenderer renderer = createRendererByName(getRendererName(), getSourceEncoding().name()); - setRenderer(renderer); - } - } - static CPDReportRenderer createRendererByName(String name, String encoding) { if (name == null || "".equals(name)) { name = DEFAULT_RENDERER; @@ -147,19 +137,10 @@ public class CPDConfiguration extends AbstractConfiguration { } } - public static String[] getRenderers() { - String[] result = RENDERERS.keySet().toArray(new String[0]); - Arrays.sort(result); - return result; + public static Set getRenderers() { + return Collections.unmodifiableSet(RENDERERS.keySet()); } - - - public static void setSystemProperties(CPDConfiguration configuration) { - - } - - public void setLanguage(net.sourceforge.pmd.lang.Language language) { setForceLanguageVersion(language.getDefaultVersion()); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 5d65b8aaac..0d8da039f6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -73,7 +73,7 @@ public final class CpdAnalysis implements AutoCloseable { private void setLanguageProperties(Language language, CPDConfiguration configuration) { LanguagePropertyBundle props = configuration.getLanguageProperties(language); - setPropertyIfMissing(Tokenizer.CPD_ANONYMiZE_LITERALS, props, configuration.isIgnoreLiterals()); + setPropertyIfMissing(Tokenizer.CPD_ANONYMIZE_LITERALS, props, configuration.isIgnoreLiterals()); setPropertyIfMissing(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS, props, configuration.isIgnoreIdentifiers()); setPropertyIfMissing(Tokenizer.CPD_IGNORE_METADATA, props, configuration.isIgnoreAnnotations()); setPropertyIfMissing(Tokenizer.CPD_IGNORE_IMPORTS, props, configuration.isIgnoreUsings()); @@ -128,6 +128,7 @@ public final class CpdAnalysis implements AutoCloseable { performAnalysis(r -> { }); } + @SuppressWarnings("PMD.CloseResource") public void performAnalysis(Consumer consumer) { try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) { @@ -148,7 +149,7 @@ public final class CpdAnalysis implements AutoCloseable { numberOfTokensPerFile.put(textDocument.getPathId(), newTokens); listener.addedFile(1); } catch (TokenMgrError | IOException e) { - if (e instanceof TokenMgrError) { + if (e instanceof TokenMgrError) { // NOPMD ((TokenMgrError) e).setFileName(textFile.getDisplayName()); } reporter.errorEx("Error while lexing.", e); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index e600e74854..5b6ea12628 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -87,7 +87,7 @@ public class GUI implements CPDListener { } public boolean canIgnoreLiterals() { - return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_ANONYMiZE_LITERALS); + return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_ANONYMIZE_LITERALS); } public boolean canIgnoreAnnotations() { @@ -633,8 +633,6 @@ public class GUI implements CPDListener { Language language = conf.getLanguage(); config.setLanguage(language); - CPDConfiguration.setSystemProperties(config); - try (CpdAnalysis cpd = new CpdAnalysis(config)) { cpd.setCpdListener(this); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java deleted file mode 100644 index ed426d346e..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.Comparator; -import java.util.Locale; -import java.util.Properties; - -import net.sourceforge.pmd.internal.LanguageServiceBase; - -public final class LanguageFactory extends LanguageServiceBase { - - public static final String EXTENSION = "extension"; - public static final String BY_EXTENSION = "by_extension"; - - private static final Comparator LANGUAGE_COMPARATOR = new Comparator() { - @Override - public int compare(Language o1, Language o2) { - return o1.getTerseName().compareToIgnoreCase(o2.getTerseName()); - } - }; - - private static final NameExtractor NAME_EXTRACTOR = new NameExtractor() { - @Override - public String getName(Language language) { - return language.getName().toLowerCase(Locale.ROOT); - } - }; - - private static final NameExtractor TERSE_NAME_EXTRACTOR = new NameExtractor() { - @Override - public String getName(Language language) { - return language.getTerseName().toLowerCase(Locale.ROOT); - } - }; - - // Important: the "instance" needs to be defined *after* LANGUAGE_COMPARATOR and *NAME_EXTRACTOR - // as these are needed in the constructor. - private static final LanguageFactory INSTANCE = new LanguageFactory(); - - public static String[] supportedLanguages; - - static { - supportedLanguages = INSTANCE.languagesByTerseName.keySet().toArray(new String[0]); - } - - private LanguageFactory() { - super(Language.class, LANGUAGE_COMPARATOR, NAME_EXTRACTOR, TERSE_NAME_EXTRACTOR); - } - - public static Language createLanguage(String language) { - return createLanguage(language, new Properties()); - } - - public static Language createLanguage(String language, Properties properties) { - Language implementation; - if (BY_EXTENSION.equals(language)) { - implementation = INSTANCE.getLanguageByExtension(properties.getProperty(EXTENSION)); - } else { - implementation = INSTANCE.languagesByTerseName.get(INSTANCE.languageAliases(language).toLowerCase(Locale.ROOT)); - } - if (implementation == null) { - // No proper implementation - // FIXME: We should log a warning, shouldn't we ? - implementation = new AnyLanguage(language); - } - implementation.setProperties(properties); - return implementation; - } - - private String languageAliases(String language) { - // CPP and C language share the same parser - if ("c".equals(language)) { - return "cpp"; - } - return language; - } - - private Language getLanguageByExtension(String extension) { - Language result = null; - - for (Language language : languages) { - if (language.getExtensions().contains(extension)) { - result = language; - break; - } - } - return result; - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java index 363eec7e46..80dab17a83 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java @@ -25,8 +25,8 @@ class MatchAlgorithm { private final int min; MatchAlgorithm(Tokens tokens, int min) { - this(tokens, min, new CPDNullListener()); - } + this(tokens, min, new CPDNullListener()); + } MatchAlgorithm(Tokens tokens, int min, @NonNull CPDListener listener) { this.tokens = tokens; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index 5ae331cfe1..c37757e7a7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -24,7 +24,7 @@ class SourceManager implements AutoCloseable { private final Map fileByName = new HashMap<>(); private final List textFiles; - public SourceManager(List files) { + SourceManager(List files) { textFiles = new ArrayList<>(files); files.forEach(f -> fileByName.put(f.getPathId(), f)); } @@ -59,6 +59,7 @@ class SourceManager implements AutoCloseable { } } + @SuppressWarnings("PMD.CloseResource") public Chars getSlice(Mark mark) { TextFile textFile = fileByName.get(mark.getFilename()); assert textFile != null; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index be20bf6570..63645db659 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -18,7 +18,7 @@ public interface Tokenizer { .desc("Ignore sequences of literals, eg `0, 0, 0, 0`") .build(); - PropertyDescriptor CPD_ANONYMiZE_LITERALS = + PropertyDescriptor CPD_ANONYMIZE_LITERALS = PropertyFactory.booleanProperty("cpdAnonymizeLiterals") .defaultValue(false) .desc("Anonymize literals. They are still part of the token stream but all literals appear to have the same value.") diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java index 4d3d409b0f..1cd04359d1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java @@ -22,7 +22,7 @@ public abstract class TokenizerBase> implements Tokeni } protected void processToken(TokenFactory tokenEntries, T currentToken) { - tokenEntries.recordToken(getImage(currentToken), currentToken.getReportLocation()); + tokenEntries.recordToken(getImage(currentToken), currentToken.getReportLocation()); } protected String getImage(T token) { @@ -30,7 +30,7 @@ public abstract class TokenizerBase> implements Tokeni } @Override - public void tokenize(TextDocument document, TokenFactory tokens) throws IOException { + public final void tokenize(TextDocument document, TokenFactory tokens) throws IOException { TokenManager tokenManager = filterTokenStream(makeLexerImpl(document)); T currentToken = tokenManager.getNextToken(); while (currentToken != null) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java index 3149dec90e..3a200c1713 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java @@ -39,7 +39,6 @@ class CpdAnalysisTest { void setup() throws Exception { config.setLanguage(PlainTextLanguage.getInstance()); config.setMinimumTileSize(10); - config.postContruct(); } /** diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java index 2c7c83f725..b33b95201a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java @@ -15,7 +15,11 @@ import java.util.Set; import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.document.TextFile; -class CpdTestUtils { +final class CpdTestUtils { + + private CpdTestUtils() { + // utility class + } static CPDReport makeReport(List matches) { return makeReport(matches, Collections.emptyMap()); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpddummyLanguage.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpddummyLanguage.java deleted file mode 100644 index 9feec747fe..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpddummyLanguage.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Sample language for testing LanguageFactory. - * - */ -public class CpddummyLanguage extends AbstractLanguage { - - public CpddummyLanguage() { - super("CPD Dummy Language used in tests", "Cpddummy", new AnyTokenizer(), "dummy"); - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/LanguageFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/LanguageFactoryTest.java deleted file mode 100644 index cb840b3aba..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/LanguageFactoryTest.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class LanguageFactoryTest { - - @Test - void testSimple() { - assertTrue(LanguageFactory.createLanguage("Cpddummy") instanceof CpddummyLanguage); - assertTrue(LanguageFactory.createLanguage("not_existing_language") instanceof AnyLanguage); - } -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index 1a481f8ecc..0b6310c0f3 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -30,7 +30,6 @@ import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; /** * @author Philippe T'Seyen * @author Romain Pelisse <belaran@gmail.com> - * */ class XMLRendererTest { @@ -184,8 +183,8 @@ class XMLRendererTest { CpdReportBuilder builder = new CpdReportBuilder(); final String filename = "/var/Foo.java"; final int lineCount = 2; - final Mark mark1 =builder. createMark("public", filename, 1, lineCount, 2, 3); - final Mark mark2 =builder. createMark("stuff", filename, 3, lineCount, 4, 5); + final Mark mark1 = builder.createMark("public", filename, 1, lineCount, 2, 3); + final Mark mark2 = builder.createMark("stuff", filename, 3, lineCount, 4, 5); builder.addMatch(new Match(75, mark1, mark2)); builder.recordNumTokens(filename, 888); @@ -195,7 +194,7 @@ class XMLRendererTest { renderer.render(report, writer); final String xmlOutput = writer.toString(); final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .parse(new ByteArrayInputStream(xmlOutput.getBytes(ENCODING))); + .parse(new ByteArrayInputStream(xmlOutput.getBytes(ENCODING))); final NodeList files = doc.getElementsByTagName("file"); final Node file = files.item(0); final NamedNodeMap attributes = file.getAttributes(); @@ -209,8 +208,8 @@ class XMLRendererTest { CpdReportBuilder builder = new CpdReportBuilder(); final String filename = "/var/Foo.java"; final int lineCount = 6; - final Mark mark1 =builder. createMark("public", filename, 1, lineCount, 2, 3); - final Mark mark2 =builder. createMark("stuff", filename, 73, lineCount, 4, 5); + final Mark mark1 = builder.createMark("public", filename, 1, lineCount, 2, 3); + final Mark mark2 = builder.createMark("stuff", filename, 73, lineCount, 4, 5); builder.addMatch(new Match(75, mark1, mark2)); builder.recordNumTokens(filename, 888); diff --git a/pmd-core/src/test/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-core/src/test/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index f76b047c77..0000000000 --- a/pmd-core/src/test/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.CpddummyLanguage diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartLanguage.java b/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartLanguage.java deleted file mode 100644 index 1f4e8d4748..0000000000 --- a/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartLanguage.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for Dart - */ -public class DartLanguage extends AbstractLanguage { - - /** - * Creates a new Dart Language instance. - */ - public DartLanguage() { - super("Dart", "dart", new DartTokenizer(), ".dart"); - } -} diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java new file mode 100644 index 0000000000..195eee9282 --- /dev/null +++ b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.dart; + +import net.sourceforge.pmd.lang.dart.cpd.DartTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * Language implementation for Dart + */ +public class DartLanguageModule extends CpdOnlyLanguageModuleBase { + + /** + * Creates a new Dart Language instance. + */ + public DartLanguageModule() { + super(LanguageMetadata.withId("dart").name("Dart").extensions("dart")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new DartTokenizer(); + } +} diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartTokenizer.java b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java similarity index 98% rename from pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartTokenizer.java rename to pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java index 06a2527d9e..2a0fc4a1b9 100644 --- a/pmd-dart/src/main/java/net/sourceforge/pmd/cpd/DartTokenizer.java +++ b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.dart.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; diff --git a/pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 9c2eb85ed5..0000000000 --- a/pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.DartLanguage diff --git a/pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..4e50fefdcb --- /dev/null +++ b/pmd-dart/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.dart.DartLanguageModule diff --git a/pmd-dart/src/test/java/net/sourceforge/pmd/cpd/DartTokenizerTest.java b/pmd-dart/src/test/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizerTest.java similarity index 94% rename from pmd-dart/src/test/java/net/sourceforge/pmd/cpd/DartTokenizerTest.java rename to pmd-dart/src/test/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizerTest.java index bf89bff6a5..02106add09 100644 --- a/pmd-dart/src/test/java/net/sourceforge/pmd/cpd/DartTokenizerTest.java +++ b/pmd-dart/src/test/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizerTest.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.dart.cpd; import org.junit.jupiter.api.Test; @@ -11,7 +11,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class DartTokenizerTest extends CpdTextComparisonTest { DartTokenizerTest() { - super(".dart"); + super("dart", ".dart"); } diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/comment.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/comment.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/comment.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/comment.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/comment.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/comment.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/comment.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/comment.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escape_sequences.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escape_sequences.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escape_sequences.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escape_sequences.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escape_sequences.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escape_sequences.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escape_sequences.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escape_sequences.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_backslash.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_backslash.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_backslash.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_backslash.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_backslash.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_backslash.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_backslash.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_backslash.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_dollar.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_dollar.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_dollar.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_dollar.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_dollar.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_dollar.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_dollar.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_dollar.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_string.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_string.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_string.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_string.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_string.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_string.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/escaped_string.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/escaped_string.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/imports.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/imports.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/imports.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/imports.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/imports.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/imports.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/imports.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/imports.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/increment.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/increment.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/increment.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/increment.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/increment.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/increment.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/increment.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/increment.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex2.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex2.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex2.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex2.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex2.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex2.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex2.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex2.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex3.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex3.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex3.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex3.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex3.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex3.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/regex3.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/regex3.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_interpolation.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_interpolation.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_interpolation.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_interpolation.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_interpolation.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_interpolation.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_interpolation.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_interpolation.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_multiline.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_multiline.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_multiline.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_multiline.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_multiline.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_multiline.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_multiline.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_multiline.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_with_backslashes.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_with_backslashes.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_with_backslashes.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_with_backslashes.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_with_backslashes.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_with_backslashes.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/string_with_backslashes.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/string_with_backslashes.txt diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/tabWidth.dart b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/tabWidth.dart similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/tabWidth.dart rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/tabWidth.dart diff --git a/pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/tabWidth.txt b/pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/tabWidth.txt similarity index 100% rename from pmd-dart/src/test/resources/net/sourceforge/pmd/cpd/testdata/tabWidth.txt rename to pmd-dart/src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata/tabWidth.txt diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java index 1fb0182046..e0d74c8658 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java @@ -30,7 +30,7 @@ public class JavaTokenizer extends JavaCCTokenizer { public JavaTokenizer(JavaLanguageProperties properties) { ignoreAnnotations = properties.getProperty(Tokenizer.CPD_IGNORE_METADATA); - ignoreLiterals = properties.getProperty(Tokenizer.CPD_ANONYMiZE_LITERALS); + ignoreLiterals = properties.getProperty(Tokenizer.CPD_ANONYMIZE_LITERALS); ignoreIdentifiers = properties.getProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); constructorDetector = new ConstructorDetector(ignoreIdentifiers); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index 4776e17029..b8af18b063 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -73,7 +73,7 @@ class MatchAlgorithmTest { Language java = JavaLanguageModule.getInstance(); LanguagePropertyBundle bundle = java.newPropertyBundle(); bundle.setProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS, true); - bundle.setProperty(Tokenizer.CPD_ANONYMiZE_LITERALS, true); + bundle.setProperty(Tokenizer.CPD_ANONYMIZE_LITERALS, true); Tokenizer tokenizer = java.createCpdTokenizer(bundle); TextDocument sourceCode = TextDocument.readOnlyString(getSampleCode(), "Foo.java", java.getDefaultVersion()); Tokens tokens = new Tokens(); diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptLanguage.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptLanguage.java deleted file mode 100644 index 2ad5fd6f41..0000000000 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptLanguage.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * - * @author Zev Blut zb@ubit.com - */ -public class EcmascriptLanguage extends AbstractLanguage { - public EcmascriptLanguage() { - super("JavaScript", "ecmascript", new EcmascriptTokenizer(), ".js"); - } -} diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java index e73a983d37..f90fe242e8 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java @@ -4,7 +4,10 @@ package net.sourceforge.pmd.lang.ecmascript; +import net.sourceforge.pmd.lang.ecmascript.cpd.EcmascriptTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ecmascript.ast.EcmascriptParser; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; @@ -26,4 +29,9 @@ public class EcmascriptLanguageModule extends SimpleLanguageModuleBase { public static Language getInstance() { return LanguageRegistry.PMD.getLanguageByFullName(NAME); } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new EcmascriptTokenizer(); + } } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java similarity index 95% rename from pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java rename to pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java index 1a2de570ec..a1e08e2e61 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/cpd/EcmascriptTokenizer.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.ecmascript.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 7019119daf..0000000000 --- a/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.EcmascriptLanguage diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerForTypescriptTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/cpd/AnyTokenizerForTypescriptTest.java similarity index 72% rename from pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerForTypescriptTest.java rename to pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/cpd/AnyTokenizerForTypescriptTest.java index c70c736f07..4f5ecf87f8 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/AnyTokenizerForTypescriptTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/cpd/AnyTokenizerForTypescriptTest.java @@ -2,11 +2,12 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.ecmascript.cpd; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.ecmascript.EcmascriptLanguageModule; /** * @@ -14,7 +15,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class AnyTokenizerForTypescriptTest extends CpdTextComparisonTest { AnyTokenizerForTypescriptTest() { - super(".ts"); + super(EcmascriptLanguageModule.getInstance(), ".ts"); } @Override diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/EcmascriptTokenizerTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizerTest.java similarity index 85% rename from pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/EcmascriptTokenizerTest.java rename to pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizerTest.java index d097841bbd..9e0163405d 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/EcmascriptTokenizerTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizerTest.java @@ -1,22 +1,18 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.ecmascript.cpd; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.ecmascript.EcmascriptLanguageModule; class EcmascriptTokenizerTest extends CpdTextComparisonTest { EcmascriptTokenizerTest() { - super(".js"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/ecmascript/cpd/testdata"; + super(EcmascriptLanguageModule.getInstance(), ".js"); } @Test diff --git a/pmd-javascript/src/test/resources/net/sourceforge/pmd/cpd/testdata/ts/SampleTypeScript.ts b/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/cpd/testdata/ts/SampleTypeScript.ts similarity index 100% rename from pmd-javascript/src/test/resources/net/sourceforge/pmd/cpd/testdata/ts/SampleTypeScript.ts rename to pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/cpd/testdata/ts/SampleTypeScript.ts diff --git a/pmd-javascript/src/test/resources/net/sourceforge/pmd/cpd/testdata/ts/SampleTypeScript.txt b/pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/cpd/testdata/ts/SampleTypeScript.txt similarity index 100% rename from pmd-javascript/src/test/resources/net/sourceforge/pmd/cpd/testdata/ts/SampleTypeScript.txt rename to pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/cpd/testdata/ts/SampleTypeScript.txt diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java index 91abf4f844..e6ca50c0ec 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java @@ -25,7 +25,7 @@ public class PLSQLTokenizer extends JavaCCTokenizer { * to true */ ignoreIdentifiers = properties.getProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); - ignoreLiterals = properties.getProperty(Tokenizer.CPD_ANONYMiZE_LITERALS); + ignoreLiterals = properties.getProperty(Tokenizer.CPD_ANONYMIZE_LITERALS); } @Override diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java index 6428d5938b..66bfdf0f32 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java @@ -40,7 +40,7 @@ public class PLSQLLanguageModule extends SimpleLanguageModuleBase { @Override public LanguagePropertyBundle newPropertyBundle() { LanguagePropertyBundle bundle = super.newPropertyBundle(); - bundle.definePropertyDescriptor(Tokenizer.CPD_ANONYMiZE_LITERALS); + bundle.definePropertyDescriptor(Tokenizer.CPD_ANONYMIZE_LITERALS); bundle.definePropertyDescriptor(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); return bundle; } From fb9f49624d7c8f514cf2290a14de16b9a68b44dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 17:32:56 +0100 Subject: [PATCH 106/347] Delete old CPD Language interface reorganize cpd modules --- justfile | 51 ++++++++++++++++ .../PmdLanguageVersionTypeSupport.java | 6 +- .../sourceforge/pmd/cpd/AbstractLanguage.java | 61 ------------------- .../net/sourceforge/pmd/cpd/Language.java | 23 ------- .../pmd/cpd/token/AntlrTokenFilter.java | 4 +- .../net/sourceforge/pmd/lang/Language.java | 3 +- .../pmd/lang/LanguageModuleBase.java | 6 +- .../sourceforge/pmd/cpd/FortranLanguage.java | 19 ------ .../lang/fortran/FortranLanguageModule.java | 29 +++++++++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../fortran}/cpd/FortranTokenizerTest.java | 11 +--- .../lang/gherkin/GherkinLanguageModule.java | 28 +++++++++ .../pmd/lang/gherkin/cpd/GherkinLanguage.java | 20 ------ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../gherkin}/cpd/GherkinTokenizerTest.java | 9 +-- .../gherkin/cpd/testdata/docstring.feature | 2 +- .../net/sourceforge/pmd/cpd/GoLanguage.java | 15 ----- .../pmd/lang/go/GoLanguageModule.java | 22 +++++++ .../pmd/{ => lang/go}/cpd/GoTokenizer.java | 4 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../{ => lang/go}/cpd/GoTokenizerTest.java | 11 +--- .../sourceforge/pmd/cpd/GroovyLanguage.java | 18 ------ .../pmd/lang/groovy/GroovyLanguageModule.java | 28 +++++++++ .../groovy}/cpd/GroovyTokenizer.java | 6 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../groovy}/cpd/GroovyTokenizerTest.java | 10 +-- .../pmd/lang/html/HtmlCpdLanguage.java | 16 ----- .../pmd/lang/html/HtmlLanguageModule.java | 8 +++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../html/{ => cpd}/HtmlTokenizerTest.java | 10 +-- .../cpd/{ => testdata}/SimpleHtmlFile.html | 0 .../cpd/{ => testdata}/SimpleHtmlFile.txt | 0 .../net/sourceforge/pmd/cpd/JSPLanguage.java | 11 ---- .../pmd/lang/jsp/JspLanguageModule.java | 7 +++ .../pmd/{ => lang/jsp}/cpd/JSPTokenizer.java | 2 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../{ => lang/jsp}/cpd/JSPTokenizerTest.java | 12 ++-- .../sourceforge/pmd/cpd/KotlinLanguage.java | 18 ------ .../pmd/lang/kotlin/KotlinLanguageModule.java | 8 +++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../pmd/cpd/KotlinTokenizerTest.java | 2 +- .../net/sourceforge/pmd/cpd/LuaLanguage.java | 31 ---------- .../pmd/lang/lua/LuaLanguageModule.java | 25 ++++++++ .../pmd/{ => lang/lua}/cpd/LuaTokenizer.java | 13 ++-- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../{ => lang/lua}/cpd/LuaTokenizerTest.java | 11 +--- .../net/sourceforge/pmd/cpd/PerlLanguage.java | 11 ---- .../pmd/lang/perl/PerlLanguageModule.java | 22 +++++++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../pmd/lang/perl/cpd/PerlTokenizerTest.java | 2 +- .../net/sourceforge/pmd/cpd/PHPLanguage.java | 18 ------ .../net/sourceforge/pmd/cpd/PHPTokenizer.java | 12 ---- .../pmd/lang/php/PhpLanguageModule.java | 25 ++++++++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../sourceforge/pmd/cpd/PythonLanguage.java | 19 ------ .../pmd/lang/python/PythonLanguageModule.java | 25 ++++++++ .../python}/cpd/PythonTokenizer.java | 4 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../python}/cpd/PythonTokenizerTest.java | 12 +--- .../net/sourceforge/pmd/cpd/RubyLanguage.java | 20 ------ .../pmd/lang/ruby/RubyLanguageModule.java | 27 ++++++++ .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../ruby}/cpd/RubyTokenizerTest.java | 12 +--- .../sourceforge/pmd/cpd/SwiftLanguage.java | 18 ------ .../pmd/lang/swift/SwiftLanguageModule.java | 8 +++ .../{ => lang/swift}/cpd/SwiftTokenizer.java | 4 +- .../swift/{ => rule}/AbstractSwiftRule.java | 4 +- .../UnavailableFunctionRule.java | 2 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../swift}/cpd/SwiftTokenizerTest.java | 13 ++-- .../pmd/lang/xml/XmlLanguageModule.java | 8 +++ .../sourceforge/pmd/lang/xml/XmlParser.java | 2 +- .../pmd/{ => lang}/xml/cpd/XmlTokenizer.java | 4 +- .../sourceforge/pmd/xml/cpd/XmlLanguage.java | 14 ----- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../xml/cpd/XmlCPDTokenizerTest.java | 7 ++- .../{ => lang}/xml/cpd/testdata/simple.txt | 0 .../{ => lang}/xml/cpd/testdata/simple.xml | 2 +- 87 files changed, 402 insertions(+), 476 deletions(-) create mode 100644 justfile delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/Language.java delete mode 100644 pmd-fortran/src/main/java/net/sourceforge/pmd/cpd/FortranLanguage.java create mode 100644 pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java delete mode 100644 pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-fortran/src/test/java/net/sourceforge/pmd/{ => lang/fortran}/cpd/FortranTokenizerTest.java (68%) create mode 100644 pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java delete mode 100644 pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinLanguage.java delete mode 100644 pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-gherkin/src/test/java/net/sourceforge/pmd/{ => lang/gherkin}/cpd/GherkinTokenizerTest.java (71%) delete mode 100644 pmd-go/src/main/java/net/sourceforge/pmd/cpd/GoLanguage.java create mode 100644 pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java rename pmd-go/src/main/java/net/sourceforge/pmd/{ => lang/go}/cpd/GoTokenizer.java (91%) delete mode 100644 pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-go/src/test/java/net/sourceforge/pmd/{ => lang/go}/cpd/GoTokenizerTest.java (78%) delete mode 100644 pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyLanguage.java create mode 100644 pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java rename pmd-groovy/src/main/java/net/sourceforge/pmd/{ => lang/groovy}/cpd/GroovyTokenizer.java (93%) delete mode 100644 pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-groovy/src/test/java/net/sourceforge/pmd/{ => lang/groovy}/cpd/GroovyTokenizerTest.java (66%) delete mode 100644 pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlCpdLanguage.java delete mode 100644 pmd-html/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language rename pmd-html/src/test/java/net/sourceforge/pmd/lang/html/{ => cpd}/HtmlTokenizerTest.java (69%) rename pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/{ => testdata}/SimpleHtmlFile.html (100%) rename pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/{ => testdata}/SimpleHtmlFile.txt (100%) delete mode 100644 pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java rename pmd-jsp/src/main/java/net/sourceforge/pmd/{ => lang/jsp}/cpd/JSPTokenizer.java (94%) delete mode 100644 pmd-jsp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language rename pmd-jsp/src/test/java/net/sourceforge/pmd/{ => lang/jsp}/cpd/JSPTokenizerTest.java (68%) delete mode 100644 pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinLanguage.java delete mode 100644 pmd-kotlin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language delete mode 100644 pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java create mode 100644 pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java rename pmd-lua/src/main/java/net/sourceforge/pmd/{ => lang/lua}/cpd/LuaTokenizer.java (94%) delete mode 100644 pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-lua/src/test/java/net/sourceforge/pmd/{ => lang/lua}/cpd/LuaTokenizerTest.java (78%) delete mode 100644 pmd-perl/src/main/java/net/sourceforge/pmd/cpd/PerlLanguage.java create mode 100644 pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java delete mode 100644 pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language delete mode 100644 pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPLanguage.java delete mode 100644 pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java create mode 100644 pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java delete mode 100644 pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language delete mode 100644 pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonLanguage.java create mode 100644 pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java rename pmd-python/src/main/java/net/sourceforge/pmd/{ => lang/python}/cpd/PythonTokenizer.java (97%) delete mode 100644 pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-python/src/test/java/net/sourceforge/pmd/{ => lang/python}/cpd/PythonTokenizerTest.java (81%) delete mode 100644 pmd-ruby/src/main/java/net/sourceforge/pmd/cpd/RubyLanguage.java create mode 100644 pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java delete mode 100644 pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language rename pmd-ruby/src/test/java/net/sourceforge/pmd/{ => lang/ruby}/cpd/RubyTokenizerTest.java (70%) delete mode 100644 pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftLanguage.java rename pmd-swift/src/main/java/net/sourceforge/pmd/{ => lang/swift}/cpd/SwiftTokenizer.java (91%) rename pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/{ => rule}/AbstractSwiftRule.java (90%) delete mode 100644 pmd-swift/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language rename pmd-swift/src/test/java/net/sourceforge/pmd/{ => lang/swift}/cpd/SwiftTokenizerTest.java (84%) rename pmd-xml/src/main/java/net/sourceforge/pmd/{ => lang}/xml/cpd/XmlTokenizer.java (90%) delete mode 100644 pmd-xml/src/main/java/net/sourceforge/pmd/xml/cpd/XmlLanguage.java delete mode 100644 pmd-xml/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language rename pmd-xml/src/test/java/net/sourceforge/pmd/{ => lang}/xml/cpd/XmlCPDTokenizerTest.java (69%) rename pmd-xml/src/test/resources/net/sourceforge/pmd/{ => lang}/xml/cpd/testdata/simple.txt (100%) rename pmd-xml/src/test/resources/net/sourceforge/pmd/{ => lang}/xml/cpd/testdata/simple.xml (96%) diff --git a/justfile b/justfile new file mode 100644 index 0000000000..367f1a64d0 --- /dev/null +++ b/justfile @@ -0,0 +1,51 @@ + + +pmdJavaDeps := "pmd-core,pmd-lang-test,pmd-test,pmd-java" +commonBuildOpts := "-Dkotlin.compiler.incremental" + +genJavaAst: + rm -f pmd-java/target/generated-sources/javacc/last-generated-timestamp + mvnd generate-sources -pl pmd-java + +reGenAllSources: + rm -rf pmd-*/target/generated-sources + rm pmd-*/target/last-generated-timestamp + mvnd generate-sources + +install MOD: + mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl "pmd-{{MOD}}" + +alias i := install + +cleanInstallEverything *FLAGS: + mvnd clean install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -fae {{FLAGS}} + +testCore *FLAGS: + mvnd test checkstyle:check pmd:check -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl pmd-core {{FLAGS}} + +testJava *FLAGS: + mvnd test checkstyle:check pmd:check -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl pmd-java {{FLAGS}} + +installJavaAndDeps *FLAGS: + mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl {{pmdJavaDeps}} {{FLAGS}} + +installJava *FLAGS: + mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl pmd-java {{FLAGS}} + + +lintChanged DIFF="master" *FLAGS="": + #!/bin/env zsh + changed=$(git diff --name-only {{DIFF}}) + changed=${(f)changed} + projects=${changed%%/*} # remove all but first segment + echo $projects + # todo filter to pmd-* + mvnd checkstyle:check pmd:check -pl $projects -fae + + +lint projects="pmd-java": + mvnd checkstyle:check pmd:check -pl {{projects}} -fae + +lintAll: + mvnd checkstyle:check pmd:check -fae + diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageVersionTypeSupport.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageVersionTypeSupport.java index 2c71afd4b3..114bcf64d0 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageVersionTypeSupport.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/typesupport/internal/PmdLanguageVersionTypeSupport.java @@ -28,15 +28,15 @@ public class PmdLanguageVersionTypeSupport implements ITypeConverter l.getVersionNamesAndAliases().stream().map(v -> l.getTerseName() + "-" + v)) + .flatMap(l -> l.getVersionNamesAndAliases().stream().map(v -> l.getId() + "-" + v)) .collect(Collectors.toCollection(TreeSet::new)).iterator(); } @Override public LanguageVersion convert(final String value) throws Exception { return LanguageRegistry.PMD.getLanguages().stream() - .filter(l -> value.startsWith(l.getTerseName() + "-")) - .map(l -> l.getVersion(value.substring(l.getTerseName().length() + 1))) + .filter(l -> value.startsWith(l.getId() + "-")) + .map(l -> l.getVersion(value.substring(l.getId().length() + 1))) .filter(Objects::nonNull) .findFirst() .orElseThrow(() -> new TypeConversionException("Unknown language version: " + value)); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java deleted file mode 100644 index 7e08eccf8a..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.io.FilenameFilter; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; -import java.util.function.Predicate; - -import net.sourceforge.pmd.internal.util.PredicateUtil; - -public abstract class AbstractLanguage implements Language { - private final String name; - private final String terseName; - private final Tokenizer tokenizer; - private final Predicate fileFilter; - private final List extensions; - - public AbstractLanguage(String name, String terseName, Tokenizer tokenizer, String... extensions) { - this.name = name; - this.terseName = terseName; - this.tokenizer = tokenizer; - this.fileFilter = PredicateUtil.toNormalizedFileFilter(PredicateUtil.getFileExtensionFilter(extensions).or(it -> Files.isDirectory(Paths.get(it)))); - this.extensions = Arrays.asList(extensions); - } - - @Override - public FilenameFilter getFileFilter() { - return (dir, name) -> fileFilter.test(dir.toPath().resolve(name).toString()); - } - - @Override - public Tokenizer getTokenizer() { - return tokenizer; - } - - @Override - public void setProperties(Properties properties) { - // needs to be implemented by subclasses. - } - - @Override - public String getName() { - return name; - } - - @Override - public String getTerseName() { - return terseName; - } - - @Override - public List getExtensions() { - return extensions; - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Language.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Language.java deleted file mode 100644 index d25e7fab36..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Language.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.io.FilenameFilter; -import java.util.List; -import java.util.Properties; - -public interface Language { - String getName(); - - String getTerseName(); - - Tokenizer getTokenizer(); - - FilenameFilter getFileFilter(); - - void setProperties(Properties properties); - - List getExtensions(); -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java index 32d23b0ec0..4de4c5bc82 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java @@ -5,8 +5,8 @@ package net.sourceforge.pmd.cpd.token; import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; +import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; -import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager; /** * A generic filter for Antlr-based token managers that allows to use comments @@ -18,7 +18,7 @@ public class AntlrTokenFilter extends BaseTokenFilter { * Creates a new AntlrTokenFilter * @param tokenManager The token manager from which to retrieve tokens to be filtered */ - public AntlrTokenFilter(final AntlrTokenManager tokenManager) { + public AntlrTokenFilter(final TokenManager tokenManager) { super(tokenManager); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java index 045dd2c9a6..53e865c7d6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.ServiceLoader; import java.util.Set; +import net.sourceforge.pmd.cpd.AnyTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; /** @@ -199,7 +200,7 @@ public interface Language extends Comparable { * @throws UnsupportedOperationException if this language does not support CPD */ default Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { - throw new UnsupportedOperationException(this + " does not support running a CPD analysis."); + return new AnyTokenizer(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java index 4c17ee7510..b75a489f35 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java @@ -272,13 +272,13 @@ public abstract class LanguageModuleBase implements Language { * assigned to the language. Parameters should not start with a period * {@code .}. * - * @param e1 First extensions + * @param extensionWithoutPeriod First extensions * @param others Other extensions (optional) * * @throws NullPointerException If any extension is null */ - public LanguageMetadata extensions(String e1, String... others) { - this.extensions = new ArrayList<>(setOf(e1, others)); + public LanguageMetadata extensions(String extensionWithoutPeriod, String... others) { + this.extensions = new ArrayList<>(setOf(extensionWithoutPeriod, others)); AssertionUtil.requireContainsNoNullValue("extensions", this.extensions); return this; } diff --git a/pmd-fortran/src/main/java/net/sourceforge/pmd/cpd/FortranLanguage.java b/pmd-fortran/src/main/java/net/sourceforge/pmd/cpd/FortranLanguage.java deleted file mode 100644 index 2147d58c11..0000000000 --- a/pmd-fortran/src/main/java/net/sourceforge/pmd/cpd/FortranLanguage.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for Fortran - * - * @author Romain PELISSE belaran@gmail.com - */ -public class FortranLanguage extends AbstractLanguage { - /** - * Create a Fortran Language instance. - */ - public FortranLanguage() { - super("Fortran", "fortran", new AnyTokenizer("!"), ".for", ".f", ".f66", ".f77", ".f90"); - } -} diff --git a/pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java b/pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java new file mode 100644 index 0000000000..a2f5d1249d --- /dev/null +++ b/pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.fortran; + +import net.sourceforge.pmd.cpd.AnyTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * Language implementation for Fortran + * + * @author Romain PELISSE belaran@gmail.com + */ +public class FortranLanguageModule extends CpdOnlyLanguageModuleBase { + /** + * Create a Fortran Language instance. + */ + public FortranLanguageModule() { + super(LanguageMetadata.withId("fortran").name("Fortran").extensions("for", "f", "f66", "f77", "f90")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer("!"); + } +} diff --git a/pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index f78952ce23..0000000000 --- a/pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.FortranLanguage diff --git a/pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..bf4133fb47 --- /dev/null +++ b/pmd-fortran/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.fortran.FortranLanguageModule diff --git a/pmd-fortran/src/test/java/net/sourceforge/pmd/cpd/FortranTokenizerTest.java b/pmd-fortran/src/test/java/net/sourceforge/pmd/lang/fortran/cpd/FortranTokenizerTest.java similarity index 68% rename from pmd-fortran/src/test/java/net/sourceforge/pmd/cpd/FortranTokenizerTest.java rename to pmd-fortran/src/test/java/net/sourceforge/pmd/lang/fortran/cpd/FortranTokenizerTest.java index 54adb90fa5..ed49ed7030 100644 --- a/pmd-fortran/src/test/java/net/sourceforge/pmd/cpd/FortranTokenizerTest.java +++ b/pmd-fortran/src/test/java/net/sourceforge/pmd/lang/fortran/cpd/FortranTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.fortran.cpd; import org.junit.jupiter.api.Test; @@ -15,12 +15,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class FortranTokenizerTest extends CpdTextComparisonTest { FortranTokenizerTest() { - super(".for"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/fortran/cpd/testdata"; + super("fortran", ".for"); } @Test diff --git a/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java b/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java new file mode 100644 index 0000000000..5bffc78db6 --- /dev/null +++ b/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.gherkin; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.gherkin.cpd.GherkinTokenizer; + +/** + * Language implementation for Gherkin. + */ +public class GherkinLanguageModule extends CpdOnlyLanguageModuleBase { + + /** + * Creates a new Gherkin Language instance. + */ + public GherkinLanguageModule() { + super(LanguageMetadata.withId("gherkin").name("Gherkin").extensions("feature")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new GherkinTokenizer(); + } +} diff --git a/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinLanguage.java b/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinLanguage.java deleted file mode 100644 index d95282944b..0000000000 --- a/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinLanguage.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.gherkin.cpd; - -import net.sourceforge.pmd.cpd.AbstractLanguage; - -/** - * Language implementation for Gherkin. - */ -public class GherkinLanguage extends AbstractLanguage { - - /** - * Creates a new Gherkin Language instance. - */ - public GherkinLanguage() { - super("Gherkin", "gherkin", new GherkinTokenizer(), ".feature"); - } -} diff --git a/pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index fbe21e1710..0000000000 --- a/pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.lang.gherkin.cpd.GherkinLanguage diff --git a/pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..b6d0eb4f49 --- /dev/null +++ b/pmd-gherkin/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.gherkin.GherkinLanguageModule diff --git a/pmd-gherkin/src/test/java/net/sourceforge/pmd/cpd/GherkinTokenizerTest.java b/pmd-gherkin/src/test/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizerTest.java similarity index 71% rename from pmd-gherkin/src/test/java/net/sourceforge/pmd/cpd/GherkinTokenizerTest.java rename to pmd-gherkin/src/test/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizerTest.java index 86ba9bf7ab..38c4d88595 100644 --- a/pmd-gherkin/src/test/java/net/sourceforge/pmd/cpd/GherkinTokenizerTest.java +++ b/pmd-gherkin/src/test/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizerTest.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.gherkin.cpd; import org.junit.jupiter.api.Test; @@ -10,12 +10,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class GherkinTokenizerTest extends CpdTextComparisonTest { GherkinTokenizerTest() { - super(".feature"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/gherkin/cpd/testdata"; + super("gherkin", ".feature"); } @Test diff --git a/pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/docstring.feature b/pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/docstring.feature index 21ef20d2a5..cbc3e33e8a 100644 --- a/pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/docstring.feature +++ b/pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/docstring.feature @@ -3,4 +3,4 @@ Given I have a lot to say: One Two Three - """ \ No newline at end of file + """ diff --git a/pmd-go/src/main/java/net/sourceforge/pmd/cpd/GoLanguage.java b/pmd-go/src/main/java/net/sourceforge/pmd/cpd/GoLanguage.java deleted file mode 100644 index 2c4f6a4d21..0000000000 --- a/pmd-go/src/main/java/net/sourceforge/pmd/cpd/GoLanguage.java +++ /dev/null @@ -1,15 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * @author oinume@gmail.com - */ -public class GoLanguage extends AbstractLanguage { - - public GoLanguage() { - super("Go", "go", new GoTokenizer(), ".go"); - } -} diff --git a/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java new file mode 100644 index 0000000000..7ea335f9a9 --- /dev/null +++ b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java @@ -0,0 +1,22 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.go; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.go.cpd.GoTokenizer; + +public class GoLanguageModule extends CpdOnlyLanguageModuleBase { + + public GoLanguageModule() { + super(LanguageMetadata.withId("go").name("Go").extensions("go")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new GoTokenizer(); + } +} diff --git a/pmd-go/src/main/java/net/sourceforge/pmd/cpd/GoTokenizer.java b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java similarity index 91% rename from pmd-go/src/main/java/net/sourceforge/pmd/cpd/GoTokenizer.java rename to pmd-go/src/main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java index 98d2653f85..54f86a77a9 100644 --- a/pmd-go/src/main/java/net/sourceforge/pmd/cpd/GoTokenizer.java +++ b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.go.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; diff --git a/pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 7d97e302eb..0000000000 --- a/pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.GoLanguage diff --git a/pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..bb8223d299 --- /dev/null +++ b/pmd-go/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.go.GoLanguageModule diff --git a/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java b/pmd-go/src/test/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizerTest.java similarity index 78% rename from pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java rename to pmd-go/src/test/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizerTest.java index b40d4ff3bb..6ae9366f0b 100644 --- a/pmd-go/src/test/java/net/sourceforge/pmd/cpd/GoTokenizerTest.java +++ b/pmd-go/src/test/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.go.cpd; import org.junit.jupiter.api.Test; @@ -11,12 +11,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class GoTokenizerTest extends CpdTextComparisonTest { GoTokenizerTest() { - super(".go"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/go/cpd/testdata"; + super("go", ".go"); } @Test diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyLanguage.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyLanguage.java deleted file mode 100644 index 5fca5f9346..0000000000 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyLanguage.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for Groovy - */ -public class GroovyLanguage extends AbstractLanguage { - - /** - * Creates a new Groovy Language instance. - */ - public GroovyLanguage() { - super("Groovy", "groovy", new GroovyTokenizer(), ".groovy"); - } -} diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java new file mode 100644 index 0000000000..fc8e810fca --- /dev/null +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.groovy; + +import net.sourceforge.pmd.lang.groovy.cpd.GroovyTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * Language implementation for Groovy + */ +public class GroovyLanguageModule extends CpdOnlyLanguageModuleBase { + + /** + * Creates a new Groovy Language instance. + */ + public GroovyLanguageModule() { + super(LanguageMetadata.withId("groovy").name("Groovy").extensions("groovy")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new GroovyTokenizer(); + } +} diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java similarity index 93% rename from pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java rename to pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java index 1c39b10da8..57b094cfd7 100644 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/cpd/GroovyTokenizer.java +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java @@ -1,12 +1,14 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.groovy.cpd; import org.codehaus.groovy.antlr.SourceInfo; import org.codehaus.groovy.antlr.parser.GroovyLexer; +import net.sourceforge.pmd.cpd.TokenFactory; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.TextDocument; diff --git a/pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 50703791e7..0000000000 --- a/pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.GroovyLanguage diff --git a/pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..ef72e7043b --- /dev/null +++ b/pmd-groovy/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.groovy.GroovyLanguageModule diff --git a/pmd-groovy/src/test/java/net/sourceforge/pmd/cpd/GroovyTokenizerTest.java b/pmd-groovy/src/test/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizerTest.java similarity index 66% rename from pmd-groovy/src/test/java/net/sourceforge/pmd/cpd/GroovyTokenizerTest.java rename to pmd-groovy/src/test/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizerTest.java index 0cbb81b7d5..eea8825a89 100644 --- a/pmd-groovy/src/test/java/net/sourceforge/pmd/cpd/GroovyTokenizerTest.java +++ b/pmd-groovy/src/test/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.groovy.cpd; import org.junit.jupiter.api.Test; @@ -11,13 +11,9 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class GroovyTokenizerTest extends CpdTextComparisonTest { GroovyTokenizerTest() { - super(".groovy"); + super("groovy", ".groovy"); } - @Override - protected String getResourcePrefix() { - return "../lang/groovy/cpd/testdata"; - } @Test void testSample() { diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlCpdLanguage.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlCpdLanguage.java deleted file mode 100644 index 3c6c0fdbde..0000000000 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlCpdLanguage.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - - -package net.sourceforge.pmd.lang.html; - -import net.sourceforge.pmd.cpd.AbstractLanguage; -import net.sourceforge.pmd.lang.html.ast.HtmlTokenizer; - -public final class HtmlCpdLanguage extends AbstractLanguage { - - public HtmlCpdLanguage() { - super("HTML", "html", new HtmlTokenizer(), ".html"); - } -} diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java index f242c180be..0770d0112b 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/HtmlLanguageModule.java @@ -5,7 +5,10 @@ package net.sourceforge.pmd.lang.html; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.html.ast.HtmlTokenizer; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; public final class HtmlLanguageModule extends SimpleLanguageModuleBase { @@ -22,4 +25,9 @@ public final class HtmlLanguageModule extends SimpleLanguageModuleBase { public static HtmlLanguageModule getInstance() { return (HtmlLanguageModule) LanguageRegistry.PMD.getLanguageById(TERSE_NAME); } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new HtmlTokenizer(); + } } diff --git a/pmd-html/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-html/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index ce5a2a4033..0000000000 --- a/pmd-html/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.lang.html.HtmlCpdLanguage diff --git a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlTokenizerTest.java b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/cpd/HtmlTokenizerTest.java similarity index 69% rename from pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlTokenizerTest.java rename to pmd-html/src/test/java/net/sourceforge/pmd/lang/html/cpd/HtmlTokenizerTest.java index 5e17879ddf..02dd1be448 100644 --- a/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/HtmlTokenizerTest.java +++ b/pmd-html/src/test/java/net/sourceforge/pmd/lang/html/cpd/HtmlTokenizerTest.java @@ -3,21 +3,17 @@ */ -package net.sourceforge.pmd.lang.html; +package net.sourceforge.pmd.lang.html.cpd; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.html.HtmlLanguageModule; class HtmlTokenizerTest extends CpdTextComparisonTest { HtmlTokenizerTest() { - super(".html"); - } - - @Override - protected String getResourcePrefix() { - return "cpd"; + super(HtmlLanguageModule.getInstance(), ".html"); } @Test diff --git a/pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/SimpleHtmlFile.html b/pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/testdata/SimpleHtmlFile.html similarity index 100% rename from pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/SimpleHtmlFile.html rename to pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/testdata/SimpleHtmlFile.html diff --git a/pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/SimpleHtmlFile.txt b/pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/testdata/SimpleHtmlFile.txt similarity index 100% rename from pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/SimpleHtmlFile.txt rename to pmd-html/src/test/resources/net/sourceforge/pmd/lang/html/cpd/testdata/SimpleHtmlFile.txt diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java deleted file mode 100644 index c4c480cecc..0000000000 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPLanguage.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -public class JSPLanguage extends AbstractLanguage { - public JSPLanguage() { - super("JSP", "jsp", new JSPTokenizer(), ".jsp", ".jspx", ".jspf", ".tag"); - } -} diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java index 573a42b94c..89a68e336d 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java @@ -4,7 +4,10 @@ package net.sourceforge.pmd.lang.jsp; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; +import net.sourceforge.pmd.lang.jsp.cpd.JSPTokenizer; /** * Created by christoferdutz on 20.09.14. @@ -20,4 +23,8 @@ public class JspLanguageModule extends SimpleLanguageModuleBase { new JspHandler()); } + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new JSPTokenizer(); + } } diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java similarity index 94% rename from pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java rename to pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java index d32b96973e..dd6892708f 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/cpd/JSPTokenizer.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.jsp.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-jsp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-jsp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index a644d2d185..0000000000 --- a/pmd-jsp/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.JSPLanguage diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/cpd/JSPTokenizerTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizerTest.java similarity index 68% rename from pmd-jsp/src/test/java/net/sourceforge/pmd/cpd/JSPTokenizerTest.java rename to pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizerTest.java index b1a3c3879e..8e23dbb477 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/cpd/JSPTokenizerTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizerTest.java @@ -1,23 +1,19 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.jsp.cpd; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.jsp.JspLanguageModule; class JSPTokenizerTest extends CpdTextComparisonTest { JSPTokenizerTest() { - super(".jsp"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/jsp/cpd/testdata"; + super(JspLanguageModule.TERSE_NAME, ".jsp"); } @Test diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinLanguage.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinLanguage.java deleted file mode 100644 index 4a14aa766f..0000000000 --- a/pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinLanguage.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for Kotlin - */ -public class KotlinLanguage extends AbstractLanguage { - - /** - * Creates a new Kotlin Language instance. - */ - public KotlinLanguage() { - super("Kotlin", "kotlin", new KotlinTokenizer(), ".kt"); - } -} diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java index 31e0d209d4..e3f012f8c2 100644 --- a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java @@ -5,6 +5,9 @@ package net.sourceforge.pmd.lang.kotlin; import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.cpd.KotlinTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; /** @@ -29,4 +32,9 @@ public class KotlinLanguageModule extends SimpleLanguageModuleBase { new KotlinHandler()); } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new KotlinTokenizer(); + } } diff --git a/pmd-kotlin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-kotlin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 6908eda6c4..0000000000 --- a/pmd-kotlin/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.KotlinLanguage diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java index 2f9241296f..1ab9bee77d 100644 --- a/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java @@ -11,7 +11,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class KotlinTokenizerTest extends CpdTextComparisonTest { KotlinTokenizerTest() { - super(".kt"); + super("kotlin", ".kt"); } @Override diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java b/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java deleted file mode 100644 index 2e485e13b8..0000000000 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaLanguage.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; - -/** - * Language implementation for Lua - */ -public class LuaLanguage extends AbstractLanguage { - - public LuaLanguage() { - this(System.getProperties()); - } - - /** - * Creates a new Lua Language instance. - */ - public LuaLanguage(Properties properties) { - super("Lua", "lua", new LuaTokenizer(), ".lua"); - setProperties(properties); - } - - @Override - public final void setProperties(Properties properties) { - LuaTokenizer tokenizer = (LuaTokenizer) getTokenizer(); - tokenizer.setProperties(properties); - } -} diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java new file mode 100644 index 0000000000..b9d8fb13ac --- /dev/null +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.lua; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.lua.cpd.LuaTokenizer; + +/** + * @author Clรฉment Fournier + */ +public class LuaLanguageModule extends CpdOnlyLanguageModuleBase { + + public LuaLanguageModule() { + super(LanguageMetadata.withId("lua").name("Lua").extensions("lua")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new LuaTokenizer(); + } +} diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java similarity index 94% rename from pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java rename to pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java index 509a7803c6..416b36596b 100644 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/cpd/LuaTokenizer.java +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.lua.cpd; import java.util.Properties; @@ -11,8 +11,8 @@ import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; +import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; -import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager; import net.sourceforge.pmd.lang.lua.ast.LuaLexer; /** @@ -42,7 +42,7 @@ public class LuaTokenizer extends AntlrTokenizer { } @Override - protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { + protected TokenManager filterTokenStream(TokenManager tokenManager) { return new LuaTokenFilter(tokenManager, ignoreLiteralSequences); } @@ -63,7 +63,7 @@ public class LuaTokenizer extends AntlrTokenizer { private boolean discardCurrent = false; - LuaTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreLiteralSequences) { + LuaTokenFilter(final TokenManager tokenManager, boolean ignoreLiteralSequences) { super(tokenManager); this.ignoreLiteralSequences = ignoreLiteralSequences; } @@ -105,8 +105,7 @@ public class LuaTokenizer extends AntlrTokenizer { } else if (type == LuaLexer.OPEN_BRACE || type == LuaLexer.OPEN_BRACKET || type == LuaLexer.OPEN_PARENS) { - final AntlrToken finalToken = findEndOfSequenceOfLiterals(remainingTokens); - discardingLiteralsUntil = finalToken; + discardingLiteralsUntil = findEndOfSequenceOfLiterals(remainingTokens); } } } diff --git a/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index ff792867ee..0000000000 --- a/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.LuaLanguage diff --git a/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..647c4605a7 --- /dev/null +++ b/pmd-lua/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.lua.LuaLanguageModule diff --git a/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java b/pmd-lua/src/test/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizerTest.java similarity index 78% rename from pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java rename to pmd-lua/src/test/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizerTest.java index 5045837e2c..a550f93818 100644 --- a/pmd-lua/src/test/java/net/sourceforge/pmd/cpd/LuaTokenizerTest.java +++ b/pmd-lua/src/test/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.lua.cpd; import org.junit.jupiter.api.Test; @@ -10,12 +10,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class LuaTokenizerTest extends CpdTextComparisonTest { LuaTokenizerTest() { - super(".lua"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/lua/cpd/testdata"; + super("lua", ".lua"); } @Test diff --git a/pmd-perl/src/main/java/net/sourceforge/pmd/cpd/PerlLanguage.java b/pmd-perl/src/main/java/net/sourceforge/pmd/cpd/PerlLanguage.java deleted file mode 100644 index c66d201c56..0000000000 --- a/pmd-perl/src/main/java/net/sourceforge/pmd/cpd/PerlLanguage.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -public class PerlLanguage extends AbstractLanguage { - public PerlLanguage() { - super("Perl", "perl", new AnyTokenizer("#"), ".pm", ".pl", ".t"); - } -} diff --git a/pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java b/pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java new file mode 100644 index 0000000000..e6305cc808 --- /dev/null +++ b/pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java @@ -0,0 +1,22 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.perl; + +import net.sourceforge.pmd.cpd.AnyTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +public class PerlLanguageModule extends CpdOnlyLanguageModuleBase { + + public PerlLanguageModule() { + super(LanguageMetadata.withId("perl").name("Perl").extensions("pm", "pl", "t")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer("#"); + } +} diff --git a/pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 40f6f3a026..0000000000 --- a/pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.PerlLanguage diff --git a/pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..c7dc531053 --- /dev/null +++ b/pmd-perl/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.perl.PerlLanguageModule diff --git a/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java b/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java index ec2a8393fb..1c42757a11 100644 --- a/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java +++ b/pmd-perl/src/test/java/net/sourceforge/pmd/lang/perl/cpd/PerlTokenizerTest.java @@ -14,7 +14,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class PerlTokenizerTest extends CpdTextComparisonTest { PerlTokenizerTest() { - super(".pl"); + super("perl", ".pl"); } @Test diff --git a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPLanguage.java b/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPLanguage.java deleted file mode 100644 index 1b5aafcfea..0000000000 --- a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPLanguage.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for PHP - */ -public class PHPLanguage extends AbstractLanguage { - - /** - * Creates a new PHP Language instance. - */ - public PHPLanguage() { - super("PHP", "php", new PHPTokenizer(), ".php", ".class"); - } -} diff --git a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java b/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java deleted file mode 100644 index 56f3615697..0000000000 --- a/pmd-php/src/main/java/net/sourceforge/pmd/cpd/PHPTokenizer.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Simple tokenizer for PHP. - */ -public class PHPTokenizer extends AnyTokenizer { - -} diff --git a/pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java b/pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java new file mode 100644 index 0000000000..dfe3516d7a --- /dev/null +++ b/pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.php; + +import net.sourceforge.pmd.cpd.AnyTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * Language implementation for PHP + */ +public class PhpLanguageModule extends CpdOnlyLanguageModuleBase { + + public PhpLanguageModule() { + super(LanguageMetadata.withId("php").name("PHP").extensions("php", "class")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer("#"); + } +} diff --git a/pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 665241293c..0000000000 --- a/pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.PHPLanguage diff --git a/pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..2d0b4aa64f --- /dev/null +++ b/pmd-php/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.php.PhpLanguageModule diff --git a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonLanguage.java b/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonLanguage.java deleted file mode 100644 index eb331766aa..0000000000 --- a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonLanguage.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Defines the Language module for Python - */ -public class PythonLanguage extends AbstractLanguage { - - /** - * Creates a new instance of {@link PythonLanguage} with the default - * extensions for python files. - */ - public PythonLanguage() { - super("Python", "python", new PythonTokenizer(), ".py"); - } -} diff --git a/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java new file mode 100644 index 0000000000..ab6cdae1b2 --- /dev/null +++ b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.python; + +import net.sourceforge.pmd.lang.python.cpd.PythonTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * Defines the Language module for Python + */ +public class PythonLanguageModule extends CpdOnlyLanguageModuleBase { + + public PythonLanguageModule() { + super(LanguageMetadata.withId("python").name("Python").extensions("py")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new PythonTokenizer(); + } +} diff --git a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java similarity index 97% rename from pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java rename to pmd-python/src/main/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java index 89f8dce9ae..b9194d0720 100644 --- a/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java +++ b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.python.cpd; import java.util.regex.Pattern; diff --git a/pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 29b0e80274..0000000000 --- a/pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.PythonLanguage diff --git a/pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..3a3c036b90 --- /dev/null +++ b/pmd-python/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.python.PythonLanguageModule diff --git a/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java b/pmd-python/src/test/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizerTest.java similarity index 81% rename from pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java rename to pmd-python/src/test/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizerTest.java index b5c7988cc1..61357c8b1c 100644 --- a/pmd-python/src/test/java/net/sourceforge/pmd/cpd/PythonTokenizerTest.java +++ b/pmd-python/src/test/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.python.cpd; import org.junit.jupiter.api.Test; @@ -11,15 +11,9 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class PythonTokenizerTest extends CpdTextComparisonTest { PythonTokenizerTest() { - super(".py"); + super("python", ".py"); } - @Override - protected String getResourcePrefix() { - return "../lang/python/cpd/testdata"; - } - - @Test void sampleTest() { doTest("sample_python"); diff --git a/pmd-ruby/src/main/java/net/sourceforge/pmd/cpd/RubyLanguage.java b/pmd-ruby/src/main/java/net/sourceforge/pmd/cpd/RubyLanguage.java deleted file mode 100644 index cfe7ee2e66..0000000000 --- a/pmd-ruby/src/main/java/net/sourceforge/pmd/cpd/RubyLanguage.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for Ruby. - * - * @author Zev Blut zb@ubit.com - */ -public class RubyLanguage extends AbstractLanguage { - - /** - * Creates a new Ruby Language instance. - */ - public RubyLanguage() { - super("Ruby", "ruby", new AnyTokenizer("#"), ".rb", ".cgi", ".class"); - } -} diff --git a/pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java b/pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java new file mode 100644 index 0000000000..65e8ef06d7 --- /dev/null +++ b/pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java @@ -0,0 +1,27 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ruby; + +import net.sourceforge.pmd.cpd.AnyTokenizer; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * Language implementation for Ruby. + * + * @author Zev Blut zb@ubit.com + */ +public class RubyLanguageModule extends CpdOnlyLanguageModuleBase { + + public RubyLanguageModule() { + super(LanguageMetadata.withId("ruby").name("Ruby").extensions("rb", "cgi", "class")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer("#"); + } +} diff --git a/pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 223d4d5f92..0000000000 --- a/pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.RubyLanguage diff --git a/pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..b35b5aa41f --- /dev/null +++ b/pmd-ruby/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.ruby.RubyLanguageModule diff --git a/pmd-ruby/src/test/java/net/sourceforge/pmd/cpd/RubyTokenizerTest.java b/pmd-ruby/src/test/java/net/sourceforge/pmd/lang/ruby/cpd/RubyTokenizerTest.java similarity index 70% rename from pmd-ruby/src/test/java/net/sourceforge/pmd/cpd/RubyTokenizerTest.java rename to pmd-ruby/src/test/java/net/sourceforge/pmd/lang/ruby/cpd/RubyTokenizerTest.java index dfe8574b64..9f34d472fd 100644 --- a/pmd-ruby/src/test/java/net/sourceforge/pmd/cpd/RubyTokenizerTest.java +++ b/pmd-ruby/src/test/java/net/sourceforge/pmd/lang/ruby/cpd/RubyTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.ruby.cpd; import org.junit.jupiter.api.Test; @@ -11,15 +11,9 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class RubyTokenizerTest extends CpdTextComparisonTest { RubyTokenizerTest() { - super(".rb"); + super("ruby", ".rb"); } - @Override - protected String getResourcePrefix() { - return "../lang/ruby/cpd/testdata"; - } - - @Test void testSimple() { doTest("server"); diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftLanguage.java b/pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftLanguage.java deleted file mode 100644 index 841e0861fd..0000000000 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftLanguage.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * Language implementation for Swift - */ -public class SwiftLanguage extends AbstractLanguage { - - /** - * Creates a new Swift Language instance. - */ - public SwiftLanguage() { - super("Swift", "swift", new SwiftTokenizer(), ".swift"); - } -} diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java index 1b05e94b4b..d517bde873 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java @@ -4,7 +4,10 @@ package net.sourceforge.pmd.lang.swift; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; +import net.sourceforge.pmd.lang.swift.cpd.SwiftTokenizer; /** * Language Module for Swift @@ -22,4 +25,9 @@ public class SwiftLanguageModule extends SimpleLanguageModuleBase { public SwiftLanguageModule() { super(LanguageMetadata.withId(TERSE_NAME).name(NAME).extensions("swift"), new SwiftHandler()); } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new SwiftTokenizer(); + } } diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftTokenizer.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java similarity index 91% rename from pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftTokenizer.java rename to pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java index 41d7c28d36..868a4b0034 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/cpd/SwiftTokenizer.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.swift.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/AbstractSwiftRule.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/AbstractSwiftRule.java similarity index 90% rename from pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/AbstractSwiftRule.java rename to pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/AbstractSwiftRule.java index 11199a0239..0a4109a5b0 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/AbstractSwiftRule.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/AbstractSwiftRule.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang.swift; +package net.sourceforge.pmd.lang.swift.rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.ast.AstVisitor; diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java index 5c49fb5669..77c4700e75 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java @@ -8,7 +8,7 @@ import java.util.List; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.ast.AstVisitor; -import net.sourceforge.pmd.lang.swift.AbstractSwiftRule; +import net.sourceforge.pmd.lang.swift.rule.AbstractSwiftRule; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwAttribute; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwAttributes; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwCodeBlock; diff --git a/pmd-swift/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-swift/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index a5cd852cde..0000000000 --- a/pmd-swift/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.cpd.SwiftLanguage diff --git a/pmd-swift/src/test/java/net/sourceforge/pmd/cpd/SwiftTokenizerTest.java b/pmd-swift/src/test/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizerTest.java similarity index 84% rename from pmd-swift/src/test/java/net/sourceforge/pmd/cpd/SwiftTokenizerTest.java rename to pmd-swift/src/test/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizerTest.java index b63688ed7b..6072e311f8 100644 --- a/pmd-swift/src/test/java/net/sourceforge/pmd/cpd/SwiftTokenizerTest.java +++ b/pmd-swift/src/test/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizerTest.java @@ -1,25 +1,20 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.swift.cpd; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.swift.SwiftLanguageModule; class SwiftTokenizerTest extends CpdTextComparisonTest { SwiftTokenizerTest() { - super(".swift"); + super(SwiftLanguageModule.TERSE_NAME, ".swift"); } - @Override - protected String getResourcePrefix() { - return "../lang/swift/cpd/testdata"; - } - - @Test void testSwift42() { doTest("Swift4.2"); diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlLanguageModule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlLanguageModule.java index 5df4a35b06..eb0d42275e 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlLanguageModule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlLanguageModule.java @@ -4,7 +4,10 @@ package net.sourceforge.pmd.lang.xml; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; +import net.sourceforge.pmd.lang.xml.cpd.XmlTokenizer; /** * Created by christoferdutz on 20.09.14. @@ -17,4 +20,9 @@ public class XmlLanguageModule extends SimpleLanguageModuleBase { public XmlLanguageModule() { super(LanguageMetadata.withId(TERSE_NAME).name(NAME).extensions("xml"), new XmlHandler()); } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new XmlTokenizer(); + } } diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java index 58b91d35c2..7111402727 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java @@ -12,7 +12,7 @@ import net.sourceforge.pmd.lang.xml.ast.internal.XmlParserImpl.RootXmlNode; /** * Adapter for the XmlParser. */ -public class XmlParser implements Parser { +class XmlParser implements Parser { @Override public RootXmlNode parse(ParserTask task) throws ParseException { diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/xml/cpd/XmlTokenizer.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java similarity index 90% rename from pmd-xml/src/main/java/net/sourceforge/pmd/xml/cpd/XmlTokenizer.java rename to pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java index 278e8463eb..e15b9d6b22 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/xml/cpd/XmlTokenizer.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.xml.cpd; +package net.sourceforge.pmd.lang.xml.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/xml/cpd/XmlLanguage.java b/pmd-xml/src/main/java/net/sourceforge/pmd/xml/cpd/XmlLanguage.java deleted file mode 100644 index 38b38c8eb3..0000000000 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/xml/cpd/XmlLanguage.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.xml.cpd; - -import net.sourceforge.pmd.cpd.AbstractLanguage; - -public class XmlLanguage extends AbstractLanguage { - - public XmlLanguage() { - super("Xml", "xml", new XmlTokenizer(), ".xml"); - } -} diff --git a/pmd-xml/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-xml/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index fad9c021ea..0000000000 --- a/pmd-xml/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.xml.cpd.XmlLanguage diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/xml/cpd/XmlCPDTokenizerTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/cpd/XmlCPDTokenizerTest.java similarity index 69% rename from pmd-xml/src/test/java/net/sourceforge/pmd/xml/cpd/XmlCPDTokenizerTest.java rename to pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/cpd/XmlCPDTokenizerTest.java index 9fc4f39400..2799a93730 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/xml/cpd/XmlCPDTokenizerTest.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/cpd/XmlCPDTokenizerTest.java @@ -1,17 +1,18 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.xml.cpd; +package net.sourceforge.pmd.lang.xml.cpd; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.xml.XmlLanguageModule; class XmlCPDTokenizerTest extends CpdTextComparisonTest { XmlCPDTokenizerTest() { - super(".xml"); + super(XmlLanguageModule.TERSE_NAME, ".xml"); } @Test diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/xml/cpd/testdata/simple.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cpd/testdata/simple.txt similarity index 100% rename from pmd-xml/src/test/resources/net/sourceforge/pmd/xml/cpd/testdata/simple.txt rename to pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cpd/testdata/simple.txt diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/xml/cpd/testdata/simple.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cpd/testdata/simple.xml similarity index 96% rename from pmd-xml/src/test/resources/net/sourceforge/pmd/xml/cpd/testdata/simple.xml rename to pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cpd/testdata/simple.xml index 3b86c4c08c..7800089f69 100644 --- a/pmd-xml/src/test/resources/net/sourceforge/pmd/xml/cpd/testdata/simple.xml +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/cpd/testdata/simple.xml @@ -2,4 +2,4 @@ Somehow we would like to improve this xml so we are not repeating the same content in this file or other files Somehow we would like to improve this xml so we are not repeating the same content in this file or other files - \ No newline at end of file + From ddbfc90c14763d08fb1d3378f5e6e62f187ddf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 18:21:54 +0100 Subject: [PATCH 107/347] Fix build --- justfile | 7 +- .../sourceforge/pmd/ant/xml/cpdtasktest.xml | 2 +- .../pmd/lang/apex/ApexLanguageModule.java | 2 +- .../pmd/lang/apex/cpd/ApexTokenizer.java | 54 +++++++++++---- .../pmd/cli/commands/internal/CpdCommand.java | 2 +- .../net/sourceforge/pmd/cpd/AnyTokenizer.java | 4 +- .../java/net/sourceforge/pmd/cpd/GUI.java | 2 +- .../net/sourceforge/pmd/cpd/Tokenizer.java | 41 ++--------- .../pmd/internal/LanguageServiceBase.java | 68 ------------------- .../{ => impl}/CpdOnlyLanguageModuleBase.java | 4 +- .../pmd/cpd/MatchAlgorithmTest.java | 44 +++--------- .../pmd/lang/DummyLanguageModule.java | 9 +++ .../pmd/lang/cpp/CppLanguageModule.java | 4 +- .../pmd/lang/cs/CsLanguageModule.java | 4 +- .../pmd/{ => lang/cs}/cpd/CsTokenizer.java | 5 +- .../{ => lang/cs}/cpd/CsTokenizerTest.java | 39 ++++++----- .../pmd/lang/dart/DartLanguageModule.java | 4 +- .../lang/fortran/FortranLanguageModule.java | 2 +- .../lang/gherkin/GherkinLanguageModule.java | 2 +- .../pmd/lang/go/GoLanguageModule.java | 2 +- .../pmd/lang/groovy/GroovyLanguageModule.java | 4 +- .../pmd/lang/groovy/cpd/GroovyTokenizer.java | 4 +- .../pmd/lang/html/ast/HtmlTokenizer.java | 4 +- .../{ => lang/java}/cpd/JavaTokenizer.java | 7 +- .../java}/cpd/JavaTokenizerTest.java | 43 +++++------- .../ecmascript/EcmascriptLanguageModule.java | 2 +- .../pmd/lang/kotlin/KotlinLanguageModule.java | 2 +- .../kotlin}/cpd/KotlinTokenizer.java | 10 +-- .../kotlin}/cpd/KotlinTokenizerTest.java | 9 +-- .../pmd/lang/lua/LuaLanguageModule.java | 11 ++- .../pmd/lang/lua/cpd/LuaTokenizer.java | 20 ++---- .../pmd/lang/matlab/MatlabLanguageModule.java | 2 +- .../objectivec/ObjectiveCLanguageModule.java | 4 +- .../cpd/ObjectiveCTokenizerTest.java | 5 -- .../pmd/lang/perl/PerlLanguageModule.java | 2 +- .../pmd/lang/php/PhpLanguageModule.java | 2 +- .../pmd/lang/python/PythonLanguageModule.java | 4 +- .../pmd/lang/ruby/RubyLanguageModule.java | 2 +- .../sourceforge/pmd/cpd/ScalaTokenizer.java | 10 +-- .../UnavailableFunctionRule.java | 2 +- 40 files changed, 174 insertions(+), 276 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/LanguageServiceBase.java rename pmd-core/src/main/java/net/sourceforge/pmd/lang/{ => impl}/CpdOnlyLanguageModuleBase.java (84%) rename {pmd-java => pmd-core}/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java (58%) rename pmd-cs/src/main/java/net/sourceforge/pmd/{ => lang/cs}/cpd/CsTokenizer.java (99%) rename pmd-cs/src/test/java/net/sourceforge/pmd/{ => lang/cs}/cpd/CsTokenizerTest.java (69%) rename pmd-java/src/main/java/net/sourceforge/pmd/{ => lang/java}/cpd/JavaTokenizer.java (98%) rename pmd-java/src/test/java/net/sourceforge/pmd/{ => lang/java}/cpd/JavaTokenizerTest.java (63%) rename pmd-kotlin/src/main/java/net/sourceforge/pmd/{ => lang/kotlin}/cpd/KotlinTokenizer.java (88%) rename pmd-kotlin/src/test/java/net/sourceforge/pmd/{ => lang/kotlin}/cpd/KotlinTokenizerTest.java (80%) diff --git a/justfile b/justfile index 367f1a64d0..bac7611772 100644 --- a/justfile +++ b/justfile @@ -20,6 +20,9 @@ alias i := install cleanInstallEverything *FLAGS: mvnd clean install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -fae {{FLAGS}} +installEverything *FLAGS: + mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -fae {{FLAGS}} + testCore *FLAGS: mvnd test checkstyle:check pmd:check -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl pmd-core {{FLAGS}} @@ -46,6 +49,6 @@ lintChanged DIFF="master" *FLAGS="": lint projects="pmd-java": mvnd checkstyle:check pmd:check -pl {{projects}} -fae -lintAll: - mvnd checkstyle:check pmd:check -fae +lintAll *FLAGS: + mvnd checkstyle:check pmd:check -fae {{FLAGS}} diff --git a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml index 5a76ac24da..3f84018714 100644 --- a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml +++ b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml @@ -8,7 +8,7 @@ - + diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java index 874da4dd4c..5ef1260d54 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java @@ -4,13 +4,13 @@ package net.sourceforge.pmd.lang.apex; -import net.sourceforge.pmd.lang.apex.cpd.ApexTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.apex.cpd.ApexTokenizer; import apex.jorje.services.Version; diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java index cea287a50e..4b175ea6a0 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java @@ -4,32 +4,60 @@ package net.sourceforge.pmd.lang.apex.cpd; +import java.io.IOException; import java.util.Locale; -import org.antlr.v4.runtime.CharStream; +import org.antlr.runtime.ANTLRReaderStream; +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.Lexer; +import org.antlr.runtime.Token; +import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.lang.apex.ApexJorjeLogging; import net.sourceforge.pmd.lang.apex.ApexLanguageProperties; -import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; +import net.sourceforge.pmd.lang.ast.TokenMgrError; +import net.sourceforge.pmd.lang.document.TextDocument; + +import apex.jorje.parser.impl.ApexLexer; + +public class ApexTokenizer implements Tokenizer { -public class ApexTokenizer extends AntlrTokenizer { private final boolean caseSensitive; public ApexTokenizer(ApexLanguageProperties properties) { this.caseSensitive = properties.getProperty(Tokenizer.CPD_CASE_SENSITIVE); + ApexJorjeLogging.disableLogging(); } @Override - protected String getImage(AntlrToken token) { - if (caseSensitive) { - return token.getImage(); + public void tokenize(TextDocument document, TokenFactory tokenEntries) throws IOException { + + ANTLRStringStream ass = new ANTLRReaderStream(document.newReader()); + ApexLexer lexer = new ApexLexer(ass) { + @Override + public void emitErrorMessage(String msg) { + throw new TokenMgrError(getLine(), getCharPositionInLine(), getSourceName(), msg, null); + } + }; + + Token token = lexer.nextToken(); + + while (token.getType() != Token.EOF) { + if (token.getChannel() != Lexer.HIDDEN) { + String tokenText = token.getText(); + if (!caseSensitive) { + tokenText = tokenText.toLowerCase(Locale.ROOT); + } + tokenEntries.recordToken( + tokenText, + token.getLine(), + token.getCharPositionInLine() + 1, + token.getLine(), + token.getCharPositionInLine() + tokenText.length() + 1 + ); + } + token = lexer.nextToken(); } - return token.getImage().toLowerCase(Locale.ROOT); - } - - @Override - protected org.antlr.v4.runtime.Lexer getLexerForSource(CharStream charStream) { - return new com.nawforce.runtime.parsers.ApexLexer(charStream); } } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index d4d91786bc..5f3ae64ac7 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -124,7 +124,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { final CPDConfiguration configuration = toConfiguration(); - try (CpdAnalysis cpd = new CpdAnalysis(configuration)){ + try (CpdAnalysis cpd = new CpdAnalysis(configuration)) { MutableBoolean hasViolations = new MutableBoolean(); cpd.performAnalysis(report -> hasViolations.setValue(!report.getMatches().isEmpty())); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java index 49000eaadd..0d3de7a45d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java @@ -62,8 +62,8 @@ public class AnyTokenizer implements Tokenizer { } @Override - public void tokenize(TextDocument sourceCode, TokenFactory tokens) { - Chars text = sourceCode.getText(); + public void tokenize(TextDocument document, TokenFactory tokens) { + Chars text = document.getText(); Matcher matcher = pattern.matcher(text); int lineNo = 1; int lastLineStart = 0; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 5b6ea12628..ba52e4d7c8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -62,11 +62,11 @@ import javax.swing.table.TableModel; import net.sourceforge.pmd.PMDVersion; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; public class GUI implements CPDListener { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index 63645db659..5644159e34 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -26,10 +26,8 @@ public interface Tokenizer { PropertyDescriptor CPD_ANONYMIZE_IDENTIFIERS = PropertyFactory.booleanProperty("cpdAnonymizeIdentifiers") .defaultValue(false) - .desc("Anonymize identifiers. They are still part of the token stream but all literals appear to have the same value.") + .desc("Anonymize identifiers. They are still part of the token stream but all identifiers appear to have the same value.") .build(); - - PropertyDescriptor CPD_IGNORE_IMPORTS = PropertyFactory.booleanProperty("cpdIgnoreImports") .defaultValue(true) @@ -41,48 +39,17 @@ public interface Tokenizer { .defaultValue(false) .desc("Ignore metadata such as Java annotations or C# attributes.") .build(); - - PropertyDescriptor CPD_CASE_SENSITIVE = PropertyFactory.booleanProperty("cpdCaseSensitive") - .defaultValue(true) + .defaultValue(false) .desc("Whether CPD should ignore the case of tokens. Affects all tokens.") .build(); - String IGNORE_LITERALS = "ignore_literals"; - String IGNORE_IDENTIFIERS = "ignore_identifiers"; - String IGNORE_ANNOTATIONS = "ignore_annotations"; - - /** - * Ignore sequences of literals (e.g, 0,0,0,0...). - */ - String OPTION_IGNORE_LITERAL_SEQUENCES = "net.sourceforge.pmd.cpd.Tokenizer.skipLiteralSequences"; - /** - * Ignore using directives in C#. The default value is false. - */ - String IGNORE_USINGS = "ignore_usings"; - - /** - * Enables or disabled skipping of blocks like a pre-processor. It is a - * boolean property. The default value is true. - * - * @see #OPTION_SKIP_BLOCKS_PATTERN - */ - String OPTION_SKIP_BLOCKS = "net.sourceforge.pmd.cpd.Tokenizer.skipBlocks"; - /** - * Configures the pattern, to find the blocks to skip. It is a string - * property and contains of two parts, separated by {@code |}. The first - * part is the start pattern, the second part is the ending pattern. Default - * value is "{@code #if 0|#endif}". - * - * @see #DEFAULT_SKIP_BLOCKS_PATTERN - */ - String OPTION_SKIP_BLOCKS_PATTERN = "net.sourceforge.pmd.cpd.Tokenizer.skipBlocksPattern"; - + @Deprecated // TODO what to do with this? String DEFAULT_SKIP_BLOCKS_PATTERN = "#if 0|#endif"; - void tokenize(TextDocument sourceCode, TokenFactory tokens) throws IOException; + void tokenize(TextDocument document, TokenFactory tokens) throws IOException; static void tokenize(Tokenizer tokenizer, TextDocument textDocument, Tokens tokens) throws IOException { try (TokenFactory tf = TokenFactory.forFile(textDocument, tokens)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/LanguageServiceBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/LanguageServiceBase.java deleted file mode 100644 index 096cbbe603..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/LanguageServiceBase.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.internal; - -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; -import java.util.Set; -import java.util.TreeSet; - -import net.sourceforge.pmd.annotation.InternalApi; - -@InternalApi -public abstract class LanguageServiceBase { - - protected interface NameExtractor { - String getName(T language); - } - - protected final Set languages; - protected final Map languagesByName; - protected final Map languagesByTerseName; - - protected LanguageServiceBase(final Class serviceType, final Comparator comparator, - final NameExtractor nameExtractor, final NameExtractor terseNameExtractor) { - Set sortedLangs = new TreeSet<>(comparator); - // Use current class' classloader instead of the threads context classloader, see https://github.com/pmd/pmd/issues/1788 - ServiceLoader languageLoader = ServiceLoader.load(serviceType, getClass().getClassLoader()); - Iterator iterator = languageLoader.iterator(); - - while (true) { - // this loop is weird, but both hasNext and next may throw ServiceConfigurationError, - // it's more robust that way - try { - if (iterator.hasNext()) { - T language = iterator.next(); - sortedLangs.add(language); - } else { - break; - } - } catch (UnsupportedClassVersionError | ServiceConfigurationError e) { - // Some languages require java8 and are therefore only available - // if java8 or later is used as runtime. - System.err.println("Ignoring language for PMD: " + e.toString()); - } - } - - // using a linked hash map to maintain insertion order - languages = Collections.unmodifiableSet(new LinkedHashSet<>(sortedLangs)); - - // TODO there may be languages with duplicate names - Map byName = new LinkedHashMap<>(); - Map byTerseName = new LinkedHashMap<>(); - for (T language : sortedLangs) { - byName.put(nameExtractor.getName(language), language); - byTerseName.put(terseNameExtractor.getName(language), language); - } - languagesByName = Collections.unmodifiableMap(byName); - languagesByTerseName = Collections.unmodifiableMap(byTerseName); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/CpdOnlyLanguageModuleBase.java similarity index 84% rename from pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java rename to pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/CpdOnlyLanguageModuleBase.java index 1db5c8689f..f67f7e7dec 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/CpdOnlyLanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/CpdOnlyLanguageModuleBase.java @@ -2,9 +2,11 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.lang; +package net.sourceforge.pmd.lang.impl; import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguageModuleBase; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; /** * Base class for language modules that only support CPD and not PMD. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java similarity index 58% rename from pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java rename to pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index b8af18b063..03f4061255 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -13,12 +13,10 @@ import java.util.Iterator; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguagePropertyBundle; -import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.lang.java.JavaLanguageModule; class MatchAlgorithmTest { @@ -38,15 +36,15 @@ class MatchAlgorithmTest { @Test void testSimple() throws IOException { - Language java = JavaLanguageModule.getInstance(); - Tokenizer tokenizer = java.createCpdTokenizer(java.newPropertyBundle()); - String fileName = "Foo.java"; - TextFile textFile = TextFile.forCharSeq(getSampleCode(), fileName, java.getDefaultVersion()); + Language dummy = DummyLanguageModule.getInstance(); + Tokenizer tokenizer = dummy.createCpdTokenizer(dummy.newPropertyBundle()); + String fileName = "Foo.dummy"; + TextFile textFile = TextFile.forCharSeq(getSampleCode(), fileName, dummy.getDefaultVersion()); SourceManager sourceManager = new SourceManager(listOf(textFile)); Tokens tokens = new Tokens(); TextDocument sourceCode = sourceManager.get(textFile); Tokenizer.tokenize(tokenizer, sourceCode, tokens); - assertEquals(41, tokens.size()); + assertEquals(44, tokens.size()); MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, 5); matchAlgorithm.findMatches(); @@ -61,34 +59,10 @@ class MatchAlgorithmTest { assertEquals(3, mark1.getBeginLine()); assertEquals(fileName, mark1.getFilename()); - assertEquals(Chars.wrap(LINE_3), sourceManager.getSlice(mark1)); + assertEquals(LINE_3 + "\n", sourceManager.getSlice(mark1).toString()); assertEquals(4, mark2.getBeginLine()); assertEquals(fileName, mark2.getFilename()); - assertEquals(Chars.wrap(LINE_4), sourceManager.getSlice(mark2)); - } - - @Test - void testIgnore() throws IOException { - Language java = JavaLanguageModule.getInstance(); - LanguagePropertyBundle bundle = java.newPropertyBundle(); - bundle.setProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS, true); - bundle.setProperty(Tokenizer.CPD_ANONYMIZE_LITERALS, true); - Tokenizer tokenizer = java.createCpdTokenizer(bundle); - TextDocument sourceCode = TextDocument.readOnlyString(getSampleCode(), "Foo.java", java.getDefaultVersion()); - Tokens tokens = new Tokens(); - Tokenizer.tokenize(tokenizer, sourceCode, tokens); - - MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, 5); - matchAlgorithm.findMatches(); - Iterator matches = matchAlgorithm.matches(); - Match match = matches.next(); - assertFalse(matches.hasNext()); - - Iterator marks = match.iterator(); - marks.next(); - marks.next(); - marks.next(); - assertFalse(marks.hasNext()); + assertEquals(LINE_4 + "\n", sourceManager.getSlice(mark2).toString()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index 9ae3acac1c..b36c86e36e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang; import java.util.Objects; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.ast.ParseException; @@ -45,6 +46,14 @@ public class DummyLanguageModule extends SimpleLanguageModuleBase { return (DummyLanguageModule) Objects.requireNonNull(LanguageRegistry.PMD.getLanguageByFullName(NAME)); } + @Override + public LanguagePropertyBundle newPropertyBundle() { + LanguagePropertyBundle bundle = super.newPropertyBundle(); + bundle.definePropertyDescriptor(Tokenizer.CPD_ANONYMIZE_LITERALS); + bundle.definePropertyDescriptor(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); + return bundle; + } + public LanguageVersion getVersionWhereParserThrows() { return getVersion(PARSER_THROWS); } diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java index eadecc970f..703582bd3d 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java @@ -6,9 +6,9 @@ package net.sourceforge.pmd.lang.cpp; import net.sourceforge.pmd.cpd.CPPTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -19,7 +19,7 @@ public class CppLanguageModule extends CpdOnlyLanguageModuleBase { public static final PropertyDescriptor CPD_SKIP_BLOCKS = - PropertyFactory.stringProperty("cpdSkipBlocksPattern") + PropertyFactory.stringProperty("cpdSkipBlocksPattern") .defaultValue("#if 0|#endif") .desc("Specifies a start and end delimiter for CPD to completely ignore. " + "The delimiters are separated by a pipe |. The default skips code " diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java index 5f122b8e35..bd156eb6df 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/CsLanguageModule.java @@ -4,11 +4,11 @@ package net.sourceforge.pmd.lang.cs; -import net.sourceforge.pmd.cpd.CsTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.cs.cpd.CsTokenizer; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; /** * Defines the Language module for C#. diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java similarity index 99% rename from pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java rename to pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java index d58ccdb0d7..22fd33a9a2 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java @@ -1,12 +1,13 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.cs.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; diff --git a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java b/pmd-cs/src/test/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizerTest.java similarity index 69% rename from pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java rename to pmd-cs/src/test/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizerTest.java index d2d3e78f80..12d5317906 100644 --- a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java +++ b/pmd-cs/src/test/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizerTest.java @@ -2,28 +2,22 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.cs.cpd; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.Properties; - -import org.jetbrains.annotations.NotNull; +import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; -import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.cpd.test.LanguagePropertyConfig; import net.sourceforge.pmd.lang.ast.TokenMgrError; class CsTokenizerTest extends CpdTextComparisonTest { CsTokenizerTest() { - super(".cs"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/cs/cpd/testdata"; + super("cs", ".cs"); } @Test @@ -107,23 +101,28 @@ class CsTokenizerTest extends CpdTextComparisonTest { doTest("attributes", "_ignored", skipAttributes()); } - private Properties ignoreUsings() { + private LanguagePropertyConfig ignoreUsings() { return properties(true, false, false); } - private Properties skipLiteralSequences() { + private LanguagePropertyConfig skipLiteralSequences() { return properties(false, true, false); } - private Properties skipAttributes() { + private LanguagePropertyConfig skipAttributes() { return properties(false, false, true); } - private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { - Properties properties = new Properties(); - properties.setProperty(Tokenizer.IGNORE_USINGS, Boolean.toString(ignoreUsings)); - properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(ignoreLiteralSequences)); - properties.setProperty(Tokenizer.IGNORE_ANNOTATIONS, Boolean.toString(ignoreAttributes)); - return properties; + @Override + public @NonNull LanguagePropertyConfig defaultProperties() { + return properties(false, false, false); + } + + private LanguagePropertyConfig properties(boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { + return properties -> { + properties.setProperty(Tokenizer.CPD_IGNORE_IMPORTS, ignoreUsings); + properties.setProperty(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES, ignoreLiteralSequences); + properties.setProperty(Tokenizer.CPD_IGNORE_METADATA, ignoreAttributes); + }; } } diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java index 195eee9282..b137f4dece 100644 --- a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java +++ b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.lang.dart; -import net.sourceforge.pmd.lang.dart.cpd.DartTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.dart.cpd.DartTokenizer; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; /** * Language implementation for Dart diff --git a/pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java b/pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java index a2f5d1249d..803fde8ad4 100644 --- a/pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java +++ b/pmd-fortran/src/main/java/net/sourceforge/pmd/lang/fortran/FortranLanguageModule.java @@ -6,8 +6,8 @@ package net.sourceforge.pmd.lang.fortran; import net.sourceforge.pmd.cpd.AnyTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; /** * Language implementation for Fortran diff --git a/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java b/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java index 5bffc78db6..5d73c7cb5d 100644 --- a/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java +++ b/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/GherkinLanguageModule.java @@ -5,9 +5,9 @@ package net.sourceforge.pmd.lang.gherkin; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.gherkin.cpd.GherkinTokenizer; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; /** * Language implementation for Gherkin. diff --git a/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java index 7ea335f9a9..979e1da007 100644 --- a/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java +++ b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/GoLanguageModule.java @@ -5,9 +5,9 @@ package net.sourceforge.pmd.lang.go; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.go.cpd.GoTokenizer; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; public class GoLanguageModule extends CpdOnlyLanguageModuleBase { diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java index fc8e810fca..bfe683cfda 100644 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/GroovyLanguageModule.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.lang.groovy; -import net.sourceforge.pmd.lang.groovy.cpd.GroovyTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.groovy.cpd.GroovyTokenizer; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; /** * Language implementation for Groovy diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java index 57b094cfd7..b2c9ff2da9 100644 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java @@ -22,8 +22,8 @@ import groovyjarjarantlr.TokenStreamException; public class GroovyTokenizer implements Tokenizer { @Override - public void tokenize(TextDocument sourceCode, TokenFactory tokens) { - GroovyLexer lexer = new GroovyLexer(sourceCode.newReader()); + public void tokenize(TextDocument document, TokenFactory tokens) { + GroovyLexer lexer = new GroovyLexer(document.newReader()); TokenStream tokenStream = lexer.plumb(); try { diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java index 530b2d4a12..26149963cb 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java @@ -19,13 +19,13 @@ import net.sourceforge.pmd.lang.html.HtmlLanguageModule; public class HtmlTokenizer implements Tokenizer { @Override - public void tokenize(TextDocument sourceCode, TokenFactory tokens) { + public void tokenize(TextDocument document, TokenFactory tokens) { HtmlLanguageModule html = HtmlLanguageModule.getInstance(); try (LanguageProcessor processor = html.createProcessor(html.newPropertyBundle())) { ParserTask task = new ParserTask( - sourceCode, + document, SemanticErrorReporter.noop(), LanguageProcessorRegistry.singleton(processor) ); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java similarity index 98% rename from pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java rename to pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java index e0d74c8658..1dd4192786 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java @@ -1,12 +1,15 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.java.cpd; import java.util.Deque; import java.util.LinkedList; +import net.sourceforge.pmd.cpd.TokenEntry; +import net.sourceforge.pmd.cpd.TokenFactory; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokenizerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizerTest.java similarity index 63% rename from pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokenizerTest.java rename to pmd-java/src/test/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizerTest.java index 5f95ef8785..ebc1eea44b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokenizerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizerTest.java @@ -1,27 +1,20 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.java.cpd; -import java.util.Properties; - -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.cpd.test.LanguagePropertyConfig; +import net.sourceforge.pmd.lang.java.JavaLanguageModule; -// TODO - enable test -@Disabled("Needs to be enabled after java-grammar changes are finalized") class JavaTokenizerTest extends CpdTextComparisonTest { JavaTokenizerTest() { - super(".java"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/java/cpd/testdata"; + super(JavaLanguageModule.getInstance(), ".java"); } @Test @@ -85,32 +78,32 @@ class JavaTokenizerTest extends CpdTextComparisonTest { } - private static Properties ignoreAnnotations() { + private static LanguagePropertyConfig ignoreAnnotations() { return properties(true, false, false); } - private static Properties ignoreIdents() { + private static LanguagePropertyConfig ignoreIdents() { return properties(false, false, true); } - private static Properties ignoreLiterals() { + private static LanguagePropertyConfig ignoreLiterals() { return properties(false, true, false); } @Override - public Properties defaultProperties() { + public LanguagePropertyConfig defaultProperties() { return properties(false, false, false); } - private static Properties properties(boolean ignoreAnnotations, - boolean ignoreLiterals, - boolean ignoreIdents) { - Properties properties = new Properties(); - properties.setProperty(Tokenizer.IGNORE_ANNOTATIONS, Boolean.toString(ignoreAnnotations)); - properties.setProperty(Tokenizer.IGNORE_IDENTIFIERS, Boolean.toString(ignoreIdents)); - properties.setProperty(Tokenizer.IGNORE_LITERALS, Boolean.toString(ignoreLiterals)); - return properties; + private static LanguagePropertyConfig properties(boolean ignoreAnnotations, + boolean ignoreLiterals, + boolean ignoreIdents) { + return properties -> { + properties.setProperty(Tokenizer.CPD_IGNORE_METADATA, ignoreAnnotations); + properties.setProperty(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS, ignoreIdents); + properties.setProperty(Tokenizer.CPD_ANONYMIZE_LITERALS, ignoreLiterals); + }; } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java index f90fe242e8..fe3c46da3f 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java @@ -4,12 +4,12 @@ package net.sourceforge.pmd.lang.ecmascript; -import net.sourceforge.pmd.lang.ecmascript.cpd.EcmascriptTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ecmascript.ast.EcmascriptParser; +import net.sourceforge.pmd.lang.ecmascript.cpd.EcmascriptTokenizer; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; /** diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java index e3f012f8c2..7be1c12913 100644 --- a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/KotlinLanguageModule.java @@ -5,10 +5,10 @@ package net.sourceforge.pmd.lang.kotlin; import net.sourceforge.pmd.annotation.Experimental; -import net.sourceforge.pmd.cpd.KotlinTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; +import net.sourceforge.pmd.lang.kotlin.cpd.KotlinTokenizer; /** * Language Module for Kotlin diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinTokenizer.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java similarity index 88% rename from pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinTokenizer.java rename to pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java index 143c1f5f06..63aa140a95 100644 --- a/pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinTokenizer.java +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java @@ -1,16 +1,16 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.kotlin.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; +import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; -import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager; import net.sourceforge.pmd.lang.kotlin.ast.KotlinLexer; /** @@ -24,7 +24,7 @@ public class KotlinTokenizer extends AntlrTokenizer { } @Override - protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { + protected TokenManager filterTokenStream(TokenManager tokenManager) { return new KotlinTokenFilter(tokenManager); } @@ -40,7 +40,7 @@ public class KotlinTokenizer extends AntlrTokenizer { private boolean discardingPackageAndImport = false; private boolean discardingNL = false; - /* default */ KotlinTokenFilter(final AntlrTokenManager tokenManager) { + /* default */ KotlinTokenFilter(final TokenManager tokenManager) { super(tokenManager); } diff --git a/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizerTest.java similarity index 80% rename from pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java rename to pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizerTest.java index 1ab9bee77d..ea3dd542bb 100644 --- a/pmd-kotlin/src/test/java/net/sourceforge/pmd/cpd/KotlinTokenizerTest.java +++ b/pmd-kotlin/src/test/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.kotlin.cpd; import org.junit.jupiter.api.Test; @@ -14,11 +14,6 @@ class KotlinTokenizerTest extends CpdTextComparisonTest { super("kotlin", ".kt"); } - @Override - protected String getResourcePrefix() { - return "../lang/kotlin/cpd/testdata"; - } - @Test void testComments() { doTest("comment"); diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java index b9d8fb13ac..a0688f1a97 100644 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/LuaLanguageModule.java @@ -5,8 +5,8 @@ package net.sourceforge.pmd.lang.lua; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.lua.cpd.LuaTokenizer; /** @@ -18,8 +18,15 @@ public class LuaLanguageModule extends CpdOnlyLanguageModuleBase { super(LanguageMetadata.withId("lua").name("Lua").extensions("lua")); } + @Override + public LanguagePropertyBundle newPropertyBundle() { + LanguagePropertyBundle bundle = super.newPropertyBundle(); + bundle.definePropertyDescriptor(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES); + return bundle; + } + @Override public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { - return new LuaTokenizer(); + return new LuaTokenizer(bundle); } } diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java index 416b36596b..6f569e711a 100644 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java @@ -4,13 +4,13 @@ package net.sourceforge.pmd.lang.lua.cpd; -import java.util.Properties; - import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; import net.sourceforge.pmd.lang.lua.ast.LuaLexer; @@ -20,20 +20,10 @@ import net.sourceforge.pmd.lang.lua.ast.LuaLexer; */ public class LuaTokenizer extends AntlrTokenizer { - private boolean ignoreLiteralSequences = false; + private final boolean ignoreLiteralSequences; - /** - * Sets the possible options for the Lua tokenizer. - * - * @param properties the properties - * @see #OPTION_IGNORE_LITERAL_SEQUENCES - */ - public void setProperties(Properties properties) { - ignoreLiteralSequences = getBooleanProperty(properties, OPTION_IGNORE_LITERAL_SEQUENCES); - } - - private boolean getBooleanProperty(final Properties properties, final String property) { - return Boolean.parseBoolean(properties.getProperty(property, Boolean.FALSE.toString())); + public LuaTokenizer(LanguagePropertyBundle bundle) { + ignoreLiteralSequences = bundle.getProperty(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES); } @Override diff --git a/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java index dc33c1970d..5e8b414d1d 100644 --- a/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java +++ b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/MatlabLanguageModule.java @@ -5,8 +5,8 @@ package net.sourceforge.pmd.lang.matlab; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.matlab.cpd.MatlabTokenizer; /** diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java index a0bb56045a..25e115c2d6 100644 --- a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/ObjectiveCLanguageModule.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.lang.objectivec; -import net.sourceforge.pmd.lang.objectivec.cpd.ObjectiveCTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.objectivec.cpd.ObjectiveCTokenizer; /** * Defines the Language module for Objective-C diff --git a/pmd-objectivec/src/test/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizerTest.java b/pmd-objectivec/src/test/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizerTest.java index 720a369fce..33dffcf64b 100644 --- a/pmd-objectivec/src/test/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizerTest.java +++ b/pmd-objectivec/src/test/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizerTest.java @@ -15,11 +15,6 @@ class ObjectiveCTokenizerTest extends CpdTextComparisonTest { super("objectivec", ".m"); } - @Override - protected String getResourcePrefix() { - return "../lang/objectivec/cpd/testdata"; - } - @Test void testLongSample() { doTest("big_sample"); diff --git a/pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java b/pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java index e6305cc808..d0eba303db 100644 --- a/pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java +++ b/pmd-perl/src/main/java/net/sourceforge/pmd/lang/perl/PerlLanguageModule.java @@ -6,8 +6,8 @@ package net.sourceforge.pmd.lang.perl; import net.sourceforge.pmd.cpd.AnyTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; public class PerlLanguageModule extends CpdOnlyLanguageModuleBase { diff --git a/pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java b/pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java index dfe3516d7a..8e6d9f33d9 100644 --- a/pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java +++ b/pmd-php/src/main/java/net/sourceforge/pmd/lang/php/PhpLanguageModule.java @@ -6,8 +6,8 @@ package net.sourceforge.pmd.lang.php; import net.sourceforge.pmd.cpd.AnyTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; /** * Language implementation for PHP diff --git a/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java index ab6cdae1b2..194e8b6660 100644 --- a/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java +++ b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/PythonLanguageModule.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.lang.python; -import net.sourceforge.pmd.lang.python.cpd.PythonTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.python.cpd.PythonTokenizer; /** * Defines the Language module for Python diff --git a/pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java b/pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java index 65e8ef06d7..91952318e1 100644 --- a/pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java +++ b/pmd-ruby/src/main/java/net/sourceforge/pmd/lang/ruby/RubyLanguageModule.java @@ -6,8 +6,8 @@ package net.sourceforge.pmd.lang.ruby; import net.sourceforge.pmd.cpd.AnyTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; /** * Language implementation for Ruby. diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 227988325e..0a5eb4f9f5 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -38,20 +38,20 @@ public class ScalaTokenizer implements Tokenizer { } @Override - public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) { + public void tokenize(TextDocument document, TokenFactory tokenEntries) { try { - String fullCode = sourceCode.getText().toString(); + String fullCode = document.getText().toString(); // create the input file for scala - Input.VirtualFile vf = new Input.VirtualFile(sourceCode.getDisplayName(), fullCode); + Input.VirtualFile vf = new Input.VirtualFile(document.getDisplayName(), fullCode); ScalametaTokenizer tokenizer = new ScalametaTokenizer(vf, dialect); // tokenize with a filter scala.meta.tokens.Tokens tokens = tokenizer.tokenize(); // use extensions to the standard PMD TokenManager and Filter - ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator(), sourceCode); + ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator(), document); ScalaTokenFilter filter = new ScalaTokenFilter(scalaTokenManager); ScalaTokenAdapter token; @@ -68,7 +68,7 @@ public class ScalaTokenizer implements Tokenizer { TokenizeException tokE = (TokenizeException) e; Position pos = tokE.pos(); throw new TokenMgrError( - pos.startLine() + 1, pos.startColumn() + 1, sourceCode.getDisplayName(), "Scalameta threw", tokE); + pos.startLine() + 1, pos.startColumn() + 1, document.getDisplayName(), "Scalameta threw", tokE); } else { throw e; } diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java index 77c4700e75..77fb67f9a9 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/rule/bestpractices/UnavailableFunctionRule.java @@ -8,7 +8,6 @@ import java.util.List; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.ast.AstVisitor; -import net.sourceforge.pmd.lang.swift.rule.AbstractSwiftRule; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwAttribute; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwAttributes; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwCodeBlock; @@ -16,6 +15,7 @@ import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwFunctionDeclaration; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwInitializerDeclaration; import net.sourceforge.pmd.lang.swift.ast.SwiftParser.SwStatement; import net.sourceforge.pmd.lang.swift.ast.SwiftVisitorBase; +import net.sourceforge.pmd.lang.swift.rule.AbstractSwiftRule; public class UnavailableFunctionRule extends AbstractSwiftRule { From 519e9d366f78803bffbeb3a4ebc63bea6ca26345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 20:20:12 +0100 Subject: [PATCH 108/347] Fix java tests --- .../pmd/lang/java/JavaLanguageModule.java | 7 +++ .../pmd/lang/java/cpd/JavaTokenizer.java | 4 +- .../java/internal/JavaLanguageProperties.java | 4 ++ .../discardedElements_no_ignore_annots.txt | 4 +- .../ignoreIdentsPreservesClassLiteral.txt | 10 ++-- .../testdata/ignoreIdentsPreservesCtor.txt | 46 +++++++++---------- .../testdata/ignoreIdentsPreservesEnum.txt | 10 ++-- .../lang/java/cpd/testdata/ignoreLiterals.txt | 8 ++-- ...ignoreSpecialAnnotations_ignore_annots.txt | 8 ++-- 9 files changed, 56 insertions(+), 45 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java index 4bd8bbde05..522331adec 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java @@ -4,11 +4,13 @@ package net.sourceforge.pmd.lang.java; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.java.cpd.JavaTokenizer; import net.sourceforge.pmd.lang.java.internal.JavaLanguageProcessor; import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties; @@ -54,6 +56,11 @@ public class JavaLanguageModule extends LanguageModuleBase { return new JavaLanguageProcessor((JavaLanguageProperties) bundle); } + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new JavaTokenizer((JavaLanguageProperties) bundle); + } + public static Language getInstance() { return LanguageRegistry.PMD.getLanguageByFullName(NAME); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java index 1dd4192786..ef475c2b78 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java @@ -22,8 +22,8 @@ import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties; public class JavaTokenizer extends JavaCCTokenizer { - public static final String CPD_START = "\"CPD-START\""; - public static final String CPD_END = "\"CPD-END\""; + private static final String CPD_START = "\"CPD-START\""; + private static final String CPD_END = "\"CPD-END\""; private final boolean ignoreAnnotations; private final boolean ignoreLiterals; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProperties.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProperties.java index 31e09cf556..69ee99790a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProperties.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProperties.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.internal; import org.apache.commons.lang3.EnumUtils; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.JvmLanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.java.JavaLanguageModule; @@ -27,6 +28,9 @@ public class JavaLanguageProperties extends JvmLanguagePropertyBundle { public JavaLanguageProperties() { super(JavaLanguageModule.getInstance()); definePropertyDescriptor(INTERNAL_INFERENCE_LOGGING_VERBOSITY); + definePropertyDescriptor(Tokenizer.CPD_IGNORE_METADATA); + definePropertyDescriptor(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); + definePropertyDescriptor(Tokenizer.CPD_ANONYMIZE_LITERALS); } public static boolean isPreviewEnabled(LanguageVersion version) { diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt index 17a1259e12..9ce249a74f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt @@ -49,8 +49,8 @@ L31 [@] 28 29 [Nested] 29 35 L32 - [}] 14 15 - [)] 15 16 + [}] 9 10 + [)] 10 11 L33 [public] 5 11 [void] 12 16 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt index 8161367842..11a46cf1a4 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt @@ -2,7 +2,7 @@ L2 [public] 1 7 [class] 8 13 - [74] 14 17 + [80] 14 17 [{] 18 19 L3 [Foo] 5 8 @@ -14,16 +14,16 @@ L4 L5 [public] 5 11 [void] 12 16 - [74] 17 20 + [80] 17 20 [(] 20 21 [)] 21 22 [{] 23 24 L6 - [74] 9 12 + [80] 9 12 [.] 12 13 - [74] 13 16 + [80] 13 16 [(] 16 17 - [74] 17 20 + [80] 17 20 [.] 20 21 [class] 21 26 [Foo] 26 27 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt index 195cb5180d..0eb4a58cd0 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt @@ -2,25 +2,25 @@ L2 [public] 1 7 [class] 8 13 - [74] 14 17 + [80] 14 17 [extends] 18 25 - [74] 26 29 + [80] 26 29 [{] 30 31 L4 [private] 5 12 - [74] 13 16 - [74] 17 32 + [80] 13 16 + [80] 17 32 L6 [public] 5 11 [Foo] 12 15 [(] 15 16 [int] 16 19 - [74] 20 21 + [80] 20 21 [)] 21 22 [{] 23 24 [super] 25 30 [(] 30 31 - [74] 31 32 + [80] 31 32 [)] 32 33 [}] 35 36 L8 @@ -28,57 +28,57 @@ L8 [Foo] 13 16 [(] 16 17 [int] 17 20 - [74] 21 22 + [80] 21 22 [,] 22 23 - [74] 24 30 - [74] 31 32 + [80] 24 30 + [80] 31 32 [)] 32 33 [{] 34 35 [super] 36 41 [(] 41 42 - [74] 42 43 + [80] 42 43 [,] 43 44 - [74] 45 46 + [80] 45 46 [)] 46 47 [}] 49 50 L10 [Foo] 19 22 [(] 22 23 [int] 23 26 - [74] 27 28 + [80] 27 28 [,] 28 29 - [74] 30 36 - [74] 37 38 + [80] 30 36 + [80] 37 38 [,] 38 39 - [74] 40 46 - [74] 47 48 + [80] 40 46 + [80] 47 48 [)] 48 49 [{] 50 51 [super] 52 57 [(] 57 58 - [74] 58 59 + [80] 58 59 [,] 59 60 - [74] 61 62 + [80] 61 62 [,] 62 63 - [74] 64 65 + [80] 64 65 [)] 65 66 [}] 68 69 L12 [private] 5 12 [static] 13 19 [class] 20 25 - [74] 26 31 + [80] 26 31 [{] 32 33 L14 [Inner] 9 14 [(] 14 15 [)] 15 16 [{] 17 18 - [74] 19 25 + [80] 19 25 [.] 25 26 - [74] 26 29 + [80] 26 29 [.] 29 30 - [74] 30 37 + [80] 30 37 [(] 37 38 ["Guess who?"] 38 50 [)] 50 51 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt index ecbb4b7b12..82e10ffcf7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt @@ -1,17 +1,17 @@ [Image] or [Truncated image[ Bcol Ecol L2 [public] 1 7 - [74] 8 12 - [74] 13 16 + [80] 8 12 + [80] 13 16 [{] 17 18 L3 - [74] 5 8 + [80] 5 8 [(] 8 9 [1] 9 10 [)] 10 11 [,] 11 12 L4 - [74] 5 8 + [80] 5 8 [(] 8 9 [2] 9 10 [)] 10 11 @@ -19,7 +19,7 @@ L6 [Foo] 5 8 [(] 8 9 [int] 9 12 - [74] 13 16 + [80] 13 16 [)] 16 17 [{] 18 19 L7 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt index 578cb5162e..dc102bf6e9 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt @@ -18,7 +18,7 @@ L3 [.] 19 20 [println] 20 27 [(] 27 28 - [71] 28 35 + [75] 28 35 [)] 35 36 L4 [System] 9 15 @@ -27,13 +27,13 @@ L4 [.] 19 20 [println] 20 27 [(] 27 28 - [71] 28 35 + [75] 28 35 [)] 35 36 L5 [int] 9 12 [i] 13 14 [=] 15 16 - [5] 17 18 + [61] 17 18 L6 [System] 9 15 [.] 15 16 @@ -41,7 +41,7 @@ L6 [.] 19 20 [print] 20 25 [(] 25 26 - [71] 26 33 + [75] 26 33 [)] 33 34 L8 [}] 5 6 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt index c3fde40d3d..70ba02ec3a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreSpecialAnnotations_ignore_annots.txt @@ -1,7 +1,7 @@ [Image] or [Truncated image[ Bcol Ecol L13 - [class] 1 5 - [Other] 7 11 - [{] 13 13 - [}] 14 14 + [class] 1 6 + [Other] 7 12 + [{] 13 14 + [}] 14 15 EOF From 51b50161633cde8deeacd1c7ee52ec5540d72f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 20:31:18 +0100 Subject: [PATCH 109/347] Cleanups --- justfile | 54 ------------------- .../pmd/lang/apex/cpd/ApexTokenizerTest.java | 6 +++ .../net/sourceforge/pmd/cpd/Tokenizer.java | 12 ++++- .../java/net/sourceforge/pmd/cpd/Tokens.java | 29 ++++------ .../pmd/cpd/test/CpdTextComparisonTest.kt | 2 +- .../pmd/lang/vf/VfLanguageModule.java | 2 +- .../pmd/{ => lang/vf}/cpd/VfTokenizer.java | 2 +- .../pmd/lang/vf/cpd/VfTokenizerTest.java | 2 +- 8 files changed, 31 insertions(+), 78 deletions(-) delete mode 100644 justfile rename pmd-visualforce/src/main/java/net/sourceforge/pmd/{ => lang/vf}/cpd/VfTokenizer.java (97%) diff --git a/justfile b/justfile deleted file mode 100644 index bac7611772..0000000000 --- a/justfile +++ /dev/null @@ -1,54 +0,0 @@ - - -pmdJavaDeps := "pmd-core,pmd-lang-test,pmd-test,pmd-java" -commonBuildOpts := "-Dkotlin.compiler.incremental" - -genJavaAst: - rm -f pmd-java/target/generated-sources/javacc/last-generated-timestamp - mvnd generate-sources -pl pmd-java - -reGenAllSources: - rm -rf pmd-*/target/generated-sources - rm pmd-*/target/last-generated-timestamp - mvnd generate-sources - -install MOD: - mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl "pmd-{{MOD}}" - -alias i := install - -cleanInstallEverything *FLAGS: - mvnd clean install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -fae {{FLAGS}} - -installEverything *FLAGS: - mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -fae {{FLAGS}} - -testCore *FLAGS: - mvnd test checkstyle:check pmd:check -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl pmd-core {{FLAGS}} - -testJava *FLAGS: - mvnd test checkstyle:check pmd:check -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl pmd-java {{FLAGS}} - -installJavaAndDeps *FLAGS: - mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl {{pmdJavaDeps}} {{FLAGS}} - -installJava *FLAGS: - mvnd install -Dmaven.javadoc.skip -Dkotlin.compiler.incremental -pl pmd-java {{FLAGS}} - - -lintChanged DIFF="master" *FLAGS="": - #!/bin/env zsh - changed=$(git diff --name-only {{DIFF}}) - changed=${(f)changed} - projects=${changed%%/*} # remove all but first segment - echo $projects - # todo filter to pmd-* - mvnd checkstyle:check pmd:check -pl $projects -fae - - -lint projects="pmd-java": - mvnd checkstyle:check pmd:check -pl {{projects}} -fae - -lintAll *FLAGS: - mvnd checkstyle:check pmd:check -fae {{FLAGS}} - diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizerTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizerTest.java index 76467e63b1..4c93a423d4 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizerTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizerTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.apex.cpd; +import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.Tokenizer; @@ -44,6 +45,11 @@ class ApexTokenizerTest extends CpdTextComparisonTest { return properties(true); } + @Override + public @NonNull LanguagePropertyConfig defaultProperties() { + return properties(false); + } + private LanguagePropertyConfig properties(boolean caseSensitive) { return properties -> properties.setProperty(Tokenizer.CPD_CASE_SENSITIVE, caseSensitive); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index 5644159e34..508606b2c2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -41,16 +41,24 @@ public interface Tokenizer { .build(); PropertyDescriptor CPD_CASE_SENSITIVE = PropertyFactory.booleanProperty("cpdCaseSensitive") - .defaultValue(false) - .desc("Whether CPD should ignore the case of tokens. Affects all tokens.") + .defaultValue(true) + .desc("Whether CPD should respect the case of tokens. Affects all tokens.") .build(); @Deprecated // TODO what to do with this? String DEFAULT_SKIP_BLOCKS_PATTERN = "#if 0|#endif"; + /** + * Tokenize the source code and record tokens using the provided token factory. + * Implementations should not add an EOF token at the end. + */ void tokenize(TextDocument document, TokenFactory tokens) throws IOException; + /** + * Wraps a call to {@link #tokenize(TextDocument, TokenFactory)} to properly + * create and close the token factory. + */ static void tokenize(Tokenizer tokenizer, TextDocument textDocument, Tokens tokens) throws IOException { try (TokenFactory tf = TokenFactory.forFile(textDocument, tokens)) { tokenizer.tokenize(textDocument, tf); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index ebc86025ca..ba3dc0292b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.cpd; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -45,15 +44,11 @@ public class Tokens { return images.entrySet().stream().filter(it -> it.getValue() == i).findFirst().map(Entry::getKey).orElse(null); } - public TokenEntry peekLastToken() { - return get(size() - 1); + TokenEntry peekLastToken() { + return getToken(size() - 1); } - public Iterator iterator() { - return tokens.iterator(); - } - - private TokenEntry get(int index) { + private TokenEntry getToken(int index) { return tokens.get(index); } @@ -61,8 +56,8 @@ public class Tokens { return tokens.size(); } - public TokenEntry getEndToken(TokenEntry mark, Match match) { - return get(mark.getIndex() + match.getTokenCount() - 1); + TokenEntry getEndToken(TokenEntry mark, Match match) { + return getToken(mark.getIndex() + match.getTokenCount() - 1); } public List getTokens() { @@ -70,15 +65,12 @@ public class Tokens { } TokenEntry addToken(String image, String fileName, int startLine, int startCol, int endLine, int endCol) { - TokenEntry newToken = new TokenEntry(getImageId(image), fileName, - startLine, startCol, - endLine, endCol, - tokens.size()); + TokenEntry newToken = new TokenEntry(getImageId(image), fileName, startLine, startCol, endLine, endCol, tokens.size()); add(newToken); return newToken; } - public State savePoint() { + State savePoint() { return new State(this); } @@ -89,15 +81,16 @@ public class Tokens { static final class State { private final int tokenCount; - private final int tokensMapSize; + private final int curImageId; State(Tokens tokens) { this.tokenCount = tokens.tokens.size(); - this.tokensMapSize = tokens.images.size(); + this.curImageId = tokens.curImageId; } public void restore(Tokens tokens) { - tokens.images.entrySet().removeIf(e -> e.getValue() > tokensMapSize); + tokens.images.entrySet().removeIf(e -> e.getValue() >= curImageId); + tokens.curImageId = this.curImageId; final List entries = tokens.getTokens(); entries.subList(tokenCount, entries.size()).clear(); diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index f73386a117..6458cbcf52 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -94,7 +94,7 @@ abstract class CpdTextComparisonTest( var curLine = -1 - for (token in tokens.iterator()) { + for (token in tokens.tokens) { if (token.isEof) { append("EOF").appendLine() diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java index 3504d228e2..cba2e8c13d 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.lang.vf; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.VfTokenizer; +import net.sourceforge.pmd.lang.vf.cpd.VfTokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java similarity index 97% rename from pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java rename to pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java index 72d16b342c..f79bcf48cb 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/cpd/VfTokenizer.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.vf.cpd; import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java index 07842f43c6..0e67555e1e 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizerTest.java @@ -12,7 +12,7 @@ import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class VfTokenizerTest extends CpdTextComparisonTest { VfTokenizerTest() { - super(".page"); + super("vf", ".page"); } @Test From d6ec427aa8e8882c57919fa8bbda70d649519347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 12 Feb 2023 23:15:44 +0100 Subject: [PATCH 110/347] Doc --- .../adding_new_cpd_language.md | 144 +++++++++--------- .../pmd/lang/LanguageRegistry.java | 21 ++- 2 files changed, 91 insertions(+), 74 deletions(-) diff --git a/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md b/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md index 4590867fa8..ca62205242 100644 --- a/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md +++ b/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md @@ -2,96 +2,101 @@ title: How to add a new CPD language short_title: Add a new CPD language tags: [devdocs, extending] -summary: How to add a new CPD language -last_updated: March 18, 2019 (6.13.0) +summary: How to add a new language module with CPD support. +last_updated: 2023-02-13 (7.0.0) permalink: pmd_devdocs_major_adding_new_cpd_language.html -author: Matรญas Fraga +author: Matรญas Fraga, Clรฉment Fournier --- -First of all, thanks for the contribution! +## Adding support for a CPD language -Happily for you, to add CPD support for a new language is now easier than ever! +CPD works generically on the tokens produced by a {% jdoc core::cpd.Tokenizer %}. +To add support for a new language, the crucial piece is writing a tokenizer that +splits the source file into the tokens specific to your language. Thankfully you +can use a stock [Antlr grammar](https://github.com/antlr/grammars-v4) or JavaCC +grammar to generate a lexer for you. If you cannot use a lexer generator, for +instance because you are wrapping a lexer for another library, it is still relatively +easy to implement the Tokenizer interface. -{% include callout.html content="**Pro Tip**: If you wish to add a new language, there are more than 50 languages you could easily add with just an [Antlr grammar](https://github.com/antlr/grammars-v4)." type="primary" %} +Use the following guide to set up a new language module that supports CPD. -All you need to do is follow this few steps: +1. Create a new Maven module for your language. You can take [the Golang module](https://github.com/pmd/pmd/tree/master/pmd-go/pom.xml) as an example. -1. Create a new module for your language, you can take [the Golang module](https://github.com/pmd/pmd/tree/master/pmd-go) as an example -2. Create a Tokenizer - - - For Antlr grammars you can take the grammar from [here](https://github.com/antlr/grammars-v4) and extend [AntlrTokenizer](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java) taking Go as an example - - - ```java - public class GoTokenizer extends AntlrTokenizer { - - @Override protected AntlrTokenManager getLexerForSource(SourceCode sourceCode) { - CharStream charStream = AntlrTokenizer.getCharStreamFromSourceCode(sourceCode); - return new AntlrTokenManager(new GolangLexer(charStream), sourceCode.getFileName()); - } - } +2. Implement a {% jdoc core::cpd.Tokenizer %}. + - For Antlr grammars you can take the grammar from [antlr/grammars-v4](https://github.com/antlr/grammars-v4) and place it in `src/main/antlr4` followed by the package name of the language. You then need to call the appropriate ant wrapper to generate + the lexer from the grammar. To do so, edit `pom.xml` (eg like [the Golang module](https://github.com/pmd/pmd/tree/master/pmd-go/pom.xml)). + Once that is done, `mvn generate-sources` should generate the lexer sources for you. + + You can now implement a tokenizer, for instance by extending {% jdoc core::cpd.impl.AntlrTokenizer %}. The following reproduces the Go implementation: + ```java + // mind the package convention if you are going to make a PR + package net.sourceforge.pmd.lang.go.cpd; + + public class GoTokenizer extends AntlrTokenizer { + + @Override + protected Lexer getLexerForSource(CharStream charStream) { + return new GolangLexer(charStream); + } + } ``` - - For JavaCC grammars you should subclass [JavaCCTokenizer](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java) which has many examples you could follow, you should also take the [Python implementation](https://github.com/pmd/pmd/blob/master/pmd-python/src/main/java/net/sourceforge/pmd/cpd/PythonTokenizer.java) as reference - - For any other scenario you can use [AnyTokenizer](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AnyTokenizer.java) + - For JavaCC grammars, place your grammar in `etc/grammar` and edit the `pom.xml` like the [Python implementation](https://github.com/pmd/pmd/blob/master/pmd-python/pom.xml) does. + You can then subclass {% jdoc core::cpd.impl.JavaCCTokenizer %} instead of AntlrTokenizer. + - For any other scenario just implement the interface however you can. Look at the Scala or Apex module for existing implementations. - If you're using Antlr or JavaCC, update the pom.xml of your submodule to use the appropriate ant wrapper. See `pmd-go/pom.xml` and `pmd-python/pom.xml` for examples. - -3. Create your [Language](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/cpd/AbstractLanguage.java) class +3. Create a {% jdoc core::lang.Language %} implementation and override `createCpdTokenizer`. +If your language only supports CPD, then you can subclass {% jdoc core::lang.impl.CpdOnlyLanguageModuleBase %}: - ```java - public class GoLanguage extends AbstractLanguage { + ```java + // mind the package convention if you are going to make a PR + package net.sourceforge.pmd.lang.go; + + public class GoLanguageModule extends CpdOnlyLanguageModuleBase { - public GoLanguage() { - super("Go", "go", new GoTokenizer(), ".go"); - } + public GoLanguageModule() { + super(LanguageMetadata.withId("go").name("Go").extensions("go")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new GoTokenizer(); + } } - ``` - - {% include callout.html content="**Pro Tip**: Yes, keep looking at Go!" type="primary" %} - - **You are almost there!** - -4. Update the list of supported languages + ``` - - Write the fully-qualified name of your Language class to the file `src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language` + To make PMD find the language module at run time, write the fully-qualified name of your language class into the file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`. - - Update the test that asserts the list of supported languages by updating the `SUPPORTED_LANGUAGES` constant in [BinaryDistributionIT](https://github.com/pmd/pmd/blob/master/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java) +4. Update the test that asserts the list of supported languages by updating the `SUPPORTED_LANGUAGES` constant in [BinaryDistributionIT](https://github.com/pmd/pmd/blob/master/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java). -5. Please don't forget to add some test, you can again.. look at Go implementation ;) - - If you read this far, I'm keen to think you would also love to support some extra CPD configuration (ignore imports or crazy things like that) - If that's your case , you came to the right place! - -6. You can add your custom properties using a Token filter - - - For Antlr grammars all you need to do is implement your own [AntlrTokenFilter](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java) - - And by now, I know where you are going to look... - - **WRONG** - - Why do you want GO to solve all your problems? - - You should take a look to [Kotlin token filter implementation](https://github.com/pmd/pmd/blob/master/pmd-kotlin/src/main/java/net/sourceforge/pmd/cpd/KotlinTokenizer.java) - - - For non-Antlr grammars you can use [BaseTokenFilter](https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java) directly or take a peek to [Java's token filter](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java) +5. Add some tests for your tokenizer by following the [section below](#testing-your-implementation). + +### Declaring tokenizer options + +To make the tokenizer configurable, first define some property descriptors using +{% jdoc core::properties.PropertyFactory %}. Look at {% jdoc core::cpd.Tokenizer %} +for some predefined ones which you can reuse. You need to override {% jdoc core::Language#newPropertyBundle() %} +and call `definePropertyDescriptor` to register your descriptors. +After that you can access the values of the properties from the parameter +of {% jdoc core::lang.Language#createCpdTokenizer(core::properties.LanguagePropertyBundle) %}. + +To implement simple token filtering, you can use {% jdoc core::cpd.impl.BaseTokenFilter %} +as a base class, or eg {% jdoc core::cpd.impl.AntlrTokenFilter %} if you have an Antlr grammar. Take a look at the [Kotlin token filter implementation](https://github.com/pmd/pmd/blob/master/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java), or the [Java one](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java). ### Testing your implementation Add a Maven dependency on `pmd-lang-test` (scope `test`) in your `pom.xml`. -This contains utilities to test your Tokenizer. - -For simple tests, create a test class extending from `CpdTextComparisonTest`. -That class is written in Kotlin, but you can extend it in Java as well. +This contains utilities to test your tokenizer. +Create a test class extending from {% lang-test::cpd.test.CpdTextComparisonTest %}. To add tests, you need to write regular JUnit `@Test`-annotated methods, and call the method `doTest` with the name of the test file. For example, for the Dart language: ```java +package net.sourceforge.pmd.lang.dart.cpd; public class DartTokenizerTest extends CpdTextComparisonTest { @@ -101,20 +106,15 @@ public class DartTokenizerTest extends CpdTextComparisonTest { public DartTokenizerTest() { - super(".dart"); // the file extension for the dart language + super("dart", ".dart"); // the ID of the language, then the file extension used by test files } @Override protected String getResourcePrefix() { - // If your class is in src/test/java /some/package - // you need to place the test files in src/test/resources/some/package/cpdData - return "cpdData"; - } - - @Override - public Tokenizer newTokenizer() { - // Override this abstract method to return the correct tokenizer - return new DartTokenizer(); + // "testdata" is the default value, you don't need to override. + // This specifies that you should place the test files in + // src/test/resources/net/sourceforge/pmd/lang/dart/cpd/testdata + return "testdata"; } /************** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java index 5cfd9f9baf..205be5bf14 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java @@ -16,6 +16,7 @@ import java.util.ServiceLoader; import java.util.Set; import java.util.TreeSet; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; @@ -36,12 +37,20 @@ public final class LanguageRegistry implements Iterable { private static final Logger LOG = LoggerFactory.getLogger(LanguageRegistry.class); + private static final LanguageRegistry ALL_LANGUAGES = + loadLanguages(LanguageRegistry.class.getClassLoader()); + /** * Contains the languages that support PMD and are found on the classpath * of the classloader of this class. This can be used as a "default" registry. */ - public static final LanguageRegistry PMD = loadLanguages(LanguageRegistry.class.getClassLoader()); - public static final LanguageRegistry CPD = loadLanguages(LanguageRegistry.class.getClassLoader()); // todo + public static final LanguageRegistry PMD = ALL_LANGUAGES.filter(Language::supportsParsing); + + /** + * Contains the languages that support CPD and are found on the classpath + * of the classloader of this class. + */ + public static final LanguageRegistry CPD = ALL_LANGUAGES; private final Set languages; @@ -60,6 +69,14 @@ public final class LanguageRegistry implements Iterable { this.languagesByFullName = CollectionUtil.associateBy(languages, Language::getName); } + /** + * Create a new registry with the languages that satisfy the predicate. + */ + public LanguageRegistry filter(Predicate filterFun) { + return new LanguageRegistry(languages.stream().filter(filterFun) + .collect(Collectors.toSet())); + } + /** * Creates a language registry containing a single language. Note * that this may be inconvertible to a {@link LanguageProcessorRegistry} From 9c3434a07b3fade47a3d27922902e0f6ca16271a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 13 Feb 2023 14:53:14 +0100 Subject: [PATCH 111/347] Split cpd/pmd specific methods into... subinterfaces of Language --- .../pmd/lang/apex/ApexLanguageModule.java | 9 ++-- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 3 +- .../pmd/cpd/CpdCapableLanguage.java | 32 +++++++++++++ .../pmd/cpd/PmdCapableLanguage.java | 31 ++++++++++++ .../net/sourceforge/pmd/lang/Language.java | 47 ------------------- .../pmd/lang/LanguageProcessorRegistry.java | 7 ++- .../pmd/lang/LanguageRegistry.java | 6 ++- .../pmd/lang/PlainTextLanguage.java | 3 +- .../lang/impl/CpdOnlyLanguageModuleBase.java | 8 +--- .../lang/impl/SimpleLanguageModuleBase.java | 12 ++--- .../pmd/cpd/MatchAlgorithmTest.java | 3 +- .../pmd/lang/DummyLanguageModule.java | 3 +- .../pmd/lang/LanguageModuleBaseTest.java | 10 ++-- .../pmd/lang/java/JavaLanguageModule.java | 9 ++-- .../ecmascript/EcmascriptLanguageModule.java | 5 +- .../pmd/lang/jsp/JspLanguageModule.java | 3 +- .../pmd/cpd/test/CpdTextComparisonTest.kt | 4 +- .../pmd/lang/ast/test/BaseParsingHelper.kt | 5 +- .../pmd/lang/plsql/PLSQLLanguageModule.java | 5 +- .../pmd/lang/vf/VfLanguageModule.java | 5 +- 20 files changed, 115 insertions(+), 95 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java index 5ef1260d54..489fa4c194 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java @@ -4,8 +4,9 @@ package net.sourceforge.pmd.lang.apex; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; @@ -14,7 +15,7 @@ import net.sourceforge.pmd.lang.apex.cpd.ApexTokenizer; import apex.jorje.services.Version; -public class ApexLanguageModule extends LanguageModuleBase { +public class ApexLanguageModule extends LanguageModuleBase implements PmdCapableLanguage, CpdCapableLanguage { public static final String NAME = "Apex"; public static final String TERSE_NAME = "apex"; @@ -39,7 +40,7 @@ public class ApexLanguageModule extends LanguageModuleBase { return new ApexTokenizer((ApexLanguageProperties) bundle); } - public static Language getInstance() { - return LanguageRegistry.PMD.getLanguageByFullName(NAME); + public static ApexLanguageModule getInstance() { + return (ApexLanguageModule) LanguageRegistry.PMD.getLanguageById(TERSE_NAME); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 0d8da039f6..2e4e5911a0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -136,7 +136,8 @@ public final class CpdAnalysis implements AutoCloseable { sourceManager.getTextFiles().stream() .map(it -> it.getLanguageVersion().getLanguage()) .distinct() - .collect(Collectors.toMap(lang -> lang, lang -> lang.createCpdTokenizer(configuration.getLanguageProperties(lang)))); + .filter(it -> it instanceof CpdCapableLanguage) + .collect(Collectors.toMap(lang -> lang, lang -> ((CpdCapableLanguage) lang).createCpdTokenizer(configuration.getLanguageProperties(lang)))); Map numberOfTokensPerFile = new HashMap<>(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java new file mode 100644 index 0000000000..45c54f7494 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java @@ -0,0 +1,32 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * A language that also supports {@link CpdAnalysis CPD}. + * + * @author Clรฉment Fournier + */ +public interface CpdCapableLanguage extends Language { + + + /** + * Create a new {@link Tokenizer} for this language, given + * a property bundle with configuration. The bundle was created by + * this instance using {@link #newPropertyBundle()}. It can be assumed + * that the bundle will never be mutated anymore, and this method + * takes ownership of it. + * + * @param bundle A bundle of properties created by this instance. + * + * @return A new language processor + */ + default Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer(); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java new file mode 100644 index 0000000000..06e50ea658 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java @@ -0,0 +1,31 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageProcessor; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; + +/** + * A language that also supports {@link CpdAnalysis CPD}. + * + * @author Clรฉment Fournier + */ +public interface PmdCapableLanguage extends Language { + + /** + * Create a new {@link LanguageProcessor} for this language, given + * a property bundle with configuration. The bundle was created by + * this instance using {@link #newPropertyBundle()}. It can be assumed + * that the bundle will never be mutated anymore, and this method + * takes ownership of it. + * + * @param bundle A bundle of properties created by this instance. + * + * @return A new language processor + */ + LanguageProcessor createProcessor(LanguagePropertyBundle bundle); + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java index 53e865c7d6..ad31b2afb3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java @@ -8,9 +8,6 @@ import java.util.List; import java.util.ServiceLoader; import java.util.Set; -import net.sourceforge.pmd.cpd.AnyTokenizer; -import net.sourceforge.pmd.cpd.Tokenizer; - /** * Represents a language module, and provides access to language-specific * functionality. You can get a language instance from a {@link LanguageRegistry}. @@ -159,50 +156,6 @@ public interface Language extends Comparable { return new LanguagePropertyBundle(this); } - /** - * Return true if this language supports parsing files into an AST. - * In that case {@link #createProcessor(LanguagePropertyBundle)} should - * also be implemented. - */ - default boolean supportsParsing() { - return false; - } - - /** - * Create a new {@link LanguageProcessor} for this language, given - * a property bundle with configuration. The bundle was created by - * this instance using {@link #newPropertyBundle()}. It can be assumed - * that the bundle will never be mutated anymore, and this method - * takes ownership of it. - * - * @param bundle A bundle of properties created by this instance. - * - * @return A new language processor - * - * @throws UnsupportedOperationException if this language does not support PMD - */ - default LanguageProcessor createProcessor(LanguagePropertyBundle bundle) { - throw new UnsupportedOperationException(this + " does not support running a PMD analysis."); - } - - - /** - * Create a new {@link Tokenizer} for this language, given - * a property bundle with configuration. The bundle was created by - * this instance using {@link #newPropertyBundle()}. It can be assumed - * that the bundle will never be mutated anymore, and this method - * takes ownership of it. - * - * @param bundle A bundle of properties created by this instance. - * - * @return A new language processor - * - * @throws UnsupportedOperationException if this language does not support CPD - */ - default Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { - return new AnyTokenizer(); - } - /** * Returns a set of the IDs of languages that this language instance diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java index 506ca11d2f..69e8b9e89a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java @@ -18,6 +18,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertySource; @@ -119,6 +120,10 @@ public final class LanguageProcessorRegistry implements AutoCloseable { MessageReporter messageReporter) { Set processors = new HashSet<>(); for (Language language : registry) { + if (!(language instanceof PmdCapableLanguage)) { + LOG.trace("Not instantiating language {} because it does not support PMD", language); + continue; + } LanguagePropertyBundle properties = languageProperties.getOrDefault(language, language.newPropertyBundle()); if (!properties.getLanguage().equals(language)) { throw new IllegalArgumentException("Mismatched language"); @@ -128,7 +133,7 @@ public final class LanguageProcessorRegistry implements AutoCloseable { // readLanguagePropertiesFromEnv(properties, messageReporter); - processors.add(language.createProcessor(properties)); + processors.add(((PmdCapableLanguage) language).createProcessor(properties)); } catch (IllegalArgumentException e) { messageReporter.error(e); // todo } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java index 205be5bf14..5853a4f745 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java @@ -25,6 +25,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.annotation.DeprecatedUntil700; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.util.CollectionUtil; /** @@ -44,13 +46,13 @@ public final class LanguageRegistry implements Iterable { * Contains the languages that support PMD and are found on the classpath * of the classloader of this class. This can be used as a "default" registry. */ - public static final LanguageRegistry PMD = ALL_LANGUAGES.filter(Language::supportsParsing); + public static final LanguageRegistry PMD = ALL_LANGUAGES.filter(it -> it instanceof PmdCapableLanguage); /** * Contains the languages that support CPD and are found on the classpath * of the classloader of this class. */ - public static final LanguageRegistry CPD = ALL_LANGUAGES; + public static final LanguageRegistry CPD = ALL_LANGUAGES.filter(it -> it instanceof CpdCapableLanguage); private final Set languages; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java index 07945c453a..4df7968745 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/PlainTextLanguage.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.cpd.AnyTokenizer; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.Parser; @@ -26,7 +27,7 @@ import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; * @since 6.48.0 */ @Experimental -public final class PlainTextLanguage extends SimpleLanguageModuleBase { +public final class PlainTextLanguage extends SimpleLanguageModuleBase implements CpdCapableLanguage { private static final Language INSTANCE = new PlainTextLanguage(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/CpdOnlyLanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/CpdOnlyLanguageModuleBase.java index f67f7e7dec..b66848e2ed 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/CpdOnlyLanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/CpdOnlyLanguageModuleBase.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.impl; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; @@ -13,7 +14,7 @@ import net.sourceforge.pmd.lang.LanguagePropertyBundle; * * @author Clรฉment Fournier */ -public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase { +public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase implements CpdCapableLanguage { /** * Construct a module instance using the given metadata. The metadata must @@ -25,11 +26,6 @@ public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase { super(metadata); } - @Override - public boolean supportsParsing() { - return false; - } - @Override public abstract Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java index 23cf5ee7b1..ebce185f6a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/SimpleLanguageModuleBase.java @@ -8,6 +8,8 @@ import java.util.function.Function; import org.checkerframework.checker.nullness.qual.NonNull; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; @@ -15,12 +17,13 @@ import net.sourceforge.pmd.lang.LanguageVersionHandler; /** * The simplest implementation of a language, where only a {@link LanguageVersionHandler} - * needs to be implemented. + * needs to be implemented. A default {@link CpdCapableLanguage} implementation + * is provided. * * @author Clรฉment Fournier * @since 7.0.0 */ -public abstract class SimpleLanguageModuleBase extends LanguageModuleBase { +public class SimpleLanguageModuleBase extends LanguageModuleBase implements PmdCapableLanguage, CpdCapableLanguage { private final Function handler; @@ -33,11 +36,6 @@ public abstract class SimpleLanguageModuleBase extends LanguageModuleBase { this.handler = makeHandler; } - @Override - public boolean supportsParsing() { - return true; - } - @Override public LanguageProcessor createProcessor(LanguagePropertyBundle bundle) { LanguageVersionHandler services = handler.apply(bundle); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index 03f4061255..6fb50927d6 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -14,7 +14,6 @@ import java.util.Iterator; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.DummyLanguageModule; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextFile; @@ -36,7 +35,7 @@ class MatchAlgorithmTest { @Test void testSimple() throws IOException { - Language dummy = DummyLanguageModule.getInstance(); + DummyLanguageModule dummy = DummyLanguageModule.getInstance(); Tokenizer tokenizer = dummy.createCpdTokenizer(dummy.newPropertyBundle()); String fileName = "Foo.dummy"; TextFile textFile = TextFile.forCharSeq(getSampleCode(), fileName, dummy.getDefaultVersion()); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index b36c86e36e..749124c65a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang; import java.util.Objects; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; @@ -22,7 +23,7 @@ import net.sourceforge.pmd.reporting.ViolationDecorator; /** * Dummy language used for testing PMD. */ -public class DummyLanguageModule extends SimpleLanguageModuleBase { +public class DummyLanguageModule extends SimpleLanguageModuleBase implements CpdCapableLanguage { public static final String NAME = "Dummy"; public static final String TERSE_NAME = "dummy"; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageModuleBaseTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageModuleBaseTest.java index 64fae5a9c8..f14c208f69 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageModuleBaseTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageModuleBaseTest.java @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata; +import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; /** * @author Clรฉment Fournier @@ -57,12 +58,9 @@ class LanguageModuleBaseTest { } private static LanguageModuleBase makeLanguage(LanguageMetadata meta) { - return new LanguageModuleBase(meta) { - @Override - public LanguageProcessor createProcessor(LanguagePropertyBundle bundle) { - throw new UnsupportedOperationException("fake instance"); - } - }; + return new SimpleLanguageModuleBase(meta, p -> { + throw new UnsupportedOperationException("fake instance"); + }); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java index 522331adec..1347ace2bf 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/JavaLanguageModule.java @@ -4,8 +4,9 @@ package net.sourceforge.pmd.lang.java; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; @@ -17,7 +18,7 @@ import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties; /** * Created by christoferdutz on 20.09.14. */ -public class JavaLanguageModule extends LanguageModuleBase { +public class JavaLanguageModule extends LanguageModuleBase implements PmdCapableLanguage, CpdCapableLanguage { public static final String NAME = "Java"; public static final String TERSE_NAME = "java"; @@ -61,7 +62,7 @@ public class JavaLanguageModule extends LanguageModuleBase { return new JavaTokenizer((JavaLanguageProperties) bundle); } - public static Language getInstance() { - return LanguageRegistry.PMD.getLanguageByFullName(NAME); + public static JavaLanguageModule getInstance() { + return (JavaLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME); } } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java index fe3c46da3f..508ef6629a 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptLanguageModule.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.ecmascript; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.ecmascript.ast.EcmascriptParser; @@ -26,8 +25,8 @@ public class EcmascriptLanguageModule extends SimpleLanguageModuleBase { properties -> () -> new EcmascriptParser(properties)); } - public static Language getInstance() { - return LanguageRegistry.PMD.getLanguageByFullName(NAME); + public static EcmascriptLanguageModule getInstance() { + return (EcmascriptLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME); } @Override diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java index 89a68e336d..1fd9b0ca88 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspLanguageModule.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.jsp; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; @@ -12,7 +13,7 @@ import net.sourceforge.pmd.lang.jsp.cpd.JSPTokenizer; /** * Created by christoferdutz on 20.09.14. */ -public class JspLanguageModule extends SimpleLanguageModuleBase { +public class JspLanguageModule extends SimpleLanguageModuleBase implements CpdCapableLanguage { public static final String NAME = "Java Server Pages"; public static final String TERSE_NAME = "jsp"; diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt index 6458cbcf52..73f69075cd 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/cpd/test/CpdTextComparisonTest.kt @@ -24,12 +24,12 @@ import java.util.* * Baseline files are saved in txt files. */ abstract class CpdTextComparisonTest( - val language: Language, + val language: CpdCapableLanguage, override val extensionIncludingDot: String ) : BaseTextComparisonTest() { constructor(langId: String, extensionIncludingDot: String) : this( - LanguageRegistry.CPD.getLanguageById(langId)!!, + LanguageRegistry.CPD.getLanguageById(langId) as CpdCapableLanguage, extensionIncludingDot ) diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 06647a855d..d38cc1bb1a 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.ast.test import net.sourceforge.pmd.* +import net.sourceforge.pmd.cpd.PmdCapableLanguage import net.sourceforge.pmd.internal.util.IOUtil import net.sourceforge.pmd.lang.* import net.sourceforge.pmd.lang.ast.Node @@ -64,9 +65,9 @@ abstract class BaseParsingHelper, T : RootNode ?: throw AssertionError("Unsupported version $version for language $language") } - val language: Language + val language: PmdCapableLanguage get() = - params.languageRegistry.getLanguageByFullName(langName) + params.languageRegistry.getLanguageByFullName(langName) as? PmdCapableLanguage? ?: run { val langNames = params.languageRegistry.commaSeparatedList { it.name } throw AssertionError("'$langName' is not a supported language (available $langNames)") diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java index 66bfdf0f32..1fcf3d2555 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.plsql; import net.sourceforge.pmd.cpd.PLSQLTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; @@ -50,7 +49,7 @@ public class PLSQLLanguageModule extends SimpleLanguageModuleBase { return new PLSQLTokenizer(bundle); } - public static Language getInstance() { - return LanguageRegistry.PMD.getLanguageById("plsql"); + public static PLSQLLanguageModule getInstance() { + return (PLSQLLanguageModule) LanguageRegistry.PMD.getLanguageById("plsql"); } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java index cba2e8c13d..72d0ff6e1e 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java @@ -4,19 +4,20 @@ package net.sourceforge.pmd.lang.vf; +import net.sourceforge.pmd.cpd.CpdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.vf.cpd.VfTokenizer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; +import net.sourceforge.pmd.lang.vf.cpd.VfTokenizer; /** * @author sergey.gorbaty */ -public class VfLanguageModule extends SimpleLanguageModuleBase { +public class VfLanguageModule extends SimpleLanguageModuleBase implements CpdCapableLanguage { public static final String NAME = "Salesforce VisualForce"; public static final String TERSE_NAME = "vf"; From c572cb88d7c74a5b8b2edddc972bc18e7a9fb251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 13 Feb 2023 15:18:37 +0100 Subject: [PATCH 112/347] Rename package cpd.internal to cpd.impl --- .../sourceforge/pmd/cpd/{internal => impl}/AntlrTokenizer.java | 2 +- .../sourceforge/pmd/cpd/{internal => impl}/JavaCCTokenizer.java | 2 +- .../sourceforge/pmd/cpd/{internal => impl}/TokenizerBase.java | 2 +- .../pmd/lang/ast/impl/javacc/JavaccTokenDocument.java | 2 +- pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java | 2 +- .../main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java | 2 +- .../java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java | 2 +- .../net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizer.java | 2 +- .../main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java | 2 +- .../java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java | 2 +- .../pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java | 2 +- .../java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java | 2 +- .../net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java | 2 +- .../java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java | 2 +- .../net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java | 2 +- .../main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java | 2 +- .../pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java | 2 +- .../src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java | 2 +- .../net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java | 2 +- .../java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java | 2 +- .../main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java | 2 +- .../java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/cpd/{internal => impl}/AntlrTokenizer.java (95%) rename pmd-core/src/main/java/net/sourceforge/pmd/cpd/{internal => impl}/JavaCCTokenizer.java (84%) rename pmd-core/src/main/java/net/sourceforge/pmd/cpd/{internal => impl}/TokenizerBase.java (97%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java similarity index 95% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java rename to pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java index d5a3472281..0d15d2f659 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/AntlrTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.internal; +package net.sourceforge.pmd.cpd.impl; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenizer.java similarity index 84% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java rename to pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenizer.java index 3a629d5af4..e6feb5bed9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/JavaCCTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenizer.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.internal; +package net.sourceforge.pmd.cpd.impl; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java rename to pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java index 1cd04359d1..3196849e49 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/internal/TokenizerBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.internal; +package net.sourceforge.pmd.cpd.impl; import java.io.IOException; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java index 6b13d723d4..5887805ec4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/JavaccTokenDocument.java @@ -10,7 +10,7 @@ import java.util.List; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.ast.impl.TokenDocument; import net.sourceforge.pmd.lang.document.TextDocument; diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java index bb9f5c2811..043696bb27 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java @@ -8,7 +8,7 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; -import net.sourceforge.pmd.cpd.internal.TokenizerBase; +import net.sourceforge.pmd.cpd.impl.TokenizerBase; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java index 22fd33a9a2..1719019e4c 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java @@ -8,7 +8,7 @@ import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java index 2a0fc4a1b9..2daf6ec443 100644 --- a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java +++ b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.dart.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizer.java b/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizer.java index fd9d03a244..1bbe22cf51 100644 --- a/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizer.java +++ b/pmd-gherkin/src/main/java/net/sourceforge/pmd/lang/gherkin/cpd/GherkinTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.gherkin.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.lang.gherkin.ast.GherkinLexer; /** diff --git a/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java index 54f86a77a9..a4d9526a52 100644 --- a/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java +++ b/pmd-go/src/main/java/net/sourceforge/pmd/lang/go/cpd/GoTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.go.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.lang.go.ast.GolangLexer; public class GoTokenizer extends AntlrTokenizer { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java index ef475c2b78..c0f884bbfe 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java @@ -10,7 +10,7 @@ import java.util.LinkedList; import net.sourceforge.pmd.cpd.TokenEntry; import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java index a1e08e2e61..34a0d42f30 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/cpd/EcmascriptTokenizer.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.ecmascript.cpd; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java index dd6892708f..1a6b8044ab 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/cpd/JSPTokenizer.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.jsp.cpd; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java index 63aa140a95..c46c8be666 100644 --- a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.kotlin.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java index 6f569e711a..6d7942247c 100644 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java @@ -8,7 +8,7 @@ import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java index 1a275cd4ef..269deed52b 100644 --- a/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java +++ b/pmd-matlab/src/main/java/net/sourceforge/pmd/lang/matlab/cpd/MatlabTokenizer.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.matlab.cpd; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java index 140d9a941d..a51a8518b4 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.cpd; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; diff --git a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java index 2e21fdc7c8..fb17635fce 100644 --- a/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java +++ b/pmd-objectivec/src/main/java/net/sourceforge/pmd/lang/objectivec/cpd/ObjectiveCTokenizer.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.objectivec.cpd; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java index e6ca50c0ec..bc35e3c957 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.cpd; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; diff --git a/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java index b9194d0720..49c301a128 100644 --- a/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java +++ b/pmd-python/src/main/java/net/sourceforge/pmd/lang/python/cpd/PythonTokenizer.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.python.cpd; import java.util.regex.Pattern; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java index 868a4b0034..ae4ed9d5b0 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/cpd/SwiftTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.swift.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.lang.swift.ast.SwiftLexer; /** diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java index f79bcf48cb..f7c669333b 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/cpd/VfTokenizer.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.vf.cpd; -import net.sourceforge.pmd.cpd.internal.JavaCCTokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaEscapeTranslator; diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java index e15b9d6b22..5b24f1ef1f 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.xml.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.lang.xml.antlr4.XMLLexer; public class XmlTokenizer extends AntlrTokenizer { From 30a7f07d1c4b5d880db8f13d0dca358a59e195fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 13 Feb 2023 15:26:48 +0100 Subject: [PATCH 113/347] Cleanups --- .../pmd/lang/apex/cpd/ApexCpdTest.java | 2 +- .../sourceforge/pmd/cpd/CPDConfiguration.java | 6 +- .../net/sourceforge/pmd/cpd/CPDReport.java | 21 +- .../net/sourceforge/pmd/cpd/CSVRenderer.java | 8 +- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 18 +- .../pmd/cpd/CpdCapableLanguage.java | 2 +- .../java/net/sourceforge/pmd/cpd/GUI.java | 200 ++++++++---------- .../sourceforge/pmd/cpd/GridBagHelper.java | 11 +- .../java/net/sourceforge/pmd/cpd/Mark.java | 68 +++--- .../java/net/sourceforge/pmd/cpd/Match.java | 2 +- .../pmd/cpd/PmdCapableLanguage.java | 3 +- .../sourceforge/pmd/cpd/SimpleRenderer.java | 8 +- .../sourceforge/pmd/cpd/SourceManager.java | 6 +- .../net/sourceforge/pmd/cpd/TokenEntry.java | 16 +- .../net/sourceforge/pmd/cpd/TokenFactory.java | 70 +++--- .../net/sourceforge/pmd/cpd/Tokenizer.java | 6 +- .../java/net/sourceforge/pmd/cpd/Tokens.java | 69 +++++- .../net/sourceforge/pmd/cpd/VSRenderer.java | 10 +- .../net/sourceforge/pmd/cpd/XMLRenderer.java | 33 +-- .../pmd/cpd/impl/AntlrTokenizer.java | 6 +- .../pmd/cpd/impl/TokenizerBase.java | 2 +- .../net/sourceforge/pmd/lang/Language.java | 33 ++- .../pmd/lang/LanguageModuleBase.java | 2 +- .../pmd/lang/LanguageProcessor.java | 3 +- .../pmd/lang/LanguageProcessorRegistry.java | 2 +- .../pmd/lang/LanguageRegistry.java | 2 +- .../pmd/lang/document/FileLocation.java | 4 + .../pmd/lang/impl/BatchLanguageProcessor.java | 13 +- .../sourceforge/pmd/cpd/CPDReportTest.java | 4 +- .../sourceforge/pmd/cpd/CpdAnalysisTest.java | 4 +- .../net/sourceforge/pmd/cpd/CpdTestUtils.java | 7 +- .../net/sourceforge/pmd/cpd/MarkTest.java | 29 +-- .../pmd/cpd/MatchAlgorithmTest.java | 8 +- .../net/sourceforge/pmd/cpd/MatchTest.java | 4 +- .../sourceforge/pmd/cpd/TokenEntryTest.java | 2 +- .../pmd/lang/java/cpd/JavaTokenizer.java | 4 +- .../ignoreIdentsPreservesClassLiteral.txt | 10 +- .../testdata/ignoreIdentsPreservesCtor.txt | 46 ++-- .../testdata/ignoreIdentsPreservesEnum.txt | 10 +- .../lang/java/cpd/testdata/ignoreLiterals.txt | 8 +- 40 files changed, 416 insertions(+), 346 deletions(-) diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java index 5944e910c0..60c661cfd8 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java @@ -41,7 +41,7 @@ class ApexCpdTest { cpd.performAnalysis(matches -> { assertEquals(1, matches.getMatches().size()); Match firstDuplication = matches.getMatches().get(0); - assertTrue(matches.getSourceCodeSlice(firstDuplication).startsWith("global with sharing class SFDCEncoder")); + assertTrue(matches.getSourceCodeSlice(firstDuplication.getFirstMark()).startsWith("global with sharing class SFDCEncoder")); }); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 520b72b2ec..f514588e9f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -50,7 +50,7 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean skipDuplicates; - private String rendererName; + private String rendererName = DEFAULT_RENDERER; CPDReportRenderer cpdReportRenderer; @@ -167,6 +167,10 @@ public class CPDConfiguration extends AbstractConfiguration { public void setRendererName(String rendererName) { this.rendererName = rendererName; + if (rendererName == null) { + this.cpdReportRenderer = null; + } + this.cpdReportRenderer = createRendererByName(rendererName, getSourceEncoding().name()); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java index 3c1ba5a562..46a173b29e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java @@ -4,15 +4,15 @@ package net.sourceforge.pmd.cpd; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.function.Predicate; +import java.util.stream.Collectors; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.document.Chars; -import net.sourceforge.pmd.util.Predicate; /** * @since 6.48.0 @@ -39,10 +39,16 @@ public class CPDReport { return numberOfTokensPerFile; } - public Chars getSourceCodeSlice(Match match) { - return sourceManager.getSlice(match.getFirstMark()); + /** + * Return the slice of source code where the mark was found. This + * returns the entire lines from the start to the end line of the + * mark. + */ + public Chars getSourceCodeSlice(Mark mark) { + return sourceManager.getSlice(mark); } + /** * Creates a new CPD report taking all the information from this report, * but filtering the matches. @@ -53,12 +59,7 @@ public class CPDReport { */ @Experimental public CPDReport filterMatches(Predicate filter) { - List filtered = new ArrayList<>(); - for (Match match : this.getMatches()) { - if (filter.test(match)) { - filtered.add(match); - } - } + List filtered = this.matches.stream().filter(filter).collect(Collectors.toList()); return new CPDReport(sourceManager, filtered, this.getNumberOfTokensPerFile()); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java index 8dde3cb762..ea0b8edbd0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java @@ -12,6 +12,7 @@ import org.apache.commons.lang3.StringEscapeUtils; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; +import net.sourceforge.pmd.lang.document.FileLocation; public class CSVRenderer implements CPDReportRenderer { @@ -56,12 +57,13 @@ public class CSVRenderer implements CPDReportRenderer { .append(String.valueOf(match.getMarkCount())).append(separator); for (Iterator marks = match.iterator(); marks.hasNext();) { Mark mark = marks.next(); + FileLocation loc = mark.getLocation(); - writer.append(String.valueOf(mark.getBeginLine())).append(separator); + writer.append(String.valueOf(loc.getStartLine())).append(separator); if (lineCountPerFile) { - writer.append(String.valueOf(mark.getLineCount())).append(separator); + writer.append(String.valueOf(loc.getLineCount())).append(separator); } - writer.append(StringEscapeUtils.escapeCsv(mark.getFilename())); + writer.append(StringEscapeUtils.escapeCsv(loc.getFileName())); if (marks.hasNext()) { writer.append(separator); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 2e4e5911a0..7f9c40dfda 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -37,6 +37,7 @@ public final class CpdAnalysis implements AutoCloseable { private final CPDConfiguration configuration; private final FileCollector files; private final MessageReporter reporter; + private final @Nullable CPDReportRenderer renderer; private @NonNull CPDListener listener = new CPDNullListener(); @@ -48,16 +49,9 @@ public final class CpdAnalysis implements AutoCloseable { reporter ); - if (config.getRendererName() == null) { - config.setRendererName(CPDConfiguration.DEFAULT_RENDERER); - } - if (config.cpdReportRenderer == null) { - //may throw - CPDReportRenderer renderer = CPDConfiguration.createRendererByName(config.getRendererName(), config.getSourceEncoding().name()); - config.setRenderer(renderer); - } + this.renderer = config.getCPDReportRenderer(); // Add all sources - extractAllSources(); + extractAllSources(config); for (Language language : config.getLanguageRegistry()) { setLanguageProperties(language, config); @@ -88,7 +82,7 @@ public final class CpdAnalysis implements AutoCloseable { return files; } - private void extractAllSources() throws IOException { + private void extractAllSources(CPDConfiguration configuration) throws IOException { // Add files if (null != configuration.getFiles() && !configuration.getFiles().isEmpty()) { addSourcesFilesToCPD(configuration.getFiles()); @@ -167,8 +161,8 @@ public final class CpdAnalysis implements AutoCloseable { CPDReport cpdReport = new CPDReport(sourceManager, matchAlgorithm.getMatches(), numberOfTokensPerFile); - if (configuration.getCPDReportRenderer() != null) { - configuration.getCPDReportRenderer().render(cpdReport, IOUtil.createWriter(Charset.defaultCharset(), null)); + if (renderer != null) { + renderer.render(cpdReport, IOUtil.createWriter(Charset.defaultCharset(), null)); } consumer.accept(cpdReport); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java index 45c54f7494..34f854268d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdCapableLanguage.java @@ -8,7 +8,7 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; /** - * A language that also supports {@link CpdAnalysis CPD}. + * A language that supports {@link CpdAnalysis CPD}. * * @author Clรฉment Fournier */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index ba52e4d7c8..c39c74cce7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -24,7 +24,6 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -67,6 +66,7 @@ import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.util.CollectionUtil; public class GUI implements CPDListener { @@ -78,10 +78,16 @@ public class GUI implements CPDListener { private abstract static class LanguageConfig { - public abstract String getLabel(); - public abstract Language getLanguage(); + boolean canUseCustomExtension() { + return false; + } + + void setExtension(String extension) { + // by default do nothing + } + public boolean canIgnoreIdentifiers() { return getLanguage().newPropertyBundle().hasDescriptor(Tokenizer.CPD_ANONYMIZE_IDENTIFIERS); } @@ -107,57 +113,60 @@ public class GUI implements CPDListener { private static final List LANGUAGE_SETS; - public static final String CUSTOM_EXTENSION_SENTINEL = "custom"; + private static final LanguageConfig CUSTOM_EXTENSION_LANG = new LanguageConfig() { + private String extension = "custom_ext"; + + @Override + void setExtension(String extension) { + this.extension = extension; + } + + @Override + boolean canUseCustomExtension() { + return true; + } + + @Override + public Language getLanguage() { + return new CpdOnlyLanguageModuleBase( + LanguageMetadata.withId("custom_extension") + .extensions(extension) + .name("By extension...")) { + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new AnyTokenizer(); + } + }; + } + }; static { - LANGUAGE_SETS = new ArrayList<>(); - - for (Language lang : LanguageRegistry.CPD) { - LanguageConfig config = new LanguageConfig() { - @Override - public String getLabel() { - return lang.getName(); - } - - @Override - public Language getLanguage() { - return lang; - } - }; - LANGUAGE_SETS.add(config); - } - LanguageConfig last = new LanguageConfig() { - @Override - public String getLabel() { - return "By extension..."; - } - + List languages = new ArrayList<>(); + LanguageRegistry.CPD.getLanguages().stream().map(l -> new LanguageConfig() { @Override public Language getLanguage() { - return new CpdOnlyLanguageModuleBase(LanguageMetadata.withId("custom_extension").extensions(CUSTOM_EXTENSION_SENTINEL).name("By extension...")) { - @Override - public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { - return new AnyTokenizer(); - } - }; + return l; } - }; - LANGUAGE_SETS.add(last); + }).forEach(languages::add); + languages.add(CUSTOM_EXTENSION_LANG); + LANGUAGE_SETS = languages; } private static final int DEFAULT_CPD_MINIMUM_LENGTH = 75; - private static final Map LANGUAGE_CONFIGS_BY_LABEL = new HashMap<>(LANGUAGE_SETS.size()); + private static final Map LANGUAGE_CONFIGS_BY_LABEL = + CollectionUtil.associateBy(LANGUAGE_SETS, l -> l.getLanguage().getName()); private static final KeyStroke COPY_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK, false); private static final KeyStroke DELETE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0); - private class ColumnSpec { - private String label; - private int alignment; - private int width; - private Comparator sorter; + private static class ColumnSpec { + + private final String label; + private final int alignment; + private final int width; + private final Comparator sorter; ColumnSpec(String aLabel, int anAlignment, int aWidth, Comparator aSorter) { label = aLabel; @@ -183,17 +192,11 @@ public class GUI implements CPDListener { } } - private final ColumnSpec[] matchColumns = new ColumnSpec[] { + private final ColumnSpec[] matchColumns = { new ColumnSpec("Source", SwingConstants.LEFT, -1, Match.LABEL_COMPARATOR), new ColumnSpec("Matches", SwingConstants.RIGHT, 60, Match.MATCHES_COMPARATOR), new ColumnSpec("Lines", SwingConstants.RIGHT, 45, Match.LINES_COMPARATOR), }; - static { - for (LanguageConfig lconf : LANGUAGE_SETS) { - LANGUAGE_CONFIGS_BY_LABEL.put(lconf.getLabel(), lconf); - } - } - private static LanguageConfig languageConfigFor(String label) { return LANGUAGE_CONFIGS_BY_LABEL.get(label); } @@ -271,9 +274,9 @@ public class GUI implements CPDListener { } } - private class AlignmentRenderer extends DefaultTableCellRenderer { + private static class AlignmentRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = -2190382865483285032L; - private int[] alignments; + private final int[] alignments; AlignmentRenderer(int[] theAlignments) { alignments = theAlignments; @@ -290,27 +293,27 @@ public class GUI implements CPDListener { } } - private JTextField rootDirectoryField = new JTextField(System.getProperty("user.home")); - private JTextField minimumLengthField = new JTextField(Integer.toString(DEFAULT_CPD_MINIMUM_LENGTH)); - private JTextField encodingField = new JTextField(System.getProperty("file.encoding")); - private JTextField timeField = new JTextField(6); - private JLabel phaseLabel = new JLabel(); - private JProgressBar tokenizingFilesBar = new JProgressBar(); - private JTextArea resultsTextArea = new JTextArea(); - private JCheckBox recurseCheckbox = new JCheckBox("", true); - private JCheckBox ignoreIdentifiersCheckbox = new JCheckBox("", false); - private JCheckBox ignoreLiteralsCheckbox = new JCheckBox("", false); - private JCheckBox ignoreAnnotationsCheckbox = new JCheckBox("", false); - private JCheckBox ignoreUsingsCheckbox = new JCheckBox("", false); - private JCheckBox ignoreLiteralSequencesCheckbox = new JCheckBox("", false); - private JComboBox languageBox = new JComboBox<>(); - private JTextField extensionField = new JTextField(); - private JLabel extensionLabel = new JLabel("Extension:", SwingConstants.RIGHT); - private JTable resultsTable = new JTable(); - private JButton goButton; - private JButton cancelButton; - private JPanel progressPanel; - private JFrame frame; + private final JTextField rootDirectoryField = new JTextField(System.getProperty("user.home")); + private final JTextField minimumLengthField = new JTextField(Integer.toString(DEFAULT_CPD_MINIMUM_LENGTH)); + private final JTextField encodingField = new JTextField(System.getProperty("file.encoding")); + private final JTextField timeField = new JTextField(6); + private final JLabel phaseLabel = new JLabel(); + private final JProgressBar tokenizingFilesBar = new JProgressBar(); + private final JTextArea resultsTextArea = new JTextArea(); + private final JCheckBox recurseCheckbox = new JCheckBox("", true); + private final JCheckBox ignoreIdentifiersCheckbox = new JCheckBox("", false); + private final JCheckBox ignoreLiteralsCheckbox = new JCheckBox("", false); + private final JCheckBox ignoreAnnotationsCheckbox = new JCheckBox("", false); + private final JCheckBox ignoreUsingsCheckbox = new JCheckBox("", false); + private final JCheckBox ignoreLiteralSequencesCheckbox = new JCheckBox("", false); + private final JComboBox languageBox = new JComboBox<>(); + private final JTextField extensionField = new JTextField(); + private final JLabel extensionLabel = new JLabel("Extension:", SwingConstants.RIGHT); + private final JTable resultsTable = new JTable(); + private final JButton goButton; + private final JButton cancelButton; + private final JPanel progressPanel; + private final JFrame frame; private boolean trimLeadingWhitespace; private List matches = new ArrayList<>(); @@ -392,11 +395,11 @@ public class GUI implements CPDListener { ignoreAnnotationsCheckbox.setEnabled(current.canIgnoreAnnotations()); ignoreUsingsCheckbox.setEnabled(current.canIgnoreUsings()); ignoreLiteralSequencesCheckbox.setEnabled(current.canIgnoreLiteralSequences()); - String firstExt = current.getLanguage().getExtensions().get(0); - boolean enableExtension = CUSTOM_EXTENSION_SENTINEL.equals(firstExt); + boolean enableExtension = current.canUseCustomExtension(); if (enableExtension) { extensionField.setText(""); } else { + String firstExt = current.getLanguage().getExtensions().get(0); extensionField.setText(firstExt); } extensionField.setEnabled(enableExtension); @@ -415,7 +418,7 @@ public class GUI implements CPDListener { helper.add(minimumLengthField); helper.addLabel("Language:"); for (LanguageConfig lconf : LANGUAGE_SETS) { - languageBox.addItem(lconf.getLabel()); + languageBox.addItem(lconf.getLanguage().getName()); } languageBox.addActionListener(e -> adjustLanguageControlsFor(languageConfigFor((String) languageBox.getSelectedItem()))); helper.add(languageBox); @@ -573,20 +576,11 @@ public class GUI implements CPDListener { return new JScrollPane(resultsTable); } - private boolean isLegalPath(String path, LanguageConfig config) { - for (String extension : config.getLanguage().getExtensions()) { - if (path.endsWith(extension) && !extension.isEmpty()) { - return true; - } - } - return false; - } - - private String setLabelFor(Match match) { + private void setLabelFor(Match match) { Set sourceIDs = new HashSet<>(match.getMarkCount()); for (Mark mark : match) { - sourceIDs.add(mark.getFilename()); + sourceIDs.add(mark.getLocation().getFileName()); } String label; @@ -599,7 +593,6 @@ public class GUI implements CPDListener { } match.setLabel(label); - return label; } private void setProgressControls(boolean isRunning) { @@ -627,7 +620,9 @@ public class GUI implements CPDListener { config.setIgnoreAnnotations(ignoreAnnotationsCheckbox.isSelected()); config.setIgnoreUsings(ignoreUsingsCheckbox.isSelected()); config.setIgnoreLiteralSequences(ignoreLiteralSequencesCheckbox.isSelected()); - // p.setProperty(LanguageFactory.EXTENSION, extensionField.getText()); //FIXME + if (extensionField.isEnabled()) { + CUSTOM_EXTENSION_LANG.setExtension(extensionField.getText()); + } LanguageConfig conf = languageConfigFor((String) languageBox.getSelectedItem()); Language language = conf.getLanguage(); @@ -638,15 +633,7 @@ public class GUI implements CPDListener { tokenizingFilesBar.setMinimum(0); phaseLabel.setText(""); - if (isLegalPath(dirPath.getPath(), conf)) { - // should use the - // language file filter - // instead? - // fixme wth - cpd.files().addFileOrDirectory(dirPath.toPath()); - } else { - cpd.files().addFileOrDirectory(dirPath.toPath(), recurseCheckbox.isSelected()); - } + cpd.files().addFileOrDirectory(dirPath.toPath(), recurseCheckbox.isSelected()); Timer t = createTimer(); t.start(); cpd.performAnalysis(report -> { @@ -675,16 +662,13 @@ public class GUI implements CPDListener { final long start = System.currentTimeMillis(); - return new Timer(1000, new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - long now = System.currentTimeMillis(); - long elapsedMillis = now - start; - long elapsedSeconds = elapsedMillis / 1000; - long minutes = (long) Math.floor(elapsedSeconds / 60); - long seconds = elapsedSeconds - minutes * 60; - timeField.setText(formatTime(minutes, seconds)); - } + return new Timer(1000, e -> { + long now = System.currentTimeMillis(); + long elapsedMillis = now - start; + long elapsedSeconds = elapsedMillis / 1000; + long minutes = elapsedSeconds / 60; + long seconds = elapsedSeconds - minutes * 60; + timeField.setText(formatTime(minutes, seconds)); }); } @@ -702,7 +686,7 @@ public class GUI implements CPDListener { return sb.toString(); } - private abstract class SortingTableModel extends AbstractTableModel { + private abstract static class SortingTableModel extends AbstractTableModel { abstract int sortColumn(); abstract void sortColumn(int column); @@ -785,10 +769,10 @@ public class GUI implements CPDListener { @Override public void sort(Comparator comparator) { - Collections.sort(items, comparator); if (sortDescending) { - Collections.reverse(items); + comparator = comparator.reversed(); } + items.sort(comparator); } }; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GridBagHelper.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GridBagHelper.java index b02ca6163b..ac8bb6e88c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GridBagHelper.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GridBagHelper.java @@ -13,14 +13,7 @@ import java.awt.Insets; import javax.swing.JLabel; import javax.swing.SwingConstants; -import net.sourceforge.pmd.annotation.InternalApi; - -/** - * @deprecated Is internal API - */ -@Deprecated -@InternalApi -public class GridBagHelper { +class GridBagHelper { GridBagLayout gridbag; Container container; @@ -30,7 +23,7 @@ public class GridBagHelper { int labelAlignment = SwingConstants.RIGHT; double[] weights; - public GridBagHelper(Container container, double[] weights) { + GridBagHelper(Container container, double[] weights) { this.container = container; this.weights = weights; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index 4de7b63016..c4ceb5929d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -6,59 +6,56 @@ package net.sourceforge.pmd.cpd; import java.util.Objects; -public class Mark implements Comparable { - private final TokenEntry token; - private TokenEntry endToken; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; - public Mark(TokenEntry token) { +import net.sourceforge.pmd.lang.document.FileLocation; +import net.sourceforge.pmd.lang.document.TextRange2d; + +/** + * A range of tokens in a source file, identified by a start and end + * token (both included in the range). The start and end token may be + * the same token. + */ +public final class Mark implements Comparable { + + private final @NonNull TokenEntry token; + private @Nullable TokenEntry endToken; + + Mark(@NonNull TokenEntry token) { this.token = token; } - public TokenEntry getToken() { + @NonNull TokenEntry getToken() { return this.token; } - public String getFilename() { - return this.token.getFileName(); - } - - public int getBeginLine() { - return this.token.getBeginLine(); + @NonNull TokenEntry getEndToken() { + return endToken == null ? token : endToken; } /** - * The column number where this duplication begins. + * Return the location of this source range in the source file. */ - public int getBeginColumn() { - return this.token.getBeginColumn(); + public FileLocation getLocation() { + TokenEntry endToken = getEndToken(); + return FileLocation.range( + this.token.getFilePathId(), + TextRange2d.range2d(token.getBeginLine(), token.getBeginColumn(), + endToken.getEndLine(), endToken.getEndColumn())); } public int getBeginTokenIndex() { return this.token.getIndex(); } - public int getEndLine() { - return endToken == null ? token.getEndLine() : endToken.getEndLine(); - } - - /** - * The column number where this duplication ends. - * returns -1 if not available - * @return the end column number - */ - public int getEndColumn() { - return this.endToken == null ? token.getEndColumn() : this.endToken.getEndColumn(); - } - public int getEndTokenIndex() { - return this.endToken == null ? this.token.getIndex() : this.endToken.getIndex(); + return getEndToken().getIndex(); } - public int getLineCount() { - return this.getEndLine() - this.getBeginLine() + 1; - } - - void setEndToken(TokenEntry endToken) { + void setEndToken(@NonNull TokenEntry endToken) { + assert endToken.getFilePathId().equals(token.getFilePathId()) + : "Tokens are not from the same file"; this.endToken = endToken; } @@ -67,7 +64,7 @@ public class Mark implements Comparable { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((token == null) ? 0 : token.hashCode()); + result = prime * result + token.hashCode(); return result; } @@ -83,7 +80,8 @@ public class Mark implements Comparable { return false; } Mark other = (Mark) obj; - return Objects.equals(token, other.token); + return Objects.equals(token, other.token) + && Objects.equals(endToken, other.endToken); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java index 38bcce41ce..abf807e7c7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java @@ -47,7 +47,7 @@ public class Match implements Comparable, Iterable { } public int getLineCount() { - return getMark(0).getLineCount(); + return getMark(0).getLocation().getLineCount(); } public int getTokenCount() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java index 06e50ea658..e246869e5c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/PmdCapableLanguage.java @@ -4,12 +4,13 @@ package net.sourceforge.pmd.cpd; +import net.sourceforge.pmd.PmdAnalysis; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; /** - * A language that also supports {@link CpdAnalysis CPD}. + * A language that supports {@link PmdAnalysis PMD}. * * @author Clรฉment Fournier */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java index dae0dff7ab..6d773b257c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java @@ -11,6 +11,7 @@ import java.util.Iterator; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.util.StringUtil; public class SimpleRenderer implements CPDReportRenderer { @@ -54,13 +55,16 @@ public class SimpleRenderer implements CPDReportRenderer { .append(" tokens) duplication in the following files: ").append(PMD.EOL); for (Mark mark : match) { - writer.append("Starting at line ").append(String.valueOf(mark.getBeginLine())).append(" of ").append(mark.getFilename()) + FileLocation loc = mark.getLocation(); + writer.append("Starting at line ") + .append(String.valueOf(loc.getStartLine())) + .append(" of ").append(loc.getFileName()) .append(PMD.EOL); } writer.append(PMD.EOL); // add a line to separate the source from the desc above - Chars source = report.getSourceCodeSlice(match); + Chars source = report.getSourceCodeSlice(match.getFirstMark()); if (trimLeadingWhitespace) { for (Chars line : StringUtil.linesWithTrimIndent(source)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index c37757e7a7..bc78ca444b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentHashMap; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.document.TextRegion; @@ -61,10 +62,11 @@ class SourceManager implements AutoCloseable { @SuppressWarnings("PMD.CloseResource") public Chars getSlice(Mark mark) { - TextFile textFile = fileByName.get(mark.getFilename()); + FileLocation loc = mark.getLocation(); + TextFile textFile = fileByName.get(loc.getFileName()); assert textFile != null; TextDocument doc = get(textFile); - TextRegion lineRange = doc.createLineRange(mark.getBeginLine(), mark.getEndLine()); + TextRegion lineRange = doc.createLineRange(loc.getStartLine(), loc.getEndLine()); return doc.sliceOriginalText(lineRange); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java index 3146cbb693..ed329a0fbc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenEntry.java @@ -8,7 +8,7 @@ public class TokenEntry implements Comparable { private static final int EOF = 0; - private final String fileName; + private final String filePathId; private final int beginLine; private final int beginColumn; private final int endColumn; @@ -18,20 +18,20 @@ public class TokenEntry implements Comparable { private int hashCode; /** constructor for EOF entries. */ - TokenEntry(String fileName, int line, int column) { + TokenEntry(String filePathId, int line, int column) { assert isOk(line) && isOk(column) : "Coordinates are 1-based"; this.identifier = EOF; - this.fileName = fileName; + this.filePathId = filePathId; this.beginLine = line; this.beginColumn = column; this.endLine = line; this.endColumn = column; } - TokenEntry(int imageId, String fileName, int beginLine, int beginColumn, int endLine, int endColumn, int index) { + TokenEntry(int imageId, String filePathId, int beginLine, int beginColumn, int endLine, int endColumn, int index) { assert isOk(beginLine) && isOk(beginColumn) && isOk(endLine) && isOk(endColumn) : "Coordinates are 1-based"; assert imageId != EOF; - this.fileName = fileName; + this.filePathId = filePathId; this.beginLine = beginLine; this.beginColumn = beginColumn; this.endLine = endLine; @@ -49,8 +49,8 @@ public class TokenEntry implements Comparable { } - String getFileName() { - return fileName; + String getFilePathId() { + return filePathId; } @@ -103,7 +103,7 @@ public class TokenEntry implements Comparable { if (other.isEof() != this.isEof()) { return false; } else if (this.isEof()) { - return other.getFileName().equals(this.getFileName()); + return other.getFilePathId().equals(this.getFilePathId()); } return other.hashCode == hashCode; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java index 18d4a3d674..aac1717822 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java @@ -4,47 +4,59 @@ package net.sourceforge.pmd.cpd; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextDocument; +/** + * Proxy to record tokens from within {@link Tokenizer#tokenize(TextDocument, TokenFactory)}. + */ public interface TokenFactory extends AutoCloseable { - void recordToken(String image, int startLine, int startCol, int endLine, int endCol); + /** + * Record a token given its coordinates. Coordinates must match the + * requirements of {@link FileLocation}, ie, be 1-based and ordered + * properly. + * + * @param image Image of the token. This will be taken into account + * to determine the hash value of the token. + * @param startLine Start line of the token + * @param startCol Start column of the token + * @param endLine End line of the token + * @param endCol End column of the token + */ + void recordToken(@NonNull String image, int startLine, int startCol, int endLine, int endCol); - default void recordToken(String image, FileLocation location) { + /** + * Record a token given its coordinates. Coordinates must match the + * requirements of {@link FileLocation}, ie, be 1-based and ordered + * properly. + * + * @param image Image of the token. This will be taken into account + * to determine the hash value of the token. + * @param location Location of the token. + */ + default void recordToken(@NonNull String image, @NonNull FileLocation location) { recordToken(image, location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()); } - void setImage(TokenEntry entry, String newImage); + /** + * Sets the image of an existing token entry. + */ + void setImage(TokenEntry entry, @NonNull String newImage); - TokenEntry peekLastToken(); + /** + * Returns the last token that has been recorded in this file. + */ + @Nullable TokenEntry peekLastToken(); + /** + * This adds the EOF token, it must be called when + * {@link Tokenizer#tokenize(TextDocument, TokenFactory)} is done. + */ @Override void close(); - static TokenFactory forFile(TextDocument file, Tokens sink) { - return new TokenFactory() { - final String name = file.getPathId(); - - @Override - public void recordToken(String image, int startLine, int startCol, int endLine, int endCol) { - sink.addToken(image, name, startLine, startCol, endLine, endCol); - } - - @Override - public void setImage(TokenEntry entry, String newImage) { - sink.setImage(entry, newImage); - } - - @Override - public TokenEntry peekLastToken() { - return sink.peekLastToken(); - } - - @Override - public void close() { - sink.addEof(name); - } - }; - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index 508606b2c2..1aaebd5f6c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -10,6 +10,9 @@ import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; +/** + * Tokenizes a source file into tokens consumable by CPD. + */ public interface Tokenizer { PropertyDescriptor CPD_IGNORE_LITERAL_SEQUENCES = @@ -51,7 +54,6 @@ public interface Tokenizer { /** * Tokenize the source code and record tokens using the provided token factory. - * Implementations should not add an EOF token at the end. */ void tokenize(TextDocument document, TokenFactory tokens) throws IOException; @@ -60,7 +62,7 @@ public interface Tokenizer { * create and close the token factory. */ static void tokenize(Tokenizer tokenizer, TextDocument textDocument, Tokens tokens) throws IOException { - try (TokenFactory tf = TokenFactory.forFile(textDocument, tokens)) { + try (TokenFactory tf = Tokens.factoryForFile(textDocument, tokens)) { tokenizer.tokenize(textDocument, tf); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index ba3dc0292b..19b8a472de 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -10,8 +10,20 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.document.TextDocument; + +/** + * Global token collector for CPD. This is populated by lexing all files, + * after which the match algorithm proceeds. + */ +@InternalApi public class Tokens { + // This stores all the token entries recorded during the run. private final List tokens = new ArrayList<>(); private final Map images = new HashMap<>(); // the first ID is 1, 0 is the ID of the EOF token. @@ -21,14 +33,8 @@ public class Tokens { this.tokens.add(tokenEntry); } - void addEof(String fileName) { - if (tokens.isEmpty()) { - add(new TokenEntry(fileName, 1, 1)); - return; - } - - TokenEntry tok = peekLastToken(); - add(new TokenEntry(fileName, tok.getEndLine(), tok.getEndColumn())); + void addEof(String fileName, int line, int column) { + add(new TokenEntry(fileName, line, column)); } void setImage(TokenEntry entry, String newImage) { @@ -45,7 +51,7 @@ public class Tokens { } TokenEntry peekLastToken() { - return getToken(size() - 1); + return tokens.isEmpty() ? null : getToken(size() - 1); } private TokenEntry getToken(int index) { @@ -74,6 +80,51 @@ public class Tokens { return new State(this); } + /** + * Creates a token factory to process the given file with + * {@link Tokenizer#tokenize(TextDocument, TokenFactory)}. + * Tokens are accumulated in the {@link Tokens} parameter. + * + * @param file Document for the file to process + * @param tokens Token sink + * + * @return A new token factory + */ + static TokenFactory factoryForFile(TextDocument file, Tokens tokens) { + return new TokenFactory() { + final String fileName = file.getPathId(); + final int firstToken = tokens.size(); + + @Override + public void recordToken(@NonNull String image, int startLine, int startCol, int endLine, int endCol) { + tokens.addToken(image, fileName, startLine, startCol, endLine, endCol); + } + + @Override + public void setImage(TokenEntry entry, @NonNull String newImage) { + tokens.setImage(entry, newImage); + } + + @Override + public @Nullable TokenEntry peekLastToken() { + if (tokens.size() <= firstToken) { + return null; // no token has been added yet in this file + } + return tokens.peekLastToken(); + } + + @Override + public void close() { + TokenEntry tok = peekLastToken(); + if (tok == null) { + tokens.addEof(fileName, 1, 1); + } else { + tokens.addEof(fileName, tok.getEndLine(), tok.getEndColumn()); + } + } + }; + } + /** * Helper class to preserve and restore the current state of the token * entries. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java index 10938a8162..a0f68f0660 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java @@ -9,6 +9,7 @@ import java.io.Writer; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; +import net.sourceforge.pmd.lang.document.FileLocation; public class VSRenderer implements CPDReportRenderer { @@ -16,10 +17,11 @@ public class VSRenderer implements CPDReportRenderer { public void render(CPDReport report, Writer writer) throws IOException { for (Match match: report.getMatches()) { for (Mark mark : match) { - writer.append(mark.getFilename()) - .append('(').append(String.valueOf(mark.getBeginLine())).append("):") - .append(" Between lines ").append(String.valueOf(mark.getBeginLine())) - .append(" and ").append(String.valueOf(mark.getBeginLine() + match.getLineCount())) + FileLocation loc = mark.getLocation(); + writer.append(loc.getFileName()) + .append('(').append(String.valueOf(loc.getStartLine())).append("):") + .append(" Between lines ").append(String.valueOf(loc.getStartLine())) + .append(" and ").append(String.valueOf(loc.getEndLine())) .append(PMD.EOL); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java index b2738ec871..23c2500432 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.cpd; import java.io.IOException; import java.io.Writer; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -24,6 +22,7 @@ import org.w3c.dom.Element; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.Chars; +import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.util.StringUtil; /** @@ -98,8 +97,7 @@ public final class XMLRenderer implements CPDReportRenderer { final Map numberOfTokensPerFile = report.getNumberOfTokensPerFile(); doc.appendChild(root); - final List> entries = new ArrayList<>(numberOfTokensPerFile.entrySet()); - for (final Map.Entry pair : entries) { + for (final Map.Entry pair : numberOfTokensPerFile.entrySet()) { final Element fileElement = doc.createElement("file"); fileElement.setAttribute("path", pair.getKey()); fileElement.setAttribute("totalNumberOfTokens", String.valueOf(pair.getValue())); @@ -117,32 +115,23 @@ public final class XMLRenderer implements CPDReportRenderer { private Element addFilesToDuplicationElement(Document doc, Element duplication, Match match) { for (Mark mark : match) { final Element file = doc.createElement("file"); - file.setAttribute("line", String.valueOf(mark.getBeginLine())); + FileLocation loc = mark.getLocation(); + file.setAttribute("line", String.valueOf(loc.getStartLine())); // only remove invalid characters, escaping is done by the DOM impl. - String filenameXml10 = StringUtil.removedInvalidXml10Characters(mark.getFilename()); + String filenameXml10 = StringUtil.removedInvalidXml10Characters(loc.getFileName()); file.setAttribute("path", filenameXml10); - file.setAttribute("endline", String.valueOf(mark.getEndLine())); - final int beginCol = mark.getBeginColumn(); - final int endCol = mark.getEndColumn(); - if (beginCol != -1) { - file.setAttribute("column", String.valueOf(beginCol)); - } - if (endCol != -1) { - file.setAttribute("endcolumn", String.valueOf(endCol)); - } - final int beginIndex = mark.getBeginTokenIndex(); - final int endIndex = mark.getEndTokenIndex(); - file.setAttribute("begintoken", String.valueOf(beginIndex)); - if (endIndex != -1) { - file.setAttribute("endtoken", String.valueOf(endIndex)); - } + file.setAttribute("endline", String.valueOf(loc.getEndLine())); + file.setAttribute("column", String.valueOf(loc.getStartColumn())); + file.setAttribute("endcolumn", String.valueOf(loc.getEndColumn())); + file.setAttribute("begintoken", String.valueOf(mark.getBeginTokenIndex())); + file.setAttribute("endtoken", String.valueOf(mark.getEndTokenIndex())); duplication.appendChild(file); } return duplication; } private Element addCodeSnippet(Document doc, Element duplication, Match match, CPDReport report) { - Chars codeSnippet = report.getSourceCodeSlice(match); + Chars codeSnippet = report.getSourceCodeSlice(match.getFirstMark()); if (codeSnippet != null) { // the code snippet has normalized line endings String platformSpecific = codeSnippet.toString().replace("\n", System.lineSeparator()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java index 0d15d2f659..9b4f1c5084 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.cpd.impl; +import java.io.IOException; + import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.Lexer; @@ -19,8 +21,8 @@ import net.sourceforge.pmd.lang.document.TextDocument; */ public abstract class AntlrTokenizer extends TokenizerBase { @Override - protected final TokenManager makeLexerImpl(TextDocument doc) { - CharStream charStream = CharStreams.fromString(doc.getText().toString(), doc.getDisplayName()); + protected final TokenManager makeLexerImpl(TextDocument doc) throws IOException { + CharStream charStream = CharStreams.fromReader(doc.newReader(), doc.getDisplayName()); return new AntlrTokenManager(getLexerForSource(charStream), doc); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java index 3196849e49..69a3a99117 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java @@ -15,7 +15,7 @@ import net.sourceforge.pmd.lang.document.TextDocument; public abstract class TokenizerBase> implements Tokenizer { - protected abstract TokenManager makeLexerImpl(TextDocument doc); + protected abstract TokenManager makeLexerImpl(TextDocument doc) throws IOException; protected TokenManager filterTokenStream(TokenManager tokenManager) { return new BaseTokenFilter<>(tokenManager); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java index ad31b2afb3..31d7128fb1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/Language.java @@ -8,22 +8,29 @@ import java.util.List; import java.util.ServiceLoader; import java.util.Set; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.cpd.CpdCapableLanguage; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; + /** * Represents a language module, and provides access to language-specific - * functionality. You can get a language instance from a {@link LanguageRegistry}. + * functionality. You can get a language instance from a {@link LanguageRegistry}, + * see {@link LanguageRegistry#PMD} for instance. * *

Language instances are extensions to the core of PMD. They can be - * registered with a {@linkplain ServiceLoader service file} so that the - * PMD CLI automatically finds them on the classpath. + * registered with a {@linkplain ServiceLoader service file} so that + * PMD automatically finds them on the classpath. * *

Instances of this interface are stateless and immutable after construction. * They mostly provide metadata about the language, like ID, name and different - * versions that are supported. Languages can create a {@link LanguageProcessor} - * to actually run the analysis. That object can maintain analysis-global state, - * and has a proper lifecycle. + * versions that are supported. + * + *

Languages should implement the interfaces {@link PmdCapableLanguage} + * or {@link CpdCapableLanguage} to be usable by PMD or CPD, respectively. * * @see LanguageVersion - * @see LanguageVersionDiscoverer */ public interface Language extends Comparable { @@ -53,7 +60,10 @@ public interface Language extends Comparable { * module. * * @return The terse name of this language. + * + * @deprecated Use {@link #getId()} */ + @Deprecated String getTerseName(); @@ -126,7 +136,7 @@ public interface Language extends Comparable { * @return The corresponding LanguageVersion, {@code null} if the * version string is not recognized. */ - default LanguageVersion getVersion(String version) { + default @Nullable LanguageVersion getVersion(String version) { for (LanguageVersion v : getVersions()) { if (v.getVersion().equals(version)) { return v; @@ -142,13 +152,16 @@ public interface Language extends Comparable { * * @return The current default language version for this language. */ - LanguageVersion getDefaultVersion(); + @NonNull LanguageVersion getDefaultVersion(); /** * Creates a new bundle of properties that will serve to configure * the {@link LanguageProcessor} for this language. The returned - * bundle must have all relevant properties already declared. + * bundle must have all supported properties already declared. See + * {@link PmdCapableLanguage} and {@link CpdCapableLanguage} for sites + * where properties are passed back to the language with user-provided + * values. * * @return A new set of properties */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java index b75a489f35..d9d5121a35 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java @@ -103,7 +103,7 @@ public abstract class LanguageModuleBase implements Language { } @Override - public LanguageVersion getDefaultVersion() { + public @NonNull LanguageVersion getDefaultVersion() { return defaultVersion; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessor.java index a01f07bceb..050621d62b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessor.java @@ -12,6 +12,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.cache.AnalysisCache; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; import net.sourceforge.pmd.util.log.MessageReporter; @@ -46,7 +47,7 @@ public interface LanguageProcessor extends AutoCloseable { /** * The language of this processor. */ - @NonNull Language getLanguage(); + @NonNull PmdCapableLanguage getLanguage(); /** * The language version that was configured when creating this processor. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java index 69e8b9e89a..c6d9b69629 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java @@ -36,7 +36,7 @@ public final class LanguageProcessorRegistry implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(LanguageProcessorRegistry.class); - private final Map processors; + private final Map processors; private final LanguageRegistry languages; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java index 5853a4f745..8add12abab 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java @@ -63,7 +63,7 @@ public final class LanguageRegistry implements Iterable { * Create a new registry that contains the given set of languages. * @throws NullPointerException If the parameter is null */ - public LanguageRegistry(Set languages) { + public LanguageRegistry(Set languages) { this.languages = languages.stream() .sorted(Comparator.comparing(Language::getTerseName, String::compareToIgnoreCase)) .collect(CollectionUtil.toUnmodifiableSet()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java index 57768ac833..bead886ab1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java @@ -137,6 +137,10 @@ public final class FileLocation { return getFileName() + ":" + getStartPos().toDisplayStringWithColon(); } + public int getLineCount() { + return getEndLine() - getStartLine() + 1; + } + /** * Creates a new location for a range of text. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/BatchLanguageProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/BatchLanguageProcessor.java index e13be1e9f5..1a24fd0e57 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/BatchLanguageProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/impl/BatchLanguageProcessor.java @@ -9,7 +9,7 @@ import java.util.List; import org.checkerframework.checker.nullness.qual.NonNull; -import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageVersion; @@ -26,12 +26,17 @@ import net.sourceforge.pmd.lang.document.TextFile; */ public abstract class BatchLanguageProcessor

implements LanguageProcessor { - private final Language language; + private final PmdCapableLanguage language; private final P bundle; private final LanguageVersion version; protected BatchLanguageProcessor(P bundle) { - this.language = bundle.getLanguage(); + if (!(bundle.getLanguage() instanceof PmdCapableLanguage)) { + throw new IllegalArgumentException( + "Cannot create a processor for a language which does not support PMD: " + bundle.getLanguage() + ); + } + this.language = (PmdCapableLanguage) bundle.getLanguage(); this.bundle = bundle; this.version = bundle.getLanguageVersion(); } @@ -46,7 +51,7 @@ public abstract class BatchLanguageProcessor

i } @Override - public final @NonNull Language getLanguage() { + public final @NonNull PmdCapableLanguage getLanguage() { return language; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java index 2254a41982..2fb14a353e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java @@ -33,7 +33,7 @@ class CPDReportTest { match -> { // only keep file1.java for (Mark mark : match.getMarkSet()) { - if (mark.getFilename().equals("file1.java")) { + if (mark.getLocation().getFileName().equals("file1.java")) { return true; } } @@ -43,7 +43,7 @@ class CPDReportTest { for (Match match : filtered.getMatches()) { Set filenames = new HashSet<>(); for (Mark mark : match.getMarkSet()) { - filenames.add(mark.getFilename()); + filenames.add(mark.getLocation().getFileName()); } assertTrue(filenames.contains("file1.java")); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java index 3a200c1713..1d3e3d2426 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java @@ -140,8 +140,8 @@ class CpdAnalysisTest { List matches = report.getMatches(); for (Match match : matches) { // the file added first was dup2. - assertTrue(match.getFirstMark().getFilename().endsWith("dup2.java")); - assertTrue(match.getSecondMark().getFilename().endsWith("dup1.java")); + assertTrue(match.getFirstMark().getLocation().getFileName().endsWith("dup2.java")); + assertTrue(match.getSecondMark().getLocation().getFileName().endsWith("dup1.java")); } }); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java index b33b95201a..5c469ee96f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdTestUtils.java @@ -29,7 +29,10 @@ final class CpdTestUtils { Set textFiles = new HashSet<>(); for (Match match : matches) { match.iterator().forEachRemaining( - mark -> textFiles.add(TextFile.forCharSeq(DUMMY_FILE_CONTENT, mark.getFilename(), DummyLanguageModule.getInstance().getDefaultVersion()))); + mark -> textFiles.add( + TextFile.forCharSeq(DUMMY_FILE_CONTENT, + mark.getLocation().getFileName(), + DummyLanguageModule.getInstance().getDefaultVersion()))); } return new CPDReport( new SourceManager(new ArrayList<>(textFiles)), @@ -77,7 +80,7 @@ final class CpdTestUtils { } CpdReportBuilder addMatch(Match match) { - fileContents.putIfAbsent(match.getFirstMark().getFilename(), DUMMY_FILE_CONTENT); + fileContents.putIfAbsent(match.getFirstMark().getLocation().getFileName(), DUMMY_FILE_CONTENT); matches.add(match); return this; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java index 2335d382a4..bd95eade0e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MarkTest.java @@ -8,6 +8,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.lang.document.FileLocation; + class MarkTest { @Test @@ -17,14 +19,14 @@ class MarkTest { TokenEntry token = tokens.addToken("public", filename, 1, 2, 3, 4); Mark mark = new Mark(token); - + FileLocation loc = mark.getLocation(); assertEquals(token, mark.getToken()); - assertEquals(filename, mark.getFilename()); - assertEquals(1, mark.getBeginLine()); - assertEquals(3, mark.getLineCount()); - assertEquals(3, mark.getEndLine()); - assertEquals(2, mark.getBeginColumn()); - assertEquals(4, mark.getEndColumn()); + assertEquals(filename, loc.getFileName()); + assertEquals(1, loc.getStartLine()); + assertEquals(3, loc.getLineCount()); + assertEquals(3, loc.getEndLine()); + assertEquals(2, loc.getStartColumn()); + assertEquals(4, loc.getEndColumn()); } @Test @@ -42,13 +44,14 @@ class MarkTest { final Mark mark = new Mark(token); mark.setEndToken(endToken); + FileLocation loc = mark.getLocation(); assertEquals(token, mark.getToken()); - assertEquals(filename, mark.getFilename()); - assertEquals(beginLine, mark.getBeginLine()); - assertEquals(lineCount, mark.getLineCount()); - assertEquals(beginLine + lineCount - 1, mark.getEndLine()); - assertEquals(beginColumn, mark.getBeginColumn()); - assertEquals(endColumn, mark.getEndColumn()); + assertEquals(filename, loc.getFileName()); + assertEquals(beginLine, loc.getStartLine()); + assertEquals(lineCount, loc.getLineCount()); + assertEquals(beginLine + lineCount - 1, loc.getEndLine()); + assertEquals(beginColumn, loc.getStartColumn()); + assertEquals(endColumn, loc.getEndColumn()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index 6fb50927d6..ee3fe64048 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -56,12 +56,12 @@ class MatchAlgorithmTest { Mark mark2 = marks.next(); assertFalse(marks.hasNext()); - assertEquals(3, mark1.getBeginLine()); - assertEquals(fileName, mark1.getFilename()); + assertEquals(3, mark1.getLocation().getStartLine()); + assertEquals(fileName, mark1.getLocation().getFileName()); assertEquals(LINE_3 + "\n", sourceManager.getSlice(mark1).toString()); - assertEquals(4, mark2.getBeginLine()); - assertEquals(fileName, mark2.getFilename()); + assertEquals(4, mark2.getLocation().getStartLine()); + assertEquals(fileName, mark2.getLocation().getFileName()); assertEquals(LINE_4 + "\n", sourceManager.getSlice(mark2).toString()); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java index 9b3ff8e06e..93086139ca 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchTest.java @@ -43,11 +43,11 @@ class MatchTest { assertFalse(i.hasNext()); assertEquals(mark1, occurrence1); - assertEquals(1, occurrence1.getLineCount()); + assertEquals(1, occurrence1.getLocation().getLineCount()); assertEquals(Chars.wrap("1234567890"), sourceManager.getSlice(mark1)); assertEquals(mark2, occurrence2); - assertEquals(1, occurrence2.getLineCount()); + assertEquals(1, occurrence2.getLocation().getLineCount()); assertEquals(Chars.wrap("1234567890"), sourceManager.getSlice(mark2)); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java index 47cf2c65a0..1f50015a9b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TokenEntryTest.java @@ -15,7 +15,7 @@ class TokenEntryTest { Tokens tokens = new Tokens(); TokenEntry mark = tokens.addToken("public", "/var/Foo.java", 1, 2, 3, 4); assertEquals(1, mark.getBeginLine()); - assertEquals("/var/Foo.java", mark.getFileName()); + assertEquals("/var/Foo.java", mark.getFilePathId()); assertEquals(0, mark.getIndex()); assertEquals(2, mark.getBeginColumn()); assertEquals(4, mark.getEndColumn()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java index c0f884bbfe..ab91910c0c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java @@ -58,10 +58,10 @@ public class JavaTokenizer extends JavaCCTokenizer { || javaToken.kind == JavaTokenKinds.CHARACTER_LITERAL || javaToken.kind == JavaTokenKinds.INTEGER_LITERAL || javaToken.kind == JavaTokenKinds.FLOATING_POINT_LITERAL)) { - image = String.valueOf(javaToken.kind); + image = JavaTokenKinds.describe(javaToken.kind); } if (ignoreIdentifiers && javaToken.kind == JavaTokenKinds.IDENTIFIER) { - image = String.valueOf(javaToken.kind); + image = JavaTokenKinds.describe(javaToken.kind); } constructorDetector.processToken(javaToken); diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt index 11a46cf1a4..2450d1da83 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesClassLiteral.txt @@ -2,7 +2,7 @@ L2 [public] 1 7 [class] 8 13 - [80] 14 17 + [] 14 17 [{] 18 19 L3 [Foo] 5 8 @@ -14,16 +14,16 @@ L4 L5 [public] 5 11 [void] 12 16 - [80] 17 20 + [] 17 20 [(] 20 21 [)] 21 22 [{] 23 24 L6 - [80] 9 12 + [] 9 12 [.] 12 13 - [80] 13 16 + [] 13 16 [(] 16 17 - [80] 17 20 + [] 17 20 [.] 20 21 [class] 21 26 [Foo] 26 27 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt index 0eb4a58cd0..150bd14c42 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesCtor.txt @@ -2,25 +2,25 @@ L2 [public] 1 7 [class] 8 13 - [80] 14 17 + [] 14 17 [extends] 18 25 - [80] 26 29 + [] 26 29 [{] 30 31 L4 [private] 5 12 - [80] 13 16 - [80] 17 32 + [] 13 16 + [] 17 32 L6 [public] 5 11 [Foo] 12 15 [(] 15 16 [int] 16 19 - [80] 20 21 + [] 20 21 [)] 21 22 [{] 23 24 [super] 25 30 [(] 30 31 - [80] 31 32 + [] 31 32 [)] 32 33 [}] 35 36 L8 @@ -28,57 +28,57 @@ L8 [Foo] 13 16 [(] 16 17 [int] 17 20 - [80] 21 22 + [] 21 22 [,] 22 23 - [80] 24 30 - [80] 31 32 + [] 24 30 + [] 31 32 [)] 32 33 [{] 34 35 [super] 36 41 [(] 41 42 - [80] 42 43 + [] 42 43 [,] 43 44 - [80] 45 46 + [] 45 46 [)] 46 47 [}] 49 50 L10 [Foo] 19 22 [(] 22 23 [int] 23 26 - [80] 27 28 + [] 27 28 [,] 28 29 - [80] 30 36 - [80] 37 38 + [] 30 36 + [] 37 38 [,] 38 39 - [80] 40 46 - [80] 47 48 + [] 40 46 + [] 47 48 [)] 48 49 [{] 50 51 [super] 52 57 [(] 57 58 - [80] 58 59 + [] 58 59 [,] 59 60 - [80] 61 62 + [] 61 62 [,] 62 63 - [80] 64 65 + [] 64 65 [)] 65 66 [}] 68 69 L12 [private] 5 12 [static] 13 19 [class] 20 25 - [80] 26 31 + [] 26 31 [{] 32 33 L14 [Inner] 9 14 [(] 14 15 [)] 15 16 [{] 17 18 - [80] 19 25 + [] 19 25 [.] 25 26 - [80] 26 29 + [] 26 29 [.] 29 30 - [80] 30 37 + [] 30 37 [(] 37 38 ["Guess who?"] 38 50 [)] 50 51 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt index 82e10ffcf7..1d8e884c47 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreIdentsPreservesEnum.txt @@ -1,17 +1,17 @@ [Image] or [Truncated image[ Bcol Ecol L2 [public] 1 7 - [80] 8 12 - [80] 13 16 + [] 8 12 + [] 13 16 [{] 17 18 L3 - [80] 5 8 + [] 5 8 [(] 8 9 [1] 9 10 [)] 10 11 [,] 11 12 L4 - [80] 5 8 + [] 5 8 [(] 8 9 [2] 9 10 [)] 10 11 @@ -19,7 +19,7 @@ L6 [Foo] 5 8 [(] 8 9 [int] 9 12 - [80] 13 16 + [] 13 16 [)] 16 17 [{] 18 19 L7 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt index dc102bf6e9..767e3b3eb1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/ignoreLiterals.txt @@ -18,7 +18,7 @@ L3 [.] 19 20 [println] 20 27 [(] 27 28 - [75] 28 35 + [] 28 35 [)] 35 36 L4 [System] 9 15 @@ -27,13 +27,13 @@ L4 [.] 19 20 [println] 20 27 [(] 27 28 - [75] 28 35 + [] 28 35 [)] 35 36 L5 [int] 9 12 [i] 13 14 [=] 15 16 - [61] 17 18 + [] 17 18 L6 [System] 9 15 [.] 15 16 @@ -41,7 +41,7 @@ L6 [.] 19 20 [print] 20 25 [(] 25 26 - [75] 26 33 + [] 26 33 [)] 33 34 L8 [}] 5 6 From 2ef44be5b67f705b52213ad341bc1dde3af2d4d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 15 Feb 2023 17:00:26 +0100 Subject: [PATCH 114/347] Update doc --- .../adding_new_cpd_language.md | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md b/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md index ca62205242..8825597824 100644 --- a/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md +++ b/docs/pages/pmd/devdocs/major_contributions/adding_new_cpd_language.md @@ -15,7 +15,7 @@ To add support for a new language, the crucial piece is writing a tokenizer that splits the source file into the tokens specific to your language. Thankfully you can use a stock [Antlr grammar](https://github.com/antlr/grammars-v4) or JavaCC grammar to generate a lexer for you. If you cannot use a lexer generator, for -instance because you are wrapping a lexer for another library, it is still relatively +instance because you are wrapping a lexer from another library, it is still relatively easy to implement the Tokenizer interface. Use the following guide to set up a new language module that supports CPD. @@ -45,8 +45,8 @@ Use the following guide to set up a new language module that supports CPD. You can then subclass {% jdoc core::cpd.impl.JavaCCTokenizer %} instead of AntlrTokenizer. - For any other scenario just implement the interface however you can. Look at the Scala or Apex module for existing implementations. -3. Create a {% jdoc core::lang.Language %} implementation and override `createCpdTokenizer`. -If your language only supports CPD, then you can subclass {% jdoc core::lang.impl.CpdOnlyLanguageModuleBase %}: +3. Create a {% jdoc core::lang.Language %} implementation, and make it implement {% core::cpd.CpdCapableLanguage %}. +If your language only supports CPD, then you can subclass {% jdoc core::lang.impl.CpdOnlyLanguageModuleBase %} to get going: ```java // mind the package convention if you are going to make a PR @@ -54,18 +54,22 @@ If your language only supports CPD, then you can subclass {% jdoc core::lang.im public class GoLanguageModule extends CpdOnlyLanguageModuleBase { + // A public noarg constructor is required. public GoLanguageModule() { super(LanguageMetadata.withId("go").name("Go").extensions("go")); } @Override public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + // This method should return an instance of the tokenizer you created. return new GoTokenizer(); } } ``` - To make PMD find the language module at run time, write the fully-qualified name of your language class into the file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`. + To make PMD find the language module at runtime, write the fully-qualified name of your language class into the file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`. + + At this point the new language module should be available in {% jdoc core::lang.LanguageRegistry#CPD %} and usable by CPD like any other language. 4. Update the test that asserts the list of supported languages by updating the `SUPPORTED_LANGUAGES` constant in [BinaryDistributionIT](https://github.com/pmd/pmd/blob/master/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java). @@ -75,13 +79,15 @@ If your language only supports CPD, then you can subclass {% jdoc core::lang.im To make the tokenizer configurable, first define some property descriptors using {% jdoc core::properties.PropertyFactory %}. Look at {% jdoc core::cpd.Tokenizer %} -for some predefined ones which you can reuse. You need to override {% jdoc core::Language#newPropertyBundle() %} -and call `definePropertyDescriptor` to register your descriptors. +for some predefined ones which you can reuse (prefer reusing property descriptors if you can). +You need to override {% jdoc core::Language#newPropertyBundle() %} +and call `definePropertyDescriptor` to register the descriptors. After that you can access the values of the properties from the parameter -of {% jdoc core::lang.Language#createCpdTokenizer(core::properties.LanguagePropertyBundle) %}. +of {% jdoc core::cpd.CpdCapableLanguage#createCpdTokenizer(core::lang.LanguagePropertyBundle) %}. To implement simple token filtering, you can use {% jdoc core::cpd.impl.BaseTokenFilter %} -as a base class, or eg {% jdoc core::cpd.impl.AntlrTokenFilter %} if you have an Antlr grammar. Take a look at the [Kotlin token filter implementation](https://github.com/pmd/pmd/blob/master/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java), or the [Java one](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java). +as a base class, or another base class in {% jdoc_package core::cpd.impl %}. +Take a look at the [Kotlin token filter implementation](https://github.com/pmd/pmd/blob/master/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java), or the [Java one](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java). ### Testing your implementation From 10fcc1af8989b2f2bbeb0eb5e71e288ca60e1e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 17 Feb 2023 21:16:57 +0100 Subject: [PATCH 115/347] Add support for range constraints in XML-defined properties --- .../pmd/properties/NumericConstraints.java | 33 ++++- .../sourceforge/pmd/rules/RuleFactory.java | 128 +++++++++++++----- .../util/internal/xml/SchemaConstants.java | 3 + .../util/internal/xml/XmlErrorMessages.java | 1 + .../pmd/RulesetFactoryTestBase.java | 2 +- .../sourceforge/pmd/lang/rule/MockRule.java | 12 +- .../lang/rule/MockRuleWithNoProperties.java | 41 ++++++ .../pmd/properties/PropertySyntaxTest.java | 103 ++++++++++++++ 8 files changed, 279 insertions(+), 44 deletions(-) create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRuleWithNoProperties.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericConstraints.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericConstraints.java index 015d2c4396..b7f2b71c09 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericConstraints.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/NumericConstraints.java @@ -28,10 +28,39 @@ public final class NumericConstraints { * * @return A range constraint */ - public static > PropertyConstraint inRange(final N minInclusive, final N maxInclusive) { + public static > PropertyConstraint inRange(final N minInclusive, final N maxInclusive) { return PropertyConstraint.fromPredicate( t -> minInclusive.compareTo(t) <= 0 && maxInclusive.compareTo(t) >= 0, - "Should be between " + minInclusive + " and " + maxInclusive + "Should be between " + minInclusive + " and " + maxInclusive + ); + + } + + /** + * Requires the number to be greater than a lower bound. + * + * @param Type of number + * + * @return A range constraint + */ + public static > PropertyConstraint above(final N minInclusive) { + return PropertyConstraint.fromPredicate( + t -> minInclusive.compareTo(t) <= 0, + "Should be greater or equal to " + minInclusive + ); + } + + /** + * Requires the number to be lower than an upper bound. + * + * @param Type of number + * + * @return A range constraint + */ + public static > PropertyConstraint below(final N maxInclusive) { + return PropertyConstraint.fromPredicate( + t -> maxInclusive.compareTo(t) >= 0, + "Should be smaller or equal to " + maxInclusive ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 4c6639802e..120511e291 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -4,11 +4,25 @@ package net.sourceforge.pmd.rules; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.CLASS; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.DELIMITER; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.DEPRECATED; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.DESCRIPTION; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.EXAMPLE; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.EXTERNAL_INFO_URL; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.LANGUAGE; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.MAXIMUM_LANGUAGE_VERSION; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.MESSAGE; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.MINIMUM_LANGUAGE_VERSION; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.NAME; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PRIORITY; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTIES; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_ELT; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_MAX; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_MIN; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_TYPE; import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.PROPERTY_VALUE; +import static net.sourceforge.pmd.util.internal.xml.SchemaConstants.SINCE; import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION; import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__INVALID_LANG_VERSION_NO_NAMED_VERSION; import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.ERR__MISSING_REQUIRED_ELEMENT; @@ -17,6 +31,7 @@ import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.IGNORED__DU import static net.sourceforge.pmd.util.internal.xml.XmlErrorMessages.IGNORED__PROPERTY_CHILD_HAS_PRECEDENCE; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -35,7 +50,9 @@ import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.ConstraintViolatedException; +import net.sourceforge.pmd.properties.NumericConstraints; import net.sourceforge.pmd.properties.PropertyBuilder; +import net.sourceforge.pmd.properties.PropertyConstraint; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertySerializer; import net.sourceforge.pmd.properties.PropertyTypeId; @@ -44,9 +61,9 @@ import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.StringUtil; import net.sourceforge.pmd.util.internal.xml.PmdXmlReporter; import net.sourceforge.pmd.util.internal.xml.SchemaConstant; -import net.sourceforge.pmd.util.internal.xml.SchemaConstants; import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; import net.sourceforge.pmd.util.internal.xml.XmlUtil; +import net.sourceforge.pmd.util.log.MessageReporter; import com.github.oowekyala.ooxml.DomUtils; import com.github.oowekyala.ooxml.messages.XmlException; @@ -90,22 +107,22 @@ public class RuleFactory { public RuleReference decorateRule(Rule referencedRule, RuleSetReference ruleSetReference, Element ruleElement, PmdXmlReporter err) { RuleReference ruleReference = new RuleReference(referencedRule, ruleSetReference); - SchemaConstants.DEPRECATED.getAttributeOpt(ruleElement).map(Boolean::parseBoolean).ifPresent(ruleReference::setDeprecated); - SchemaConstants.NAME.getAttributeOpt(ruleElement).ifPresent(ruleReference::setName); - SchemaConstants.MESSAGE.getAttributeOpt(ruleElement).ifPresent(ruleReference::setMessage); - SchemaConstants.EXTERNAL_INFO_URL.getAttributeOpt(ruleElement).ifPresent(ruleReference::setExternalInfoUrl); + DEPRECATED.getAttributeOpt(ruleElement).map(Boolean::parseBoolean).ifPresent(ruleReference::setDeprecated); + NAME.getAttributeOpt(ruleElement).ifPresent(ruleReference::setName); + MESSAGE.getAttributeOpt(ruleElement).ifPresent(ruleReference::setMessage); + EXTERNAL_INFO_URL.getAttributeOpt(ruleElement).ifPresent(ruleReference::setExternalInfoUrl); for (Element node : DomUtils.children(ruleElement)) { - if (SchemaConstants.DESCRIPTION.matchesElt(node)) { + if (DESCRIPTION.matchesElt(node)) { ruleReference.setDescription(XmlUtil.parseTextNode(node)); - } else if (SchemaConstants.EXAMPLE.matchesElt(node)) { + } else if (EXAMPLE.matchesElt(node)) { ruleReference.addExample(XmlUtil.parseTextNode(node)); - } else if (SchemaConstants.PRIORITY.matchesElt(node)) { + } else if (PRIORITY.matchesElt(node)) { RulePriority priority = parsePriority(err, node); if (priority == null) { @@ -113,7 +130,7 @@ public class RuleFactory { } ruleReference.setPriority(priority); - } else if (SchemaConstants.PROPERTIES.matchesElt(node)) { + } else if (PROPERTIES.matchesElt(node)) { setPropertyValues(ruleReference, node, err); @@ -145,10 +162,10 @@ public class RuleFactory { Rule rule; try { - String clazz = SchemaConstants.CLASS.getNonBlankAttribute(ruleElement, err); + String clazz = CLASS.getNonBlankAttribute(ruleElement, err); rule = resourceLoader.loadRuleFromClassPath(clazz); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { - Attr node = SchemaConstants.CLASS.getAttributeNode(ruleElement); + Attr node = CLASS.getAttributeNode(ruleElement); throw err.at(node).error(e); } @@ -163,21 +180,21 @@ public class RuleFactory { rule.setMaximumLanguageVersion(getLanguageVersion(ruleElement, err, language, MAXIMUM_LANGUAGE_VERSION)); checkVersionsAreOrdered(ruleElement, err, rule); - SchemaConstants.SINCE.getAttributeOpt(ruleElement).ifPresent(rule::setSince); - SchemaConstants.MESSAGE.getAttributeOpt(ruleElement).ifPresent(rule::setMessage); - SchemaConstants.EXTERNAL_INFO_URL.getAttributeOpt(ruleElement).ifPresent(rule::setExternalInfoUrl); - rule.setDeprecated(SchemaConstants.DEPRECATED.getAsBooleanAttr(ruleElement, false)); + SINCE.getAttributeOpt(ruleElement).ifPresent(rule::setSince); + MESSAGE.getAttributeOpt(ruleElement).ifPresent(rule::setMessage); + EXTERNAL_INFO_URL.getAttributeOpt(ruleElement).ifPresent(rule::setExternalInfoUrl); + rule.setDeprecated(DEPRECATED.getAsBooleanAttr(ruleElement, false)); for (Element node : DomUtils.children(ruleElement)) { - if (SchemaConstants.DESCRIPTION.matchesElt(node)) { + if (DESCRIPTION.matchesElt(node)) { rule.setDescription(XmlUtil.parseTextNode(node)); - } else if (SchemaConstants.EXAMPLE.matchesElt(node)) { + } else if (EXAMPLE.matchesElt(node)) { rule.addExample(XmlUtil.parseTextNode(node)); - } else if (SchemaConstants.PRIORITY.matchesElt(node)) { + } else if (PRIORITY.matchesElt(node)) { RulePriority rp = parsePriority(err, node); if (rp == null) { @@ -185,7 +202,7 @@ public class RuleFactory { } rule.setPriority(rp); - } else if (SchemaConstants.PROPERTIES.matchesElt(node)) { + } else if (PROPERTIES.matchesElt(node)) { parsePropertiesForDefinitions(rule, node, err); setPropertyValues(rule, node, err); @@ -253,10 +270,10 @@ public class RuleFactory { } private void setLanguage(Element ruleElement, PmdXmlReporter err, Rule rule) { - String langId = SchemaConstants.LANGUAGE.getNonBlankAttribute(ruleElement, err); + String langId = LANGUAGE.getNonBlankAttribute(ruleElement, err); Language lang = languageRegistry.getLanguageById(langId); if (lang == null) { - Attr node = SchemaConstants.LANGUAGE.getAttributeNode(ruleElement); + Attr node = LANGUAGE.getAttributeNode(ruleElement); throw err.at(node) .error("Invalid language ''{0}'', possible values are {1}", langId, supportedLanguages()); } @@ -276,7 +293,7 @@ public class RuleFactory { * @param err Error reporter */ private void parsePropertiesForDefinitions(Rule rule, Element propertiesNode, @NonNull PmdXmlReporter err) { - for (Element child : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesNode, err)) { + for (Element child : PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesNode, err)) { if (isPropertyDefinition(child)) { rule.definePropertyDescriptor(parsePropertyDefinition(child, err)); } @@ -293,8 +310,8 @@ public class RuleFactory { Set overridden = new HashSet<>(); XmlException exception = null; - for (Element element : SchemaConstants.PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesElt, err)) { - String name = SchemaConstants.NAME.getAttributeOrThrow(element, err); + for (Element element : PROPERTY_ELT.getElementChildrenNamedReportOthers(propertiesElt, err)) { + String name = NAME.getAttributeOrThrow(element, err); if (!overridden.add(name)) { err.at(element).warn(IGNORED__DUPLICATE_PROPERTY_SETTER, name); continue; @@ -333,7 +350,7 @@ public class RuleFactory { * @return True if this element defines a new property, false if this is just stating a value */ private static boolean isPropertyDefinition(Element node) { - return SchemaConstants.PROPERTY_TYPE.hasAttribute(node); + return PROPERTY_TYPE.hasAttribute(node); } /** @@ -346,7 +363,7 @@ public class RuleFactory { */ private static PropertyDescriptor parsePropertyDefinition(Element propertyElement, PmdXmlReporter err) { - String typeId = SchemaConstants.PROPERTY_TYPE.getAttributeOrThrow(propertyElement, err); + String typeId = PROPERTY_TYPE.getAttributeOrThrow(propertyElement, err); PropertyTypeId factory = PropertyTypeId.lookupMnemonic(typeId); if (factory == null) { @@ -360,20 +377,20 @@ public class RuleFactory { private static PropertyDescriptor propertyDefCapture(Element propertyElement, PmdXmlReporter err, BuilderAndMapper factory) { - // TODO support constraints like numeric range - String name = SchemaConstants.NAME.getNonBlankAttributeOrThrow(propertyElement, err); - String description = SchemaConstants.DESCRIPTION.getNonBlankAttributeOrThrow(propertyElement, err); + String name = NAME.getNonBlankAttributeOrThrow(propertyElement, err); + String description = DESCRIPTION.getNonBlankAttributeOrThrow(propertyElement, err); try { PropertyBuilder builder = factory.newBuilder(name) - .desc(description) - .defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper())); - if (SchemaConstants.DELIMITER.hasAttribute(propertyElement)) { - err.at(SchemaConstants.DELIMITER.getAttributeNode(propertyElement)) + .desc(description); + if (DELIMITER.hasAttribute(propertyElement)) { + err.at(DELIMITER.getAttributeNode(propertyElement)) .warn(XmlErrorMessages.WARN__DELIMITER_DEPRECATED); } + parseConstraints(propertyElement, factory, builder, err); + builder.defaultValue(parsePropertyValue(propertyElement, err, factory.getXmlMapper())); return builder.build(); } catch (IllegalArgumentException e) { @@ -382,6 +399,51 @@ public class RuleFactory { } } + private static void parseConstraints(Element propertyElement, BuilderAndMapper factory, PropertyBuilder builder, PmdXmlReporter err) { + Optional> min = parseIntoComparable(propertyElement, factory, err, PROPERTY_MIN); + Optional> max = parseIntoComparable(propertyElement, factory, err, PROPERTY_MAX); + + if (min.isPresent() && max.isPresent()) { + if (min.get().compareTo((T) max.get()) > 0) { + throw err.at(PROPERTY_MIN.getAttributeNode(propertyElement)) + .error(XmlErrorMessages.ERR__INVALID_VALUE_RANGE); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) + PropertyConstraint constraint = NumericConstraints.inRange((Comparable) min.get(), (Comparable) max.get()); + builder.require(constraint); + } else if (min.isPresent() || max.isPresent()) { + Comparable minOrMax = min.orElse(max.orElse(null)); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + PropertyConstraint constraint = min.isPresent() ? NumericConstraints.above((Comparable) minOrMax) + : NumericConstraints.below((Comparable) minOrMax); + builder.require(constraint); + } + } + + private static Optional> parseIntoComparable(Element propertyElement, BuilderAndMapper factory, PmdXmlReporter err, SchemaConstant schemaConstant) { + return schemaConstant + .getAttributeOpt(propertyElement) + .map(s -> tryParsePropertyValue(factory, s, err.at(schemaConstant.getAttributeNode(propertyElement)))) + .map(s -> asComparableOrThrow(s, err.at(schemaConstant.getAttributeNode(propertyElement)))); + } + + + private static @Nullable T tryParsePropertyValue(BuilderAndMapper factory, String value, MessageReporter err) { + try { + return factory.getXmlMapper().fromString(value); + } catch (IllegalArgumentException e) { + throw err.error(e); + } + } + + private static Comparable asComparableOrThrow(T object, MessageReporter err) { + if (object instanceof Comparable) { + return (Comparable) object; + } + throw err.error("Object is not comparable"); + } + private static T parsePropertyValue(Element propertyElt, PmdXmlReporter err, PropertySerializer syntax) { String valueAttr = PROPERTY_VALUE.getAttributeOrNull(propertyElt); Element valueChild = PROPERTY_VALUE.getOptChildIn(propertyElt, err); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java index 2313d0afa9..353686f942 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/SchemaConstants.java @@ -37,6 +37,9 @@ public final class SchemaConstants { public static final SchemaConstant DELIMITER = new SchemaConstant("delimiter"); + public static final SchemaConstant PROPERTY_MIN = new SchemaConstant("min"); + public static final SchemaConstant PROPERTY_MAX = new SchemaConstant("max"); + private SchemaConstants() { // utility class } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java index 0338dd62ea..60973f9f3c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/internal/xml/XmlErrorMessages.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.util.internal.xml; // CHECKSTYLE:OFF public final class XmlErrorMessages { + public static final String ERR__INVALID_VALUE_RANGE = "Minimum value should be lower than maximum value"; private static final String THIS_WILL_BE_IGNORED = ", this will be ignored"; /** {0}: unexpected element name; {1}: parent node name; {2}: list of allowed elements in this context */ diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java index df75827af4..533623f4f4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RulesetFactoryTestBase.java @@ -156,7 +156,7 @@ public class RulesetFactoryTestBase { map -> { map.put(SchemaConstants.NAME, "MockRuleName"); map.put(SchemaConstants.LANGUAGE, DummyLanguageModule.TERSE_NAME); - map.put(SchemaConstants.CLASS, net.sourceforge.pmd.lang.rule.MockRule.class.getName()); + map.put(SchemaConstants.CLASS, net.sourceforge.pmd.lang.rule.MockRuleWithNoProperties.class.getName()); map.put(SchemaConstants.MESSAGE, "avoid the mock rule"); } ); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java index 5446339b1d..e3b760674d 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRule.java @@ -19,7 +19,7 @@ import net.sourceforge.pmd.properties.PropertyFactory; * editable surrogate used by IDE plugins. The Language of this Rule defaults to * Java. */ -public class MockRule extends AbstractRule { +public class MockRule extends MockRuleWithNoProperties { public static final PropertyDescriptor PROP = PropertyFactory.intProperty("testIntProperty") @@ -32,16 +32,12 @@ public class MockRule extends AbstractRule { } public MockRule(String name, String description, String message, String ruleSetName, RulePriority priority) { - this(name, description, message, ruleSetName); - setPriority(priority); + super(name, description, message, ruleSetName, priority); + definePropertyDescriptor(PROP); } public MockRule(String name, String description, String message, String ruleSetName) { - this(); - setName(name); - setDescription(description); - setMessage(message); - setRuleSetName(ruleSetName); + this(name, description, message, ruleSetName, RulePriority.MEDIUM); } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRuleWithNoProperties.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRuleWithNoProperties.java new file mode 100644 index 0000000000..ec4f081756 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/MockRuleWithNoProperties.java @@ -0,0 +1,41 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.rule; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.RulePriority; +import net.sourceforge.pmd.lang.ast.Node; + + +/** + * This is a Rule implementation which can be used in scenarios where an actual + * functional Rule is not needed. For example, during unit testing, or as an + * editable surrogate used by IDE plugins. The Language of this Rule defaults to + * Java. + */ +public class MockRuleWithNoProperties extends AbstractRule { + + public MockRuleWithNoProperties() { + super(); + } + + public MockRuleWithNoProperties(String name, String description, String message, String ruleSetName, RulePriority priority) { + this(name, description, message, ruleSetName); + setPriority(priority); + } + + public MockRuleWithNoProperties(String name, String description, String message, String ruleSetName) { + this(); + setName(name); + setDescription(description); + setMessage(message); + setRuleSetName(ruleSetName); + } + + @Override + public void apply(Node node, RuleContext ctx) { + // the mock rule does nothing. Usually you would start here to analyze the AST. + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java index 81bd3c7e31..ae266e5a65 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertySyntaxTest.java @@ -4,18 +4,121 @@ package net.sourceforge.pmd.properties; +import static net.sourceforge.pmd.util.CollectionUtil.emptyList; import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import static net.sourceforge.pmd.util.CollectionUtil.setOf; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.ArrayList; + import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulesetFactoryTestBase; +import net.sourceforge.pmd.util.internal.xml.XmlErrorMessages; /** * @author Clรฉment Fournier */ class PropertySyntaxTest extends RulesetFactoryTestBase { + private PropertyDescriptor defineProperty(String propDef) { + Rule rule = loadFirstRule(contextForPropertyDef(propDef)); + + ArrayList> descriptors = new ArrayList<>(rule.getPropertyDescriptors()); + descriptors.removeAll(setOf(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR, + Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR)); + return descriptors.get(0); + } + + private static String contextForPropertyDef(String propDef) { + return rulesetXml( + dummyRule( + properties( + propDef + ) + ) + ); + } + + + @Test + void testPropDefXml() { + PropertyDescriptor prop = defineProperty( + propertyDefWithValueAttr("pname", "pdesc", "String", "strvalue") + ); + + assertEquals("pname", prop.name()); + assertEquals("strvalue", prop.defaultValue()); + } + + @Test + void testNumericPropDefWithoutBounds() { + // https://github.com/pmd/pmd/issues/1204 + PropertyDescriptor prop = defineProperty( + "" + ); + + assertEquals("pname", prop.name()); + assertEquals(4, prop.defaultValue()); + assertEquals(emptyList(), prop.serializer().getConstraints()); + } + + @Test + void testNumericPropDefWithMinBound() { + // https://github.com/pmd/pmd/issues/1204 + PropertyDescriptor prop = defineProperty( + "" + ); + + assertEquals("pname", prop.name()); + assertEquals(4, prop.defaultValue()); + assertEquals(1, prop.serializer().getConstraints().size()); + assertEquals("Should be greater or equal to 1", prop.serializer().getConstraints().get(0).getConstraintDescription()); + } + + + @Test + void testNumericPropDefWithMaxBound() { + // https://github.com/pmd/pmd/issues/1204 + PropertyDescriptor prop = defineProperty( + "" + ); + + assertEquals("Should be smaller or equal to 6", prop.serializer().getConstraints().get(0).getConstraintDescription()); + } + + @Test + void testNumericPropDefWithMaxAndMin() { + // https://github.com/pmd/pmd/issues/1204 + PropertyDescriptor prop = defineProperty( + "" + ); + + assertEquals("Should be between 2 and 6", prop.serializer().getConstraints().get(0).getConstraintDescription()); + } + + @Test + void testNumericPropDefWithMaxAndMinUnordered() { + assertCannotParse( + contextForPropertyDef( + "" + ) + ); + verifyFoundAnErrorWithMessage(containing(XmlErrorMessages.ERR__INVALID_VALUE_RANGE)); + } + + @Test + void testNumericPropConstraintViolated() { + // https://github.com/pmd/pmd/issues/1204 + assertCannotParse(contextForPropertyDef( + "" + )); + verifyFoundAnErrorWithMessage(containing("'4' should be smaller or equal to 1")); + + } + + @Test void testStringProp() { assertValueRoundTrip(PropertyParsingUtil.STRING, "ad", "ad"); From 6c27d46b6bdc5fd42b63f2654bff9f2ba6787218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 18 Feb 2023 14:03:30 +0100 Subject: [PATCH 116/347] Cleanup --- .../net/sourceforge/pmd/cpd/TestTokenFactory.java | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java deleted file mode 100644 index aa379e3a23..0000000000 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/TestTokenFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -/** - * @author Clรฉment Fournier - */ -public class TestTokenFactory { -} From 62beb2b5fb1a4e73497f40e344d26b7d0d3465aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 18 Feb 2023 17:08:20 +0100 Subject: [PATCH 117/347] Update TSQL module --- .../pmd/lang/tsql/TSqlLanguageModule.java | 25 +++++++++++++++++++ .../pmd/lang/tsql/cpd/TSqlLanguage.java | 17 ------------- .../pmd/lang/tsql/cpd/TSqlTokenizer.java | 2 +- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../pmd/lang/tsql/cpd/TSqlTokenizerTest.java | 15 +---------- 6 files changed, 28 insertions(+), 33 deletions(-) create mode 100644 pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/TSqlLanguageModule.java delete mode 100644 pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlLanguage.java delete mode 100644 pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language diff --git a/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/TSqlLanguageModule.java b/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/TSqlLanguageModule.java new file mode 100644 index 0000000000..7cf569c2ec --- /dev/null +++ b/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/TSqlLanguageModule.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.tsql; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.tsql.cpd.TSqlTokenizer; + +/** + * @author pguyot@kallisys.net + */ +public class TSqlLanguageModule extends CpdOnlyLanguageModuleBase { + + public TSqlLanguageModule() { + super(LanguageMetadata.withId("tsql").name("TSql").extensions("sql")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new TSqlTokenizer(); + } +} diff --git a/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlLanguage.java b/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlLanguage.java deleted file mode 100644 index 7f087b1238..0000000000 --- a/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlLanguage.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.tsql.cpd; - -import net.sourceforge.pmd.cpd.AbstractLanguage; - -/** - * @author pguyot@kallisys.net - */ -public class TSqlLanguage extends AbstractLanguage { - - public TSqlLanguage() { - super("TSql", "tsql", new TSqlTokenizer(), ".sql"); - } -} diff --git a/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizer.java b/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizer.java index 6274dbf6da..9676b37f7c 100644 --- a/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizer.java +++ b/pmd-tsql/src/main/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizer.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.tsql.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; -import net.sourceforge.pmd.cpd.internal.AntlrTokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; import net.sourceforge.pmd.lang.tsql.ast.TSqlLexer; public class TSqlTokenizer extends AntlrTokenizer { diff --git a/pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index 1baf52236f..0000000000 --- a/pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.lang.tsql.cpd.TSqlLanguage diff --git a/pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..59fe8792ad --- /dev/null +++ b/pmd-tsql/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.tsql.TSqlLanguageModule diff --git a/pmd-tsql/src/test/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizerTest.java b/pmd-tsql/src/test/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizerTest.java index 6723ebc2fc..79f3d42e41 100644 --- a/pmd-tsql/src/test/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizerTest.java +++ b/pmd-tsql/src/test/java/net/sourceforge/pmd/lang/tsql/cpd/TSqlTokenizerTest.java @@ -4,27 +4,14 @@ package net.sourceforge.pmd.lang.tsql.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; class TSqlTokenizerTest extends CpdTextComparisonTest { TSqlTokenizerTest() { - super(".sql"); - } - - @Override - public Tokenizer newTokenizer(Properties properties) { - return new TSqlTokenizer(); - } - - @Override - protected String getResourcePrefix() { - return "../cpd/testdata"; + super("tsql", ".sql"); } @Test From 55d8953afed5d7e531dba126251239265a32644f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 18 Feb 2023 17:14:37 +0100 Subject: [PATCH 118/347] Fix tests --- .../java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java | 4 +++- .../src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java index 1b0a8d4e15..57021a387e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryMessagesTest.java @@ -10,6 +10,7 @@ import static org.hamcrest.Matchers.containsString; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.rule.MockRule; +import net.sourceforge.pmd.util.internal.xml.SchemaConstants; import com.github.stefanbirkner.systemlambda.SystemLambda; @@ -28,7 +29,7 @@ class RuleSetFactoryMessagesTest extends RulesetFactoryTestBase { assertThat(log, containsString( "Error at dummyRuleset.xml:9:1\n" + " 7| \n" - + " 8| \n" + + " 8| \n" + " 9| not a priority\n" + " ^^^^^^^^^ Not a valid priority: 'not a priority', expected a number in [1,5]" )); @@ -40,6 +41,7 @@ class RuleSetFactoryMessagesTest extends RulesetFactoryTestBase { String log = SystemLambda.tapSystemErr(() -> assertCannotParse( rulesetXml( dummyRule( + attrs -> attrs.put(SchemaConstants.CLASS, MockRule.class.getName()), properties( propertyWithValueAttr(MockRule.PROP.name(), "-4") ) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index a8582aceb9..59d592e9f4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -120,7 +120,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase { assertEquals(1, rs.size()); Rule r = rs.getRules().iterator().next(); assertEquals("MockRuleName", r.getName()); - assertEquals("net.sourceforge.pmd.lang.rule.MockRule", r.getRuleClass()); + assertEquals("net.sourceforge.pmd.lang.rule.MockRuleWithNoProperties", r.getRuleClass()); assertEquals("avoid the mock rule", r.getMessage()); } From eb373881bd7f9b4d74837235bd8623438db3aec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 18 Feb 2023 17:24:18 +0100 Subject: [PATCH 119/347] Fix problem on windows --- .../java/net/sourceforge/pmd/lang/document/NioTextFile.java | 4 +++- .../test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java index 0520f907da..e513529171 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java @@ -44,7 +44,9 @@ class NioTextFile extends BaseCloseable implements TextFile { this.path = path; this.charset = charset; this.languageVersion = languageVersion; - // using the URI here, that handles files inside zip archives automatically (schema "jar:file:...!/path/inside/zip") + // Using the URI here, that handles files inside zip archives automatically (schema "jar:file:...!/path/inside/zip") + // Note that on Windows, the path id cannot be mapped back to a Path instance, + // (because it might contain : for the drive name) this.pathId = path.toUri().toString(); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java index ce973783c3..73f86deef6 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java @@ -8,11 +8,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; -import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.commons.io.FilenameUtils; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.lang.DummyLanguageModule; @@ -34,7 +34,7 @@ class CPDFilelistTest { assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { - simpleNames.add(Paths.get(path).getFileName().toString()); + simpleNames.add(FilenameUtils.getName(path)); } assertTrue(simpleNames.contains("anotherfile.dummy")); assertTrue(simpleNames.contains("somefile.dummy")); @@ -53,7 +53,7 @@ class CPDFilelistTest { assertEquals(2, paths.size()); Set simpleNames = new HashSet<>(); for (String path : paths) { - simpleNames.add(Paths.get(path).getFileName().toString()); + simpleNames.add(FilenameUtils.getName(path)); } assertTrue(simpleNames.contains("anotherfile.dummy")); assertTrue(simpleNames.contains("somefile.dummy")); From de7ff21c1a0cc005880ee793b207dbad068167b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 19 Feb 2023 01:24:38 +0100 Subject: [PATCH 120/347] Fix bug with renderer encoding --- .../sourceforge/pmd/cpd/CPDConfiguration.java | 21 +++++++++----- .../pmd/cpd/CPDConfigurationTest.java | 29 +++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index f514588e9f..25aabf4e6c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.cpd; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.io.File; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.Collections; @@ -17,6 +16,8 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.AbstractConfiguration; @@ -52,7 +53,7 @@ public class CPDConfiguration extends AbstractConfiguration { private String rendererName = DEFAULT_RENDERER; - CPDReportRenderer cpdReportRenderer; + private @Nullable CPDReportRenderer cpdReportRenderer; private boolean ignoreLiterals; @@ -93,6 +94,14 @@ public class CPDConfiguration extends AbstractConfiguration { super(languageRegistry, new SimpleMessageReporter(LoggerFactory.getLogger(CpdAnalysis.class))); } + @Override + public void setSourceEncoding(String sourceEncoding) { + super.setSourceEncoding(sourceEncoding); + if (cpdReportRenderer != null) { + setRendererEncoding(cpdReportRenderer, sourceEncoding); + } + } + static CPDReportRenderer createRendererByName(String name, String encoding) { if (name == null || "".equals(name)) { name = DEFAULT_RENDERER; @@ -112,7 +121,7 @@ public class CPDConfiguration extends AbstractConfiguration { } } - CPDReportRenderer renderer = null; + CPDReportRenderer renderer; try { renderer = rendererClass.getDeclaredConstructor().newInstance(); setRendererEncoding(renderer, encoding); @@ -123,16 +132,14 @@ public class CPDConfiguration extends AbstractConfiguration { return renderer; } - - private static void setRendererEncoding(Object renderer, String encoding) - throws IllegalAccessException, InvocationTargetException { + private static void setRendererEncoding(@NonNull Object renderer, String encoding) { try { PropertyDescriptor encodingProperty = new PropertyDescriptor("encoding", renderer.getClass()); Method method = encodingProperty.getWriteMethod(); if (method != null) { method.invoke(renderer, encoding); } - } catch (IntrospectionException ignored) { + } catch (IntrospectionException | ReflectiveOperationException ignored) { // ignored - maybe this renderer doesn't have a encoding property } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java index f6e00b982f..cb5d44ca60 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java @@ -4,9 +4,13 @@ package net.sourceforge.pmd.cpd; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -32,4 +36,29 @@ class CPDConfigurationTest { } } + @Test + void testRendererEncoding() { + CPDConfiguration conf = new CPDConfiguration(); + conf.setRendererName("xml"); + conf.setSourceEncoding(StandardCharsets.UTF_16.name()); + + CPDReportRenderer renderer = conf.getCPDReportRenderer(); + assertNotNull(renderer); + assertThat(renderer, instanceOf(XMLRenderer.class)); + assertEquals(StandardCharsets.UTF_16.name(), ((XMLRenderer) renderer).getEncoding()); + } + + @Test + void testRendererEncoding2() { + CPDConfiguration conf = new CPDConfiguration(); + // here the order of these statements are reversed + conf.setSourceEncoding(StandardCharsets.UTF_16.name()); + conf.setRendererName("xml"); + + CPDReportRenderer renderer = conf.getCPDReportRenderer(); + assertNotNull(renderer); + assertThat(renderer, instanceOf(XMLRenderer.class)); + assertEquals(StandardCharsets.UTF_16.name(), ((XMLRenderer) renderer).getEncoding()); + } + } From 046812cf515cd7c4041447725cdb0c3ea5ecea37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 19 Feb 2023 01:24:47 +0100 Subject: [PATCH 121/347] Remove useless methods on Match --- .../java/net/sourceforge/pmd/cpd/GUI.java | 15 +++--- .../java/net/sourceforge/pmd/cpd/Match.java | 46 ++++--------------- .../java/net/sourceforge/pmd/cpd/Tokens.java | 12 ++--- .../sourceforge/pmd/cpd/CPDReportTest.java | 4 +- 4 files changed, 23 insertions(+), 54 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index c39c74cce7..0f47750bcd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -192,8 +192,9 @@ public class GUI implements CPDListener { } } + public static final Comparator LABEL_COMPARATOR = Comparator.comparing(GUI::getLabel); private final ColumnSpec[] matchColumns = { - new ColumnSpec("Source", SwingConstants.LEFT, -1, Match.LABEL_COMPARATOR), + new ColumnSpec("Source", SwingConstants.LEFT, -1, LABEL_COMPARATOR), new ColumnSpec("Matches", SwingConstants.RIGHT, 60, Match.MATCHES_COMPARATOR), new ColumnSpec("Lines", SwingConstants.RIGHT, 45, Match.LINES_COMPARATOR), }; @@ -576,23 +577,20 @@ public class GUI implements CPDListener { return new JScrollPane(resultsTable); } - private void setLabelFor(Match match) { + private static String getLabel(Match match) { Set sourceIDs = new HashSet<>(match.getMarkCount()); for (Mark mark : match) { sourceIDs.add(mark.getLocation().getFileName()); } - String label; if (sourceIDs.size() == 1) { String sourceId = sourceIDs.iterator().next(); int separatorPos = sourceId.lastIndexOf(File.separatorChar); - label = "..." + sourceId.substring(separatorPos); + return "..." + sourceId.substring(separatorPos); } else { - label = String.format("(%d separate files)", sourceIDs.size()); + return String.format("(%d separate files)", sourceIDs.size()); } - - match.setLabel(label); } private void setProgressControls(boolean isRunning) { @@ -640,7 +638,6 @@ public class GUI implements CPDListener { t.stop(); numberOfTokensPerFile = report.getNumberOfTokensPerFile(); matches = new ArrayList<>(report.getMatches()); - matches.forEach(this::setLabelFor); setListDataFrom(matches); String reportString = new SimpleRenderer().renderToString(report); if (reportString.isEmpty()) { @@ -710,7 +707,7 @@ public class GUI implements CPDListener { Match match = items.get(rowIndex); switch (columnIndex) { case 0: - return match.getLabel(); + return getLabel(match); case 2: return Integer.toString(match.getLineCount()); case 1: diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java index abf807e7c7..7c8857f925 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java @@ -6,39 +6,30 @@ package net.sourceforge.pmd.cpd; import java.util.Comparator; import java.util.Iterator; +import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.util.IteratorUtil; public class Match implements Comparable, Iterable { - private int tokenCount; - private Set markSet = new TreeSet<>(); - private String label; + private final int tokenCount; + private final Set markSet = new TreeSet<>(); public static final Comparator MATCHES_COMPARATOR = (ma, mb) -> mb.getMarkCount() - ma.getMarkCount(); public static final Comparator LINES_COMPARATOR = (ma, mb) -> mb.getLineCount() - ma.getLineCount(); - public static final Comparator LABEL_COMPARATOR = (ma, mb) -> { - if (ma.getLabel() == null) { - return 1; - } - if (mb.getLabel() == null) { - return -1; - } - return mb.getLabel().compareTo(ma.getLabel()); - }; - - public Match(int tokenCount, Mark first, Mark second) { + Match(int tokenCount, Mark first, Mark second) { markSet.add(first); markSet.add(second); this.tokenCount = tokenCount; } - public Match(int tokenCount, TokenEntry first, TokenEntry second) { + Match(int tokenCount, TokenEntry first, TokenEntry second) { this(tokenCount, new Mark(first), new Mark(second)); } @@ -82,37 +73,18 @@ public class Match implements Comparable, Iterable { return "Match: " + PMD.EOL + "tokenCount = " + tokenCount + PMD.EOL + "marks = " + markSet.size(); } - public Set getMarkSet() { - return markSet; - } - public int getEndIndex() { return getMark(0).getToken().getIndex() + getTokenCount() - 1; } - public void setMarkSet(Set markSet) { - this.markSet = markSet; - } - - public void setLabel(String aLabel) { - label = aLabel; - } - - public String getLabel() { - return label; - } - public void addTokenEntry(TokenEntry entry) { markSet.add(new Mark(entry)); } private Mark getMark(int index) { - Mark result = null; - int i = 0; - for (Iterator it = markSet.iterator(); it.hasNext() && i < index + 1;) { - result = it.next(); - i++; + if (index >= markSet.size()) { + throw new NoSuchElementException(); } - return result; + return IteratorUtil.getNth(markSet.iterator(), index); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 19b8a472de..01dae20e66 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -33,8 +33,8 @@ public class Tokens { this.tokens.add(tokenEntry); } - void addEof(String fileName, int line, int column) { - add(new TokenEntry(fileName, line, column)); + void addEof(String filePathId, int line, int column) { + add(new TokenEntry(filePathId, line, column)); } void setImage(TokenEntry entry, String newImage) { @@ -92,12 +92,12 @@ public class Tokens { */ static TokenFactory factoryForFile(TextDocument file, Tokens tokens) { return new TokenFactory() { - final String fileName = file.getPathId(); + final String filePathId = file.getPathId(); final int firstToken = tokens.size(); @Override public void recordToken(@NonNull String image, int startLine, int startCol, int endLine, int endCol) { - tokens.addToken(image, fileName, startLine, startCol, endLine, endCol); + tokens.addToken(image, filePathId, startLine, startCol, endLine, endCol); } @Override @@ -117,9 +117,9 @@ public class Tokens { public void close() { TokenEntry tok = peekLastToken(); if (tok == null) { - tokens.addEof(fileName, 1, 1); + tokens.addEof(filePathId, 1, 1); } else { - tokens.addEof(fileName, tok.getEndLine(), tok.getEndColumn()); + tokens.addEof(filePathId, tok.getEndLine(), tok.getEndColumn()); } } }; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java index 2fb14a353e..7d33a534a9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java @@ -32,7 +32,7 @@ class CPDReportTest { CPDReport filtered = original.filterMatches( match -> { // only keep file1.java - for (Mark mark : match.getMarkSet()) { + for (Mark mark : match) { if (mark.getLocation().getFileName().equals("file1.java")) { return true; } @@ -42,7 +42,7 @@ class CPDReportTest { assertEquals(2, filtered.getMatches().size()); for (Match match : filtered.getMatches()) { Set filenames = new HashSet<>(); - for (Mark mark : match.getMarkSet()) { + for (Mark mark : match) { filenames.add(mark.getLocation().getFileName()); } assertTrue(filenames.contains("file1.java")); From 0134f5e06012f0ae9ce36aff6c3bbdc081ddbbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 19 Feb 2023 15:02:05 +0100 Subject: [PATCH 122/347] Use Path instead of File in CPDConf --- .../java/net/sourceforge/pmd/ant/CPDTask.java | 3 +- .../pmd/ant/internal/PMDTaskImpl.java | 3 +- .../pmd/cli/commands/internal/CpdCommand.java | 10 ++-- .../pmd/cli/commands/internal/PmdCommand.java | 2 +- .../commands/internal/TreeExportCommand.java | 2 +- .../cli/commands/internal/CpdCommandTest.java | 6 +-- .../pmd/AbstractConfiguration.java | 4 +- .../sourceforge/pmd/cli/PMDParameters.java | 5 +- .../sourceforge/pmd/cpd/CPDConfiguration.java | 39 ++++++++------ .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 34 ++++--------- .../java/net/sourceforge/pmd/cpd/GUI.java | 8 ++- .../java/net/sourceforge/pmd/cpd/Mark.java | 14 ++++- .../java/net/sourceforge/pmd/cpd/Match.java | 8 +-- .../sourceforge/pmd/cpd/MatchAlgorithm.java | 42 +++++---------- .../sourceforge/pmd/cpd/MatchCollector.java | 51 +++++++++---------- .../sourceforge/pmd/cpd/SourceManager.java | 15 ++++-- .../sourceforge/pmd/PmdConfigurationTest.java | 2 +- .../pmd/cpd/CPDConfigurationTest.java | 6 +-- .../sourceforge/pmd/cpd/CPDFilelistTest.java | 5 +- .../pmd/cpd/MatchAlgorithmTest.java | 8 +-- .../pmd/lang/document/TextFilesTest.java | 4 +- 21 files changed, 137 insertions(+), 134 deletions(-) diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index b18493e011..4fdfea75b3 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; +import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -91,7 +92,7 @@ public class CPDTask extends Task { CPDConfiguration config = new CPDConfiguration(); config.setMinimumTileSize(minimumTokenCount); config.setLanguage(config.getLanguageRegistry().getLanguageById(language)); - config.setSourceEncoding(encoding); + config.setSourceEncoding(Charset.forName(encoding)); config.setSkipDuplicates(skipDuplicateFiles); config.setSkipLexicalErrors(skipLexicalErrors); diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java index f3d2d5e17b..f74cf67408 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.ant.internal; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -69,7 +70,7 @@ public class PMDTaskImpl { configuration.setRuleSetFactoryCompatibilityEnabled(!task.isNoRuleSetCompatibility()); if (task.getEncoding() != null) { - configuration.setSourceEncoding(task.getEncoding()); + configuration.setSourceEncoding(Charset.forName(task.getEncoding())); } configuration.setThreads(task.getThreads()); this.failuresPropertyName = task.getFailuresPropertyName(); diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index 5f3ae64ac7..68c1ec8078 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -4,12 +4,10 @@ package net.sourceforge.pmd.cli.commands.internal; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Iterator; import java.util.List; -import java.util.stream.Collectors; import org.apache.commons.lang3.mutable.MutableBoolean; import org.slf4j.Logger; @@ -80,7 +78,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { private String skipBlocksPattern; @Option(names = "--exclude", arity = "1..*", description = "Files to be excluded from the analysis") - private List excludes; + private List excludes; @Option(names = "--non-recursive", description = "Don't scan subdirectiories.") private boolean nonRecursive; @@ -97,8 +95,8 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { configuration.setDebug(debug); configuration.setExcludes(excludes); configuration.setFailOnViolation(failOnViolation); - configuration.setFileListPath(fileListPath == null ? null : fileListPath.toString()); - configuration.setFiles(inputPaths == null ? null : inputPaths.stream().map(Path::toFile).collect(Collectors.toList())); + configuration.setFileListPath(fileListPath); + configuration.setFiles(inputPaths); configuration.setIgnoreAnnotations(ignoreAnnotations); configuration.setIgnoreIdentifiers(ignoreIdentifiers); configuration.setIgnoreLiterals(ignoreLiterals); @@ -112,7 +110,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { configuration.setSkipBlocksPattern(skipBlocksPattern); configuration.setSkipDuplicates(skipDuplicates); configuration.setSkipLexicalErrors(skipLexicalErrors); - configuration.setSourceEncoding(encoding.getEncoding().name()); + configuration.setSourceEncoding(encoding.getEncoding()); configuration.setURI(uri); return configuration; diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java index e2da3c1af7..1838d86352 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java @@ -281,7 +281,7 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand { configuration.setInputUri(uri); configuration.setReportFormat(format); configuration.setDebug(debug); - configuration.setSourceEncoding(encoding.getEncoding().name()); + configuration.setSourceEncoding(encoding.getEncoding()); configuration.setMinimumPriority(minimumPriority); configuration.setReportFile(reportFile); configuration.setReportProperties(properties); diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/TreeExportCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/TreeExportCommand.java index c034682f07..a7e061e676 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/TreeExportCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/TreeExportCommand.java @@ -91,7 +91,7 @@ public class TreeExportCommand extends AbstractPmdSubcommand { configuration.setLanguage(language); configuration.setProperties(properties); configuration.setReadStdin(readStdin); - configuration.setSourceEncoding(encoding.getEncoding().name()); + configuration.setSourceEncoding(encoding.getEncoding()); return configuration; } diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java index 53f98ccbad..f12cbc806f 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java @@ -7,13 +7,13 @@ package net.sourceforge.pmd.cli.commands.internal; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.io.File; +import java.nio.file.Path; import java.util.List; -import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.CPDConfiguration; +import net.sourceforge.pmd.util.CollectionUtil; class CpdCommandTest extends BaseCommandTest { @@ -56,7 +56,7 @@ class CpdCommandTest extends BaseCommandTest { private void assertMultipleDirs(final CpdCommand result) { final CPDConfiguration config = result.toConfiguration(); - assertEquals(listOf("a", "b"), config.getFiles().stream().map(File::toString).collect(Collectors.toList())); + assertEquals(listOf("a", "b"), CollectionUtil.map(config.getFiles(), Path::toString)); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java index 2763d8dd62..2a1172a1e3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java @@ -58,8 +58,8 @@ public abstract class AbstractConfiguration { * @param sourceEncoding * The character encoding. */ - public void setSourceEncoding(String sourceEncoding) { - this.sourceEncoding = Charset.forName(sourceEncoding); + public void setSourceEncoding(Charset sourceEncoding) { + this.sourceEncoding = Objects.requireNonNull(sourceEncoding); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java index 2c96b29c8b..9a408a0f1f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.cli; +import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -334,8 +335,8 @@ public class PMDParameters { return currentVersion; } - public String getEncoding() { - return encoding; + public Charset getEncoding() { + return Charset.forName(encoding); } public Integer getThreads() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 25aabf4e6c..b7e68ebf51 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -6,14 +6,16 @@ package net.sourceforge.pmd.cpd; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; -import java.io.File; import java.lang.reflect.Method; import java.net.URI; +import java.nio.charset.Charset; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; @@ -71,11 +73,11 @@ public class CPDConfiguration extends AbstractConfiguration { private String skipBlocksPattern = Tokenizer.DEFAULT_SKIP_BLOCKS_PATTERN; - private List files; + private List files = Collections.emptyList(); - private String fileListPath; + private Path fileListPath; - private List excludes; + private List excludes; private boolean nonRecursive; @@ -95,14 +97,14 @@ public class CPDConfiguration extends AbstractConfiguration { } @Override - public void setSourceEncoding(String sourceEncoding) { + public void setSourceEncoding(Charset sourceEncoding) { super.setSourceEncoding(sourceEncoding); if (cpdReportRenderer != null) { setRendererEncoding(cpdReportRenderer, sourceEncoding); } } - static CPDReportRenderer createRendererByName(String name, String encoding) { + static CPDReportRenderer createRendererByName(String name, Charset encoding) { if (name == null || "".equals(name)) { name = DEFAULT_RENDERER; } @@ -132,12 +134,17 @@ public class CPDConfiguration extends AbstractConfiguration { return renderer; } - private static void setRendererEncoding(@NonNull Object renderer, String encoding) { + private static void setRendererEncoding(@NonNull Object renderer, Charset encoding) { try { PropertyDescriptor encodingProperty = new PropertyDescriptor("encoding", renderer.getClass()); Method method = encodingProperty.getWriteMethod(); - if (method != null) { + if (method == null) { + return; + } + if (method.getParameterTypes()[0] == Charset.class) { method.invoke(renderer, encoding); + } else if (method.getParameterTypes()[0] == String.class) { + method.invoke(renderer, encoding.name()); } } catch (IntrospectionException | ReflectiveOperationException ignored) { // ignored - maybe this renderer doesn't have a encoding property @@ -177,7 +184,7 @@ public class CPDConfiguration extends AbstractConfiguration { if (rendererName == null) { this.cpdReportRenderer = null; } - this.cpdReportRenderer = createRendererByName(rendererName, getSourceEncoding().name()); + this.cpdReportRenderer = createRendererByName(rendererName, getSourceEncoding()); } @@ -237,19 +244,19 @@ public class CPDConfiguration extends AbstractConfiguration { this.skipLexicalErrors = skipLexicalErrors; } - public List getFiles() { + public @NonNull List getFiles() { return files; } - public void setFiles(List files) { - this.files = files; + public void setFiles(List files) { + this.files = Objects.requireNonNull(files); } - public String getFileListPath() { + public Path getFileListPath() { return fileListPath; } - public void setFileListPath(String fileListPath) { + public void setFileListPath(Path fileListPath) { this.fileListPath = fileListPath; } @@ -261,11 +268,11 @@ public class CPDConfiguration extends AbstractConfiguration { this.uri = uri; } - public List getExcludes() { + public List getExcludes() { return excludes; } - public void setExcludes(List excludes) { + public void setExcludes(List excludes) { this.excludes = excludes; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 7f9c40dfda..c5300fd6bd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.cpd; -import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.HashMap; @@ -20,7 +19,6 @@ import org.slf4j.LoggerFactory; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.internal.util.FileCollectionUtil; -import net.sourceforge.pmd.internal.util.FileUtil; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; @@ -41,7 +39,7 @@ public final class CpdAnalysis implements AutoCloseable { private @NonNull CPDListener listener = new CPDNullListener(); - public CpdAnalysis(CPDConfiguration config) throws IOException { + public CpdAnalysis(CPDConfiguration config) { configuration = config; this.reporter = config.getReporter(); this.files = FileCollector.newCollector( @@ -82,25 +80,15 @@ public final class CpdAnalysis implements AutoCloseable { return files; } - private void extractAllSources(CPDConfiguration configuration) throws IOException { - // Add files - if (null != configuration.getFiles() && !configuration.getFiles().isEmpty()) { - addSourcesFilesToCPD(configuration.getFiles()); - } + private void extractAllSources(CPDConfiguration configuration) { + FileCollectionUtil.collectFiles(files(), configuration.getFiles()); - // Add Database URIS - if (null != configuration.getURI()) { + if (configuration.getURI() != null) { FileCollectionUtil.collectDB(files(), configuration.getURI()); } - if (null != configuration.getFileListPath()) { - FileCollectionUtil.collectFileList(files(), FileUtil.toExistingPath(configuration.getFileListPath())); - } - } - - private void addSourcesFilesToCPD(List files) throws IOException { - for (File file : files) { - files().addFileOrDirectory(file.toPath()); + if (configuration.getFileListPath() != null) { + FileCollectionUtil.collectFileList(files(), configuration.getFileListPath()); } } @@ -141,7 +129,7 @@ public final class CpdAnalysis implements AutoCloseable { Tokens.State savedState = tokens.savePoint(); try { int newTokens = doTokenize(textDocument, tokenizers.get(textFile.getLanguageVersion().getLanguage()), tokens); - numberOfTokensPerFile.put(textDocument.getPathId(), newTokens); + numberOfTokensPerFile.put(textDocument.getDisplayName(), newTokens); listener.addedFile(1); } catch (TokenMgrError | IOException e) { if (e instanceof TokenMgrError) { // NOPMD @@ -155,11 +143,11 @@ public final class CpdAnalysis implements AutoCloseable { LOGGER.debug("Running match algorithm on {} files...", sourceManager.size()); - MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, configuration.getMinimumTileSize(), listener); - matchAlgorithm.findMatches(); - LOGGER.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size()); + MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, configuration.getMinimumTileSize()); + List matches = matchAlgorithm.findMatches(listener, sourceManager); + LOGGER.debug("Finished: {} duplicates found", matches.size()); - CPDReport cpdReport = new CPDReport(sourceManager, matchAlgorithm.getMatches(), numberOfTokensPerFile); + CPDReport cpdReport = new CPDReport(sourceManager, matches, numberOfTokensPerFile); if (renderer != null) { renderer.render(cpdReport, IOUtil.createWriter(Charset.defaultCharset(), null)); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 0f47750bcd..6fcdf61fd3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -20,6 +20,7 @@ import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.nio.charset.Charset; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; @@ -604,7 +605,7 @@ public class GUI implements CPDListener { File dirPath = new File(rootDirectoryField.getText()); if (!dirPath.exists()) { JOptionPane.showMessageDialog(frame, "Can't read from that root source directory", "Error", - JOptionPane.ERROR_MESSAGE); + JOptionPane.ERROR_MESSAGE); return; } @@ -612,7 +613,10 @@ public class GUI implements CPDListener { CPDConfiguration config = new CPDConfiguration(); config.setMinimumTileSize(Integer.parseInt(minimumLengthField.getText())); - config.setSourceEncoding(encodingField.getText()); + try { + config.setSourceEncoding(Charset.forName(encodingField.getText())); + } catch (IllegalArgumentException ignored) { + } config.setIgnoreIdentifiers(ignoreIdentifiersCheckbox.isSelected()); config.setIgnoreLiterals(ignoreLiteralsCheckbox.isSelected()); config.setIgnoreAnnotations(ignoreAnnotationsCheckbox.isSelected()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java index c4ceb5929d..4e7e3fba23 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Mark.java @@ -21,6 +21,7 @@ public final class Mark implements Comparable { private final @NonNull TokenEntry token; private @Nullable TokenEntry endToken; + private String fileDisplayName; Mark(@NonNull TokenEntry token) { this.token = token; @@ -40,11 +41,18 @@ public final class Mark implements Comparable { public FileLocation getLocation() { TokenEntry endToken = getEndToken(); return FileLocation.range( - this.token.getFilePathId(), + getFileName(), TextRange2d.range2d(token.getBeginLine(), token.getBeginColumn(), endToken.getEndLine(), endToken.getEndColumn())); } + String getFileName() { + if (fileDisplayName == null) { + return token.getFilePathId(); + } + return fileDisplayName; + } + public int getBeginTokenIndex() { return this.token.getIndex(); } @@ -88,4 +96,8 @@ public final class Mark implements Comparable { public int compareTo(Mark other) { return getToken().compareTo(other.getToken()); } + + public void setFileDisplayName(String fileDisplayName) { + this.fileDisplayName = fileDisplayName; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java index 7c8857f925..0c0a474666 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Match.java @@ -33,6 +33,10 @@ public class Match implements Comparable, Iterable { this(tokenCount, new Mark(first), new Mark(second)); } + void addMark(TokenEntry entry) { + markSet.add(new Mark(entry)); + } + public int getMarkCount() { return markSet.size(); } @@ -77,10 +81,6 @@ public class Match implements Comparable, Iterable { return getMark(0).getToken().getIndex() + getTokenCount() - 1; } - public void addTokenEntry(TokenEntry entry) { - markSet.add(new Mark(entry)); - } - private Mark getMark(int index) { if (index >= markSet.size()) { throw new NoSuchElementException(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java index 80dab17a83..b25e8a03fc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cpd; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -18,53 +19,35 @@ class MatchAlgorithm { private static final int MOD = 37; private int lastMod = 1; - private List matches; private final Tokens tokens; private final List code; - private @NonNull CPDListener cpdListener; - private final int min; + private final int minTileSize; - MatchAlgorithm(Tokens tokens, int min) { - this(tokens, min, new CPDNullListener()); - } - - MatchAlgorithm(Tokens tokens, int min, @NonNull CPDListener listener) { + MatchAlgorithm(Tokens tokens, int minTileSize) { this.tokens = tokens; this.code = tokens.getTokens(); - this.min = min; - this.cpdListener = listener; - for (int i = 0; i < min; i++) { + this.minTileSize = minTileSize; + for (int i = 0; i < minTileSize; i++) { lastMod *= MOD; } } - public void setListener(CPDListener listener) { - this.cpdListener = listener; - } - - public Iterator matches() { - return matches.iterator(); - } - - List getMatches() { - return matches; - } public TokenEntry tokenAt(int offset, TokenEntry m) { return code.get(offset + m.getIndex()); } public int getMinimumTileSize() { - return this.min; + return this.minTileSize; } - public void findMatches() { + public List findMatches(@NonNull CPDListener cpdListener, SourceManager sourceManager) { cpdListener.phaseUpdate(CPDListener.HASH); Map markGroups = hash(); cpdListener.phaseUpdate(CPDListener.MATCH); MatchCollector matchCollector = new MatchCollector(this); - for (Iterator i = markGroups.values().iterator(); i.hasNext();) { + for (Iterator i = markGroups.values().iterator(); i.hasNext(); ) { Object o = i.next(); if (o instanceof List) { @SuppressWarnings("unchecked") @@ -75,7 +58,8 @@ class MatchAlgorithm { i.remove(); } cpdListener.phaseUpdate(CPDListener.GROUPING); - matches = matchCollector.getMatches(); + List matches = matchCollector.getMatches(); + matches.sort(Comparator.naturalOrder()); for (Match match : matches) { for (Mark mark : match) { @@ -83,9 +67,11 @@ class MatchAlgorithm { TokenEntry endToken = tokens.getEndToken(token, match); mark.setEndToken(endToken); + mark.setFileDisplayName(sourceManager.getFileDisplayName(token.getFilePathId())); } } cpdListener.phaseUpdate(CPDListener.DONE); + return matches; } @SuppressWarnings("PMD.JumbledIncrementer") @@ -95,7 +81,7 @@ class MatchAlgorithm { for (int i = code.size() - 1; i >= 0; i--) { TokenEntry token = code.get(i); if (!token.isEof()) { - int last = tokenAt(min, token).getIdentifier(); + int last = tokenAt(minTileSize, token).getIdentifier(); lastHash = MOD * lastHash + token.getIdentifier() - lastMod * last; token.setHashCode(lastHash); Object o = markGroups.get(token); @@ -117,7 +103,7 @@ class MatchAlgorithm { } } else { lastHash = 0; - for (int end = Math.max(0, i - min + 1); i > end; i--) { + for (int end = Math.max(0, i - minTileSize + 1); i > end; i--) { token = code.get(i - 1); lastHash = MOD * lastHash + token.getIdentifier(); if (token.isEof()) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java index db49682393..b79ac5efbd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchCollector.java @@ -5,17 +5,17 @@ package net.sourceforge.pmd.cpd; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; -public class MatchCollector { - private List matchList = new ArrayList<>(); - private Map> matchTree = new TreeMap<>(); - private MatchAlgorithm ma; +class MatchCollector { - public MatchCollector(MatchAlgorithm ma) { + private final List matchList = new ArrayList<>(); + private final Map> matchTree = new TreeMap<>(); + private final MatchAlgorithm ma; + + MatchCollector(MatchAlgorithm ma) { this.ma = ma; } @@ -48,25 +48,26 @@ public class MatchCollector { } private void reportMatch(TokenEntry mark1, TokenEntry mark2, int dupes) { - Map matches = matchTree.get(dupes); - if (matches == null) { - matches = new TreeMap<>(); - matchTree.put(dupes, matches); - addNewMatch(mark1, mark2, dupes, matches); - } else { - Match matchA = matchTree.get(dupes).get(mark1.getIndex()); - Match matchB = matchTree.get(dupes).get(mark2.getIndex()); + matchTree.compute(dupes, (dupCount, matches) -> { + if (matches == null) { + matches = new TreeMap<>(); + addNewMatch(mark1, mark2, dupCount, matches); + } else { + Match matchA = matches.get(mark1.getIndex()); + Match matchB = matches.get(mark2.getIndex()); - if (matchA == null && matchB == null) { - addNewMatch(mark1, mark2, dupes, matches); - } else if (matchA == null) { - matchB.addTokenEntry(mark1); - matches.put(mark1.getIndex(), matchB); - } else if (matchB == null) { - matchA.addTokenEntry(mark2); - matches.put(mark2.getIndex(), matchA); + if (matchA == null && matchB == null) { + addNewMatch(mark1, mark2, dupes, matches); + } else if (matchA == null) { + matchB.addMark(mark1); + matches.put(mark1.getIndex(), matchB); + } else if (matchB == null) { + matchA.addMark(mark2); + matches.put(mark2.getIndex(), matchA); + } } - } + return matches; + }); } private void addNewMatch(TokenEntry mark1, TokenEntry mark2, int dupes, Map matches) { @@ -76,9 +77,7 @@ public class MatchCollector { matchList.add(match); } - @SuppressWarnings("PMD.CompareObjectsWithEquals") - public List getMatches() { - Collections.sort(matchList); + List getMatches() { return matchList; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index bc78ca444b..c829799ea2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -22,12 +22,12 @@ import net.sourceforge.pmd.lang.document.TextRegion; class SourceManager implements AutoCloseable { private final Map> files = new ConcurrentHashMap<>(); - private final Map fileByName = new HashMap<>(); + private final Map fileByPathId = new HashMap<>(); private final List textFiles; SourceManager(List files) { textFiles = new ArrayList<>(files); - files.forEach(f -> fileByName.put(f.getPathId(), f)); + files.forEach(f -> fileByPathId.put(f.getPathId(), f)); } @@ -62,11 +62,16 @@ class SourceManager implements AutoCloseable { @SuppressWarnings("PMD.CloseResource") public Chars getSlice(Mark mark) { - FileLocation loc = mark.getLocation(); - TextFile textFile = fileByName.get(loc.getFileName()); - assert textFile != null; + TextFile textFile = fileByPathId.get(mark.getToken().getFilePathId()); + assert textFile != null: "No such file " + mark.getToken().getFilePathId(); TextDocument doc = get(textFile); + assert doc != null; + FileLocation loc = mark.getLocation(); TextRegion lineRange = doc.createLineRange(loc.getStartLine(), loc.getEndLine()); return doc.sliceOriginalText(lineRange); } + + public String getFileDisplayName(String filePathId) { + return fileByPathId.get(filePathId).getDisplayName(); + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/PmdConfigurationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/PmdConfigurationTest.java index 7fdc4933f6..4224a86390 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/PmdConfigurationTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/PmdConfigurationTest.java @@ -152,7 +152,7 @@ class PmdConfigurationTest { void testSourceEncoding() { PMDConfiguration configuration = new PMDConfiguration(); assertEquals(System.getProperty("file.encoding"), configuration.getSourceEncoding().name(), "Default source encoding"); - configuration.setSourceEncoding(StandardCharsets.UTF_16LE.name()); + configuration.setSourceEncoding(StandardCharsets.UTF_16LE); assertEquals(StandardCharsets.UTF_16LE, configuration.getSourceEncoding(), "Changed source encoding"); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java index cb5d44ca60..68427f6781 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java @@ -30,7 +30,7 @@ class CPDConfigurationTest { renderersToTest.put("text", SimpleRenderer.class); for (Map.Entry> entry : renderersToTest.entrySet()) { - CPDReportRenderer r = CPDConfiguration.createRendererByName(entry.getKey(), "UTF-8"); + CPDReportRenderer r = CPDConfiguration.createRendererByName(entry.getKey(), StandardCharsets.UTF_8); assertNotNull(r); assertSame(entry.getValue(), r.getClass()); } @@ -40,7 +40,7 @@ class CPDConfigurationTest { void testRendererEncoding() { CPDConfiguration conf = new CPDConfiguration(); conf.setRendererName("xml"); - conf.setSourceEncoding(StandardCharsets.UTF_16.name()); + conf.setSourceEncoding(StandardCharsets.UTF_16); CPDReportRenderer renderer = conf.getCPDReportRenderer(); assertNotNull(renderer); @@ -52,7 +52,7 @@ class CPDConfigurationTest { void testRendererEncoding2() { CPDConfiguration conf = new CPDConfiguration(); // here the order of these statements are reversed - conf.setSourceEncoding(StandardCharsets.UTF_16.name()); + conf.setSourceEncoding(StandardCharsets.UTF_16); conf.setRendererName("xml"); CPDReportRenderer renderer = conf.getCPDReportRenderer(); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java index 73f86deef6..b57ffac5ae 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; +import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -25,7 +26,7 @@ class CPDFilelistTest { void testFilelist() throws IOException { CPDConfiguration arguments = new CPDConfiguration(); arguments.setLanguage(DummyLanguageModule.getInstance()); - arguments.setFileListPath("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist.txt"); + arguments.setFileListPath(Paths.get("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist.txt")); List paths; try (CpdAnalysis cpd = new CpdAnalysis(arguments)) { paths = CollectionUtil.map(cpd.files().getCollectedFiles(), TextFile::getPathId); @@ -44,7 +45,7 @@ class CPDFilelistTest { void testFilelistMultipleLines() throws IOException { CPDConfiguration arguments = new CPDConfiguration(); arguments.setLanguage(DummyLanguageModule.getInstance()); - arguments.setFileListPath("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist2.txt"); + arguments.setFileListPath(Paths.get("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist2.txt")); List paths; try (CpdAnalysis cpd = new CpdAnalysis(arguments)) { paths = CollectionUtil.map(cpd.files().getCollectedFiles(), TextFile::getPathId); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java index ee3fe64048..bd70a8a621 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/MatchAlgorithmTest.java @@ -10,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import java.io.IOException; import java.util.Iterator; +import java.util.List; import org.junit.jupiter.api.Test; @@ -46,10 +47,9 @@ class MatchAlgorithmTest { assertEquals(44, tokens.size()); MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, 5); - matchAlgorithm.findMatches(); - Iterator matches = matchAlgorithm.matches(); - Match match = matches.next(); - assertFalse(matches.hasNext()); + List matches = matchAlgorithm.findMatches(new CPDNullListener(), sourceManager); + assertEquals(1, matches.size()); + Match match = matches.get(0); Iterator marks = match.iterator(); Mark mark1 = marks.next(); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java index ed0721292d..b67e7a7444 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/TextFilesTest.java @@ -102,13 +102,13 @@ class TextFilesTest { DataSource ds = new FileDataSource(file.toFile()); PMDConfiguration config = new PMDConfiguration(); config.setForceLanguageVersion(DummyLanguageModule.getInstance().getDefaultVersion()); - config.setSourceEncoding(StandardCharsets.UTF_16BE.name()); + config.setSourceEncoding(StandardCharsets.UTF_16BE); try (TextFile tf = TextFile.dataSourceCompat(ds, config)) { assertEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); } // different encoding to produce garbage, to make sure encoding is used - config.setSourceEncoding(StandardCharsets.UTF_16LE.name()); + config.setSourceEncoding(StandardCharsets.UTF_16LE); try (TextFile tf = TextFile.dataSourceCompat(ds, config)) { assertNotEquals(Chars.wrap("some content"), tf.readContents().getNormalizedText()); } From a3831e95dc31fb5eb9a17a94282aa342947df1f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 19 Feb 2023 15:39:47 +0100 Subject: [PATCH 123/347] move more things into AbstractConfiguration --- .../pmd/cli/commands/internal/CpdCommand.java | 29 +- .../net/sourceforge/pmd/cli/CpdCliTest.java | 11 +- .../net/sourceforge/pmd/cli/PmdCliTest.java | 15 +- .../cli/commands/internal/CpdCommandTest.java | 2 +- .../pmd/AbstractConfiguration.java | 266 +++++++++++++++++- .../net/sourceforge/pmd/PMDConfiguration.java | 234 +-------------- .../sourceforge/pmd/cli/PMDParameters.java | 5 +- .../sourceforge/pmd/cpd/CPDConfiguration.java | 59 +--- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 16 +- .../pmd/internal/util/FileCollectionUtil.java | 4 +- .../sourceforge/pmd/internal/util/IOUtil.java | 1 - .../pmd/lang/LanguageVersionDiscoverer.java | 15 +- .../pmd/lang/document/FileCollector.java | 33 +-- .../pmd/lang/document/FileCollectorTest.java | 2 +- 14 files changed, 337 insertions(+), 355 deletions(-) diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index 68c1ec8078..003059ff3e 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.cli.commands.internal; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; import java.util.List; @@ -83,6 +84,25 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { @Option(names = "--non-recursive", description = "Don't scan subdirectiories.") private boolean nonRecursive; + + private List relativizeRootPaths; + @Option(names = { "--relativize-paths-with", "-z"}, description = "Path relative to which directories are rendered in the report. " + + "This option allows shortening directories in the report; " + + "without it, paths are rendered as mentioned in the source directory (option \"--dir\"). " + + "The option can be repeated, in which case the shortest relative path will be used. " + + "If the root path is mentioned (e.g. \"/\" or \"C:\\\"), then the paths will be rendered as absolute.", + arity = "1..*", split = ",") + public void setRelativizePathsWith(List rootPaths) { + this.relativizeRootPaths = rootPaths; + + for (Path path : this.relativizeRootPaths) { + if (Files.isRegularFile(path)) { + throw new ParameterException(spec.commandLine(), + "Expected a directory path for option '--relativize-paths-with', found a file: " + path); + } + } + } + /** * Converts these parameters into a configuration. * @@ -94,9 +114,12 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { final CPDConfiguration configuration = new CPDConfiguration(); configuration.setDebug(debug); configuration.setExcludes(excludes); + if (relativizeRootPaths != null) { + configuration.addRelativizeRoots(relativizeRootPaths); + } configuration.setFailOnViolation(failOnViolation); - configuration.setFileListPath(fileListPath); - configuration.setFiles(inputPaths); + configuration.setInputFilePath(fileListPath); + configuration.setInputPathList(inputPaths); configuration.setIgnoreAnnotations(ignoreAnnotations); configuration.setIgnoreIdentifiers(ignoreIdentifiers); configuration.setIgnoreLiterals(ignoreLiterals); @@ -111,7 +134,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { configuration.setSkipDuplicates(skipDuplicates); configuration.setSkipLexicalErrors(skipLexicalErrors); configuration.setSourceEncoding(encoding.getEncoding()); - configuration.setURI(uri); + configuration.setInputUri(uri); return configuration; } diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java index 48ae61586c..36716e48cc 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java @@ -12,7 +12,6 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; -import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; @@ -35,10 +34,10 @@ class CpdCliTest extends BaseCliTest { private static final String SRC_DIR = BASE_RES_PATH + "files/"; private static final Map NUMBER_OF_TOKENS = ImmutableMap.of( - new File(SRC_DIR, "dup1.java").getAbsolutePath(), 89, - new File(SRC_DIR, "dup2.java").getAbsolutePath(), 89, - new File(SRC_DIR, "file_with_ISO-8859-1_encoding.java").getAbsolutePath(), 8, - new File(SRC_DIR, "file_with_utf8_bom.java").getAbsolutePath(), 9 + Paths.get(SRC_DIR, "dup1.java").toString(), 89, + Paths.get(SRC_DIR, "dup2.java").toString(), 89, + Paths.get(SRC_DIR, "file_with_ISO-8859-1_encoding.java").toString(), 8, + Paths.get(SRC_DIR, "file_with_utf8_bom.java").toString(), 9 ); @TempDir private Path tempDir; @@ -67,7 +66,7 @@ class CpdCliTest extends BaseCliTest { private String getExpectedFileEntryXml(final String filename) { final int numberOfTokens = NUMBER_OF_TOKENS.get(filename); return String.format(" \n", - new File(filename).getAbsolutePath(), + filename, numberOfTokens); } diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java index 87ec854b7a..56d712ec39 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java @@ -174,13 +174,26 @@ class PmdCliTest extends BaseCliTest { assertFalse(Files.exists(absoluteReportFile), "Report file must not exist yet!"); try { - runCliSuccessfully("--dir", srcDir.toString(), "--rulesets", RULESET_NO_VIOLATIONS, "--report-file", reportFile.toString()); + runCliSuccessfully("--dir", srcDir.toString(), "--rulesets", RULESET_NO_VIOLATIONS, "--report-file", reportFile); assertTrue(Files.exists(absoluteReportFile), "Report file should have been created"); } finally { Files.deleteIfExists(absoluteReportFile); } } + + @Test + void testRelativeFileInputs() throws Exception { + SystemLambda.restoreSystemProperties(() -> { + // change working directory + System.setProperty("user.dir", srcDir.toString()); + runCliSuccessfully("--dir", ".", "--rulesets", DUMMY_RULESET_WITH_VIOLATIONS) + .verify(res -> res.checkStdOut(containsString("./src/test/resources/net/sourceforge/pmd/cli/src/anotherfile.dummy"))); + + }); + } + + @Test void debugLogging() throws Exception { CliExecutionResult result = runCliSuccessfully("--debug", "--dir", srcDir.toString(), "--rulesets", RULESET_NO_VIOLATIONS); diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java index f12cbc806f..e089203283 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/commands/internal/CpdCommandTest.java @@ -56,7 +56,7 @@ class CpdCommandTest extends BaseCommandTest { private void assertMultipleDirs(final CpdCommand result) { final CPDConfiguration config = result.toConfiguration(); - assertEquals(listOf("a", "b"), CollectionUtil.map(config.getFiles(), Path::toString)); + assertEquals(listOf("a", "b"), CollectionUtil.map(config.getInputPathList(), Path::toString)); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java index 2a1172a1e3..d566175767 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java @@ -4,7 +4,13 @@ package net.sourceforge.pmd; +import java.net.URI; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +34,9 @@ import net.sourceforge.pmd.util.log.MessageReporter; */ public abstract class AbstractConfiguration { + private final List relativizeRoots = new ArrayList<>(); + protected URI inputUri; + protected Path reportFile; private Charset sourceEncoding = Charset.forName(System.getProperty("file.encoding")); private boolean debug; private final Map langProperties = new HashMap<>(); @@ -35,6 +44,11 @@ public abstract class AbstractConfiguration { private MessageReporter reporter; private final LanguageVersionDiscoverer languageVersionDiscoverer; private LanguageVersion forceLanguageVersion; + private @NonNull List inputPaths = new ArrayList<>(); + private Path inputFilePath; + private Path ignoreFilePath; + private List excludes; + private boolean nonRecursive; protected AbstractConfiguration(LanguageRegistry languageRegistry, MessageReporter messageReporter) { @@ -169,11 +183,23 @@ public abstract class AbstractConfiguration { languageVersionDiscoverer.setForcedVersion(forceLanguageVersion); } + /** + * Make it so that the only extensions that are considered are those + * of the given language. This is different from {@link #setForceLanguageVersion(LanguageVersion)} + * because that one will assign the given language version to all files + * irrespective of extension. This method, on the other hand, will + * ignore files that do not match the given language. + * + * @param lang A language + */ + protected void setOnlyRecognizeLanguage(Language lang) { + this.languageVersionDiscoverer.onlyRecognizeLanguages(LanguageRegistry.singleton(lang)); + } + /** * Set the given LanguageVersion as the current default for it's Language. * - * @param languageVersion - * the LanguageVersion + * @param languageVersion the LanguageVersion */ public void setDefaultLanguageVersion(LanguageVersion languageVersion) { Objects.requireNonNull(languageVersion); @@ -221,4 +247,240 @@ public abstract class AbstractConfiguration { } + /** + * Set the path used to shorten paths output in the report. + * The path does not need to exist. If it exists, it must point + * to a directory and not a file. See {@link #getRelativizeRoots()} + * for the interpretation. + * + *

If several paths are added, the shortest paths possible are + * built. + * + * @param path A path + * + * @throws IllegalArgumentException If the path points to a file, and not a directory + * @throws NullPointerException If the path is null + */ + public void addRelativizeRoot(Path path) { + // Note: the given path is not further modified or resolved. E.g. there is no special handling for symlinks. + // The goal is, that if the user inputs a path, PMD should output in terms of that path, not it's resolution. + this.relativizeRoots.add(Objects.requireNonNull(path)); + + if (Files.isRegularFile(path)) { + throw new IllegalArgumentException("Relativize root should be a directory: " + path); + } + } + + /** + * Add several paths to shorten paths that are output in the report. + * See {@link #addRelativizeRoot(Path)}. + * + * @param paths A list of non-null paths + * + * @throws IllegalArgumentException If any path points to a file, and not a directory + * @throws NullPointerException If the list, or any path in the list is null + */ + public void addRelativizeRoots(List paths) { + for (Path path : paths) { + addRelativizeRoot(path); + } + } + + /** + * Returns the paths used to shorten paths output in the report. + *

    + *
  • If the list is empty, then paths are not touched + *
  • If the list is non-empty, then source file paths are relativized with all the items in the list. + * The shortest of these relative paths is taken as the display name of the file. + *
+ */ + public List getRelativizeRoots() { + return Collections.unmodifiableList(relativizeRoots); + } + + /** + * Get the input URI to process for source code objects. + * + * @return URI + */ + public URI getUri() { + return inputUri; + } + + /** + * Set the input URI to process for source code objects. + * + * @param inputUri a single URI + */ + public void setInputUri(URI inputUri) { + this.inputUri = inputUri; + } + + /** + * Returns the list of input paths to explore. This is an + * unmodifiable list. + */ + public @NonNull List getInputPathList() { + return Collections.unmodifiableList(inputPaths); + } + + /** + * Set the comma separated list of input paths to process for source files. + * + * @param inputPaths The comma separated list. + * + * @throws NullPointerException If the parameter is null + * @deprecated Use {@link #setInputPathList(List)} or {@link #addInputPath(Path)} + */ + @Deprecated + public void setInputPaths(String inputPaths) { + if (inputPaths.isEmpty()) { + return; + } + List paths = new ArrayList<>(); + for (String s : inputPaths.split(",")) { + paths.add(Paths.get(s)); + } + this.inputPaths = paths; + } + + /** + * Set the input paths to the given list of paths. + * + * @throws NullPointerException If the parameter is null or contains a null value + */ + public void setInputPathList(final List inputPaths) { + AssertionUtil.requireContainsNoNullValue("input paths", inputPaths); + this.inputPaths = new ArrayList<>(inputPaths); + } + + /** + * Add an input path. It is not split on commas. + * + * @throws NullPointerException If the parameter is null + */ + public void addInputPath(@NonNull Path inputPath) { + Objects.requireNonNull(inputPath); + this.inputPaths.add(inputPath); + } + + /** Returns the path to the file list text file. */ + public @Nullable Path getInputFile() { + return inputFilePath; + } + + public @Nullable Path getIgnoreFile() { + return ignoreFilePath; + } + + /** + * The input file path points to a single file, which contains a + * comma-separated list of source file names to process. + * + * @param inputFilePath path to the file + * @deprecated Use {@link #setInputFilePath(Path)} + */ + @Deprecated + public void setInputFilePath(String inputFilePath) { + this.inputFilePath = inputFilePath == null ? null : Paths.get(inputFilePath); + } + + /** + * The input file path points to a single file, which contains a + * comma-separated list of source file names to process. + * + * @param inputFilePath path to the file + */ + public void setInputFilePath(Path inputFilePath) { + this.inputFilePath = inputFilePath; + } + + /** + * The input file path points to a single file, which contains a + * comma-separated list of source file names to ignore. + * + * @param ignoreFilePath path to the file + * @deprecated Use {@link #setIgnoreFilePath(Path)} + */ + @Deprecated + public void setIgnoreFilePath(String ignoreFilePath) { + this.ignoreFilePath = ignoreFilePath == null ? null : Paths.get(ignoreFilePath); + } + + /** + * The input file path points to a single file, which contains a + * comma-separated list of source file names to ignore. + * + * @param ignoreFilePath path to the file + */ + public void setIgnoreFilePath(Path ignoreFilePath) { + this.ignoreFilePath = ignoreFilePath; + } + + /** + * Set the input URI to process for source code objects. + * + * @param inputUri a single URI + * @deprecated Use {@link PMDConfiguration#setInputUri(URI)} + */ + @Deprecated + public void setInputUri(String inputUri) { + this.inputUri = inputUri == null ? null : URI.create(inputUri); + } + + /** + * Get the file to which the report should render. + * + * @return The file to which to render. + * @deprecated Use {@link #getReportFilePath()} + */ + @Deprecated + public String getReportFile() { + return reportFile == null ? null : reportFile.toString(); + } + + /** + * Get the file to which the report should render. + * + * @return The file to which to render. + */ + public Path getReportFilePath() { + return reportFile; + } + + /** + * Set the file to which the report should render. + * + * @param reportFile the file to set + * @deprecated Use {@link #setReportFile(Path)} + */ + @Deprecated + public void setReportFile(String reportFile) { + this.reportFile = reportFile == null ? null : Paths.get(reportFile); + } + + /** + * Set the file to which the report should render. + * + * @param reportFile the file to set + */ + public void setReportFile(Path reportFile) { + this.reportFile = reportFile; + } + + public List getExcludes() { + return excludes; + } + + public void setExcludes(List excludes) { + this.excludes = excludes; + } + + public boolean isNonRecursive() { + return nonRecursive; + } + + public void setNonRecursive(boolean nonRecursive) { + this.nonRecursive = nonRecursive; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 1f461353d0..448ade11b3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -6,13 +6,9 @@ package net.sourceforge.pmd; import java.io.File; import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Properties; @@ -108,15 +104,10 @@ public class PMDConfiguration extends AbstractConfiguration { // Rule and source file options private List ruleSets = new ArrayList<>(); private RulePriority minimumPriority = RulePriority.LOW; - private @NonNull List inputPaths = new ArrayList<>(); - private URI inputUri; - private Path inputFilePath; - private Path ignoreFilePath; private boolean ruleSetFactoryCompatibilityEnabled = true; // Reporting options private String reportFormat; - private Path reportFile; private Properties reportProperties = new Properties(); private boolean showSuppressedViolations = false; private boolean failOnViolation = true; @@ -127,7 +118,6 @@ public class PMDConfiguration extends AbstractConfiguration { private boolean benchmark; private AnalysisCache analysisCache = new NoopAnalysisCache(); private boolean ignoreIncrementalAnalysis; - private final List relativizeRoots = new ArrayList<>(); public PMDConfiguration() { this(DEFAULT_REGISTRY); @@ -348,137 +338,6 @@ public class PMDConfiguration extends AbstractConfiguration { } - /** - * Returns the list of input paths to explore. This is an - * unmodifiable list. - */ - public @NonNull List getInputPathList() { - return Collections.unmodifiableList(inputPaths); - } - - /** - * Set the comma separated list of input paths to process for source files. - * - * @param inputPaths The comma separated list. - * - * @throws NullPointerException If the parameter is null - * @deprecated Use {@link #setInputPathList(List)} or {@link #addInputPath(Path)} - */ - @Deprecated - public void setInputPaths(String inputPaths) { - if (inputPaths.isEmpty()) { - return; - } - List paths = new ArrayList<>(); - for (String s : inputPaths.split(",")) { - paths.add(Paths.get(s)); - } - this.inputPaths = paths; - } - - /** - * Set the input paths to the given list of paths. - * - * @throws NullPointerException If the parameter is null or contains a null value - */ - public void setInputPathList(final List inputPaths) { - AssertionUtil.requireContainsNoNullValue("input paths", inputPaths); - this.inputPaths = new ArrayList<>(inputPaths); - } - - - /** - * Add an input path. It is not split on commas. - * - * @throws NullPointerException If the parameter is null - */ - public void addInputPath(@NonNull Path inputPath) { - Objects.requireNonNull(inputPath); - this.inputPaths.add(inputPath); - } - - /** Returns the path to the file list text file. */ - public @Nullable Path getInputFile() { - return inputFilePath; - } - - public @Nullable Path getIgnoreFile() { - return ignoreFilePath; - } - - /** - * The input file path points to a single file, which contains a - * comma-separated list of source file names to process. - * - * @param inputFilePath path to the file - * @deprecated Use {@link #setInputFilePath(Path)} - */ - @Deprecated - public void setInputFilePath(String inputFilePath) { - this.inputFilePath = inputFilePath == null ? null : Paths.get(inputFilePath); - } - - /** - * The input file path points to a single file, which contains a - * comma-separated list of source file names to process. - * - * @param inputFilePath path to the file - */ - public void setInputFilePath(Path inputFilePath) { - this.inputFilePath = inputFilePath; - } - - /** - * The input file path points to a single file, which contains a - * comma-separated list of source file names to ignore. - * - * @param ignoreFilePath path to the file - * @deprecated Use {@link #setIgnoreFilePath(Path)} - */ - @Deprecated - public void setIgnoreFilePath(String ignoreFilePath) { - this.ignoreFilePath = ignoreFilePath == null ? null : Paths.get(ignoreFilePath); - } - - /** - * The input file path points to a single file, which contains a - * comma-separated list of source file names to ignore. - * - * @param ignoreFilePath path to the file - */ - public void setIgnoreFilePath(Path ignoreFilePath) { - this.ignoreFilePath = ignoreFilePath; - } - - /** - * Get the input URI to process for source code objects. - * - * @return URI - */ - public URI getUri() { - return inputUri; - } - - /** - * Set the input URI to process for source code objects. - * - * @param inputUri a single URI - * @deprecated Use {@link PMDConfiguration#setInputUri(URI)} - */ - @Deprecated - public void setInputUri(String inputUri) { - this.inputUri = inputUri == null ? null : URI.create(inputUri); - } - - /** - * Set the input URI to process for source code objects. - * - * @param inputUri a single URI - */ - public void setInputUri(URI inputUri) { - this.inputUri = inputUri; - } - /** * Create a Renderer instance based upon the configured reporting options. * No writer is created. @@ -502,7 +361,7 @@ public class PMDConfiguration extends AbstractConfiguration { Renderer renderer = RendererFactory.createRenderer(reportFormat, reportProperties); renderer.setShowSuppressedViolations(showSuppressedViolations); if (withReportWriter) { - renderer.setReportFile(reportFile == null ? null : reportFile.toString()); + renderer.setReportFile(getReportFile()); } return renderer; } @@ -528,46 +387,6 @@ public class PMDConfiguration extends AbstractConfiguration { this.reportFormat = reportFormat; } - /** - * Get the file to which the report should render. - * - * @return The file to which to render. - * @deprecated Use {@link #getReportFilePath()} - */ - @Deprecated - public String getReportFile() { - return reportFile == null ? null : reportFile.toString(); - } - - /** - * Get the file to which the report should render. - * - * @return The file to which to render. - */ - public Path getReportFilePath() { - return reportFile; - } - - /** - * Set the file to which the report should render. - * - * @param reportFile the file to set - * @deprecated Use {@link #setReportFile(Path)} - */ - @Deprecated - public void setReportFile(String reportFile) { - this.reportFile = reportFile == null ? null : Paths.get(reportFile); - } - - /** - * Set the file to which the report should render. - * - * @param reportFile the file to set - */ - public void setReportFile(Path reportFile) { - this.reportFile = reportFile; - } - /** * Get whether the report should show suppressed violations. * @@ -770,56 +589,5 @@ public class PMDConfiguration extends AbstractConfiguration { return ignoreIncrementalAnalysis; } - /** - * Set the path used to shorten paths output in the report. - * The path does not need to exist. If it exists, it must point - * to a directory and not a file. See {@link #getRelativizeRoots()} - * for the interpretation. - * - *

If several paths are added, the shortest paths possible are - * built. - * - * @param path A path - * - * @throws IllegalArgumentException If the path points to a file, and not a directory - * @throws NullPointerException If the path is null - */ - public void addRelativizeRoot(Path path) { - // Note: the given path is not further modified or resolved. E.g. there is no special handling for symlinks. - // The goal is, that if the user inputs a path, PMD should output in terms of that path, not it's resolution. - this.relativizeRoots.add(Objects.requireNonNull(path)); - - if (Files.isRegularFile(path)) { - throw new IllegalArgumentException("Relativize root should be a directory: " + path); - } - } - - - /** - * Add several paths to shorten paths that are output in the report. - * See {@link #addRelativizeRoot(Path)}. - * - * @param paths A list of non-null paths - * - * @throws IllegalArgumentException If any path points to a file, and not a directory - * @throws NullPointerException If the list, or any path in the list is null - */ - public void addRelativizeRoots(List paths) { - for (Path path : paths) { - addRelativizeRoot(path); - } - } - - /** - * Returns the paths used to shorten paths output in the report. - *

    - *
  • If the list is empty, then paths are not touched - *
  • If the list is non-empty, then source file paths are relativized with all the items in the list. - * The shortest of these relative paths is taken as the display name of the file. - *
- */ - public List getRelativizeRoots() { - return Collections.unmodifiableList(relativizeRoots); - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java index 9a408a0f1f..7b46c036b8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.cli; +import java.net.URI; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; @@ -445,8 +446,8 @@ public class PMDParameters { /** * @return the uri alternative to source directory. */ - public String getUri() { - return uri; + public URI getUri() { + return uri == null ? null : URI.create(uri); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index b7e68ebf51..1f235b4a9a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -7,15 +7,11 @@ package net.sourceforge.pmd.cpd; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; -import java.net.URI; import java.nio.charset.Charset; -import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; @@ -24,6 +20,7 @@ import org.slf4j.LoggerFactory; import net.sourceforge.pmd.AbstractConfiguration; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; +import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; @@ -73,16 +70,6 @@ public class CPDConfiguration extends AbstractConfiguration { private String skipBlocksPattern = Tokenizer.DEFAULT_SKIP_BLOCKS_PATTERN; - private List files = Collections.emptyList(); - - private Path fileListPath; - - private List excludes; - - private boolean nonRecursive; - - private URI uri; - private boolean help; private boolean failOnViolation = true; @@ -155,8 +142,8 @@ public class CPDConfiguration extends AbstractConfiguration { return Collections.unmodifiableSet(RENDERERS.keySet()); } - public void setLanguage(net.sourceforge.pmd.lang.Language language) { - setForceLanguageVersion(language.getDefaultVersion()); + public void setLanguage(Language language) { + setOnlyRecognizeLanguage(language); } public int getMinimumTileSize() { @@ -244,46 +231,6 @@ public class CPDConfiguration extends AbstractConfiguration { this.skipLexicalErrors = skipLexicalErrors; } - public @NonNull List getFiles() { - return files; - } - - public void setFiles(List files) { - this.files = Objects.requireNonNull(files); - } - - public Path getFileListPath() { - return fileListPath; - } - - public void setFileListPath(Path fileListPath) { - this.fileListPath = fileListPath; - } - - public URI getURI() { - return uri; - } - - public void setURI(URI uri) { - this.uri = uri; - } - - public List getExcludes() { - return excludes; - } - - public void setExcludes(List excludes) { - this.excludes = excludes; - } - - public boolean isNonRecursive() { - return nonRecursive; - } - - public void setNonRecursive(boolean nonRecursive) { - this.nonRecursive = nonRecursive; - } - public boolean isHelp() { return help; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index c5300fd6bd..6d053655fd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -48,8 +48,8 @@ public final class CpdAnalysis implements AutoCloseable { ); this.renderer = config.getCPDReportRenderer(); - // Add all sources - extractAllSources(config); + + FileCollectionUtil.collectFiles(config, files()); for (Language language : config.getLanguageRegistry()) { setLanguageProperties(language, config); @@ -80,18 +80,6 @@ public final class CpdAnalysis implements AutoCloseable { return files; } - private void extractAllSources(CPDConfiguration configuration) { - FileCollectionUtil.collectFiles(files(), configuration.getFiles()); - - if (configuration.getURI() != null) { - FileCollectionUtil.collectDB(files(), configuration.getURI()); - } - - if (configuration.getFileListPath() != null) { - FileCollectionUtil.collectFileList(files(), configuration.getFileListPath()); - } - } - public void setCpdListener(@Nullable CPDListener cpdListener) { if (cpdListener == null) { cpdListener = new CPDNullListener(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java index efa42a1df1..d955e11b7d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java @@ -15,7 +15,7 @@ import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sourceforge.pmd.PMDConfiguration; +import net.sourceforge.pmd.AbstractConfiguration; import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; @@ -34,7 +34,7 @@ public final class FileCollectionUtil { } - public static void collectFiles(PMDConfiguration configuration, FileCollector collector) { + public static void collectFiles(AbstractConfiguration configuration, FileCollector collector) { if (configuration.getSourceEncoding() != null) { collector.setCharset(configuration.getSourceEncoding()); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IOUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IOUtil.java index 1fce3ef4c4..61e3ff1f91 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IOUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IOUtil.java @@ -243,7 +243,6 @@ public final class IOUtil { public static String normalizePath(String path) { Path path1 = Paths.get(path); - path1.isAbsolute(); String normalized = path1.normalize().toString(); if (normalized.contains("." + File.separator) || normalized.contains(".." + File.separator) || "".equals(normalized)) { return null; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java index 6e7b1f3f94..ad8fef10b8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionDiscoverer.java @@ -24,7 +24,7 @@ import net.sourceforge.pmd.util.AssertionUtil; */ public class LanguageVersionDiscoverer { - private final LanguageRegistry languageRegistry; + private LanguageRegistry languageRegistry; private final Map languageToLanguageVersion = new HashMap<>(); private LanguageVersion forcedVersion; @@ -156,5 +156,16 @@ public class LanguageVersionDiscoverer { return StringUtils.substringAfterLast(fileName, "."); } - + /** + * Make it so that the only extensions that are considered are those + * of the given language. This is different from {@link #setForcedVersion(LanguageVersion)}. + * because that one will assign the given language version to all files + * irrespective of extension. This method, on the other hand, will + * ignore files that do not match the given language. + * + * @param lang A language + */ + public void onlyRecognizeLanguages(LanguageRegistry lang) { + this.languageRegistry = Objects.requireNonNull(lang); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java index 55c9a1c952..349df40646 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java @@ -62,8 +62,6 @@ public final class FileCollector implements AutoCloseable { private final LanguageVersionDiscoverer discoverer; private final MessageReporter reporter; private final String outerFsDisplayName; - @Deprecated - private final List legacyRelativizeRoots = new ArrayList<>(); private final List relativizeRootPaths = new ArrayList<>(); private boolean closed; @@ -267,11 +265,7 @@ public final class FileCollector implements AutoCloseable { } private String getLocalDisplayName(Path file) { - if (!relativizeRootPaths.isEmpty()) { - // takes precedence over legacy behavior - return getDisplayName(file, relativizeRootPaths); - } - return getDisplayNameLegacy(file, legacyRelativizeRoots); + return getDisplayName(file, relativizeRootPaths); } /** @@ -482,23 +476,6 @@ public final class FileCollector implements AutoCloseable { this.charset = Objects.requireNonNull(charset); } - /** - * Add a prefix that is used to relativize file paths as their display name. - * For instance, when adding a file {@code /tmp/src/main/java/org/foo.java}, - * and relativizing with {@code /tmp/src/}, the registered {@link TextFile} - * will have a path id of {@code /tmp/src/main/java/org/foo.java}, and a - * display name of {@code main/java/org/foo.java}. - * - *

This only matters for files added from a {@link Path} object. - * - * @param prefix Prefix to relativize (if a directory, include a trailing slash) - * - * @deprecated Use {@link #relativizeWith(Path)} - */ - @Deprecated - public void relativizeWith(String prefix) { - this.legacyRelativizeRoots.add(Objects.requireNonNull(prefix)); - } /** * Add a prefix that is used to relativize file paths as their display name. @@ -513,13 +490,7 @@ public final class FileCollector implements AutoCloseable { */ public void relativizeWith(Path path) { this.relativizeRootPaths.add(Objects.requireNonNull(path)); - Collections.sort(relativizeRootPaths, new Comparator() { - @Override - public int compare(Path o1, Path o2) { - int lengthCmp = Integer.compare(o1.getNameCount(), o2.getNameCount()); - return lengthCmp == 0 ? o1.compareTo(o2) : lengthCmp; - } - }); + relativizeRootPaths.sort(Comparator.comparingInt(Path::getNameCount).thenComparing(o -> o)); } // filtering diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java index f9879c92b3..b425bb9c1c 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/FileCollectorTest.java @@ -139,7 +139,7 @@ class FileCollectorTest { private FileCollector newCollector(LanguageVersion forcedVersion) { LanguageVersionDiscoverer discoverer = new LanguageVersionDiscoverer(LanguageRegistry.PMD, forcedVersion); FileCollector collector = FileCollector.newCollector(discoverer, new TestMessageReporter()); - collector.relativizeWith(tempFolder.toAbsolutePath().toString()); + collector.relativizeWith(tempFolder.toAbsolutePath()); return collector; } } From a12bbf8dde021322224f245bbe9169c993aa9e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 19 Feb 2023 16:31:00 +0100 Subject: [PATCH 124/347] Remove duplicated options in AbstractConfiguration Fix logging issue --- .../java/net/sourceforge/pmd/ant/CPDTask.java | 4 +- .../pmd/lang/apex/cpd/ApexCpdTest.java | 4 +- .../internal/AbstractPmdSubcommand.java | 19 ----- .../pmd/cli/commands/internal/CpdCommand.java | 24 ++++--- .../pmd/cli/commands/internal/PmdCommand.java | 8 ++- .../net/sourceforge/pmd/cli/CpdCliTest.java | 25 +++---- .../pmd/AbstractConfiguration.java | 60 +++------------- .../main/java/net/sourceforge/pmd/PMD.java | 62 +++------------- .../net/sourceforge/pmd/PMDConfiguration.java | 41 +++++++++++ .../sourceforge/pmd/cli/PMDParameters.java | 42 +++++------ .../sourceforge/pmd/cpd/CPDConfiguration.java | 7 +- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 29 ++++++-- .../java/net/sourceforge/pmd/cpd/GUI.java | 6 +- .../sourceforge/pmd/cpd/MatchAlgorithm.java | 30 ++++---- .../sourceforge/pmd/cpd/SourceManager.java | 2 +- .../pmd/internal/PmdRootLogger.java | 70 +++++++++++++++++++ .../pmd/internal/util/FileCollectionUtil.java | 11 ++- .../pmd/lang/document/FileCollector.java | 7 +- .../treeexport/TreeExportConfiguration.java | 42 +++++++++++ .../net/sourceforge/pmd/FileSelectorTest.java | 2 +- .../net/sourceforge/pmd/PmdAnalysisTest.java | 2 +- .../sourceforge/pmd/cli/PMDFilelistTest.java | 4 +- .../sourceforge/pmd/cpd/CPDFilelistTest.java | 12 ++-- .../sourceforge/pmd/cpd/CpdAnalysisTest.java | 16 ++--- .../pmd/lang/DummyLanguageModule.java | 2 +- 25 files changed, 308 insertions(+), 223 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/internal/PmdRootLogger.java diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index 4fdfea75b3..02d80ca6b3 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -91,7 +91,7 @@ public class CPDTask extends Task { log("Tokenizing files", Project.MSG_INFO); CPDConfiguration config = new CPDConfiguration(); config.setMinimumTileSize(minimumTokenCount); - config.setLanguage(config.getLanguageRegistry().getLanguageById(language)); + config.setOnlyRecognizeLanguage(config.getLanguageRegistry().getLanguageById(language)); config.setSourceEncoding(Charset.forName(encoding)); config.setSkipDuplicates(skipDuplicateFiles); config.setSkipLexicalErrors(skipLexicalErrors); @@ -104,7 +104,7 @@ public class CPDTask extends Task { config.setSkipBlocksPattern(skipBlocksPattern); } - try (CpdAnalysis cpd = new CpdAnalysis(config)) { + try (CpdAnalysis cpd = CpdAnalysis.create(config)) { addFiles(cpd); log("Starting to analyze code", Project.MSG_INFO); diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java index 60c661cfd8..c934a1f76a 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/cpd/ApexCpdTest.java @@ -33,8 +33,8 @@ class ApexCpdTest { void testIssue427() throws Exception { CPDConfiguration configuration = new CPDConfiguration(); configuration.setMinimumTileSize(10); - configuration.setLanguage(ApexLanguageModule.getInstance()); - try (CpdAnalysis cpd = new CpdAnalysis(configuration)) { + configuration.setOnlyRecognizeLanguage(ApexLanguageModule.getInstance()); + try (CpdAnalysis cpd = CpdAnalysis.create(configuration)) { cpd.files().addFile(testdir.resolve("SFDCEncoder.cls")); cpd.files().addFile(testdir.resolve("SFDCEncoderConstants.cls")); diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractPmdSubcommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractPmdSubcommand.java index 05a8ef9b75..bd55b1cc14 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractPmdSubcommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractPmdSubcommand.java @@ -6,11 +6,7 @@ package net.sourceforge.pmd.cli.commands.internal; import java.util.concurrent.Callable; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; - import net.sourceforge.pmd.cli.internal.CliExitCode; -import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Option; @@ -30,7 +26,6 @@ public abstract class AbstractPmdSubcommand implements Callable { @Override public final Integer call() throws Exception { - setupCliLogger(); validate(); return execute().getExitCode(); } @@ -48,18 +43,4 @@ public abstract class AbstractPmdSubcommand implements Callable { protected abstract CliExitCode execute(); - private void setupCliLogger() { - // only reconfigure logging, if debug flag was used on command line - // otherwise just use whatever is in conf/simplelogger.properties which happens automatically - if (debug) { - Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.TRACE); - } - - // always install java.util.logging to slf4j bridge - Slf4jSimpleConfiguration.installJulBridge(); - - // logging, mostly for testing purposes - Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); - LoggerFactory.getLogger(AbstractPmdSubcommand.class).info("Log level is at {}", defaultLogLevel); - } } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index 003059ff3e..12d400d3b4 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -7,12 +7,12 @@ package net.sourceforge.pmd.cli.commands.internal; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.lang3.mutable.MutableBoolean; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.cli.commands.typesupport.internal.CpdLanguageTypeSupport; import net.sourceforge.pmd.cli.internal.CliExitCode; @@ -20,7 +20,9 @@ import net.sourceforge.pmd.cpd.CPDConfiguration; import net.sourceforge.pmd.cpd.CpdAnalysis; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.internal.LogMessages; +import net.sourceforge.pmd.internal.PmdRootLogger; import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.util.StringUtil; import picocli.CommandLine.Command; import picocli.CommandLine.Option; @@ -79,7 +81,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { private String skipBlocksPattern; @Option(names = "--exclude", arity = "1..*", description = "Files to be excluded from the analysis") - private List excludes; + private List excludes = new ArrayList<>(); @Option(names = "--non-recursive", description = "Don't scan subdirectiories.") private boolean nonRecursive; @@ -125,9 +127,9 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { configuration.setIgnoreLiterals(ignoreLiterals); configuration.setIgnoreLiteralSequences(ignoreLiteralSequences); configuration.setIgnoreUsings(ignoreUsings); - configuration.setLanguage(language); + configuration.setOnlyRecognizeLanguage(language); configuration.setMinimumTileSize(minimumTokens); - configuration.setNonRecursive(nonRecursive); + configuration.collectFilesRecursively(!nonRecursive); configuration.setNoSkipBlocks(noSkipBlocks); configuration.setRendererName(rendererName); configuration.setSkipBlocksPattern(skipBlocksPattern); @@ -141,11 +143,13 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { @Override protected CliExitCode execute() { - final Logger logger = LoggerFactory.getLogger(CpdCommand.class); - final CPDConfiguration configuration = toConfiguration(); - try (CpdAnalysis cpd = new CpdAnalysis(configuration)) { + return PmdRootLogger.executeInLoggingContext(configuration, CpdCommand::doExecute); + } + + private static @NonNull CliExitCode doExecute(CPDConfiguration configuration) { + try (CpdAnalysis cpd = CpdAnalysis.create(configuration)) { MutableBoolean hasViolations = new MutableBoolean(); cpd.performAnalysis(report -> hasViolations.setValue(!report.getMatches().isEmpty())); @@ -154,8 +158,8 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { return CliExitCode.VIOLATIONS_FOUND; } } catch (IOException | RuntimeException e) { - logger.debug(e.toString(), e); - logger.error(LogMessages.errorDetectedMessage(1, "cpd")); + configuration.getReporter().errorEx("Exception while running CPD.", e); + configuration.getReporter().info(StringUtil.quoteMessageFormat(LogMessages.errorDetectedMessage(1, "cpd"))); return CliExitCode.ERROR; } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java index 1838d86352..bd71718bc0 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Properties; import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +31,7 @@ import net.sourceforge.pmd.cli.commands.typesupport.internal.PmdLanguageVersionT import net.sourceforge.pmd.cli.internal.CliExitCode; import net.sourceforge.pmd.cli.internal.ProgressBarListener; import net.sourceforge.pmd.internal.LogMessages; +import net.sourceforge.pmd.internal.PmdRootLogger; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -321,11 +323,15 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand { @Override protected CliExitCode execute() { + final PMDConfiguration configuration = toConfiguration(); + return PmdRootLogger.executeInLoggingContext(configuration, this::doExecute); + } + + private @NonNull CliExitCode doExecute(PMDConfiguration configuration) { if (benchmark) { TimeTracker.startGlobalTracking(); } - final PMDConfiguration configuration = toConfiguration(); final MessageReporter pmdReporter = configuration.getReporter(); try { diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java index 36716e48cc..efb3a93594 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java @@ -81,13 +81,13 @@ class CpdCliTest extends BaseCliTest { @Test void debugLogging() throws Exception { CliExecutionResult result = runCliSuccessfully("--debug", "--minimum-tokens", "340", "--dir", SRC_DIR); - result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand - Log level is at TRACE")); + result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd - Log level is at TRACE")); } @Test void defaultLogging() throws Exception { CliExecutionResult result = runCliSuccessfully("--minimum-tokens", "340", "--dir", SRC_DIR); - result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand - Log level is at INFO")); + result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd - Log level is at INFO")); } @Test @@ -146,17 +146,17 @@ class CpdCliTest extends BaseCliTest { @Test void testNoDuplicatesResultRendering() throws Exception { - final Path absoluteSrcDir = Paths.get(SRC_DIR).toAbsolutePath(); + final Path srcDir = Paths.get(SRC_DIR); String expectedReport = "\n" + "\n" - + " \n" - + " \n" - + " \n" - + " \n" + "\n"; @@ -178,7 +178,8 @@ class CpdCliTest extends BaseCliTest { "-d", BASE_RES_PATH + "encodingTest/", "--ignore-identifiers", "--format", "xml", // request UTF-8 for CPD - "--encoding", "UTF-8") + "--encoding", "UTF-8", + "--debug") .verify(r -> { r.checkStdOut(startsWith("")); r.checkStdOut(containsPattern("System\\.out\\.println\\([ij] \\+ \"รค\"\\);")); @@ -197,7 +198,7 @@ class CpdCliTest extends BaseCliTest { "--format", "text", "--skip-lexical-errors") .verify(r -> { - r.checkStdErr(containsPattern("Skipping .*?BadFile\\.java\\. Reason: Lexical error in file")); + r.checkStdErr(containsPattern("Skipping file: Lexical error in file .*?BadFile\\.java")); r.checkStdOut(containsString("Found a 5 line (13 tokens) duplication")); }); } @@ -205,21 +206,21 @@ class CpdCliTest extends BaseCliTest { @Test void jsShouldFindDuplicatesWithDifferentFileExtensions() throws Exception { - runCli(VIOLATIONS_FOUND, "--minimum-tokens", "5", "--language", "js", + runCli(VIOLATIONS_FOUND, "--minimum-tokens", "5", "--language", "ecmascript", "-d", BASE_RES_PATH + "tsFiles/File1.ts", BASE_RES_PATH + "tsFiles/File2.ts") .checkStdOut(containsString("Found a 9 line (32 tokens) duplication in the following files")); } @Test void jsShouldFindNoDuplicatesWithDifferentFileExtensions() throws Exception { - runCli(OK, "--minimum-tokens", "5", "--language", "js", + runCli(OK, "--minimum-tokens", "5", "--language", "ecmascript", "-d", BASE_RES_PATH + "tsFiles/") .checkStdOut(emptyString()); } @Test void renderEmptyReportXml() throws Exception { - runCli(OK, "--minimum-tokens", "5", "--language", "js", + runCli(OK, "--minimum-tokens", "5", "--language", "ecmascript", "-f", "xml", "-d", BASE_RES_PATH + "tsFiles/") .checkStdOut(equalTo( diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java index d566175767..c86a3cb236 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java @@ -36,7 +36,6 @@ public abstract class AbstractConfiguration { private final List relativizeRoots = new ArrayList<>(); protected URI inputUri; - protected Path reportFile; private Charset sourceEncoding = Charset.forName(System.getProperty("file.encoding")); private boolean debug; private final Map langProperties = new HashMap<>(); @@ -47,8 +46,8 @@ public abstract class AbstractConfiguration { private @NonNull List inputPaths = new ArrayList<>(); private Path inputFilePath; private Path ignoreFilePath; - private List excludes; - private boolean nonRecursive; + private List excludes = new ArrayList<>(); + private boolean collectRecursive; protected AbstractConfiguration(LanguageRegistry languageRegistry, MessageReporter messageReporter) { @@ -192,7 +191,7 @@ public abstract class AbstractConfiguration { * * @param lang A language */ - protected void setOnlyRecognizeLanguage(Language lang) { + public void setOnlyRecognizeLanguage(Language lang) { this.languageVersionDiscoverer.onlyRecognizeLanguages(LanguageRegistry.singleton(lang)); } @@ -364,11 +363,12 @@ public abstract class AbstractConfiguration { this.inputPaths.add(inputPath); } - /** Returns the path to the file list text file. */ + /** Returns the path to the file list include file. */ public @Nullable Path getInputFile() { return inputFilePath; } + /** Returns the path to the file list exclude file. */ public @Nullable Path getIgnoreFile() { return ignoreFilePath; } @@ -428,59 +428,19 @@ public abstract class AbstractConfiguration { this.inputUri = inputUri == null ? null : URI.create(inputUri); } - /** - * Get the file to which the report should render. - * - * @return The file to which to render. - * @deprecated Use {@link #getReportFilePath()} - */ - @Deprecated - public String getReportFile() { - return reportFile == null ? null : reportFile.toString(); - } - - /** - * Get the file to which the report should render. - * - * @return The file to which to render. - */ - public Path getReportFilePath() { - return reportFile; - } - - /** - * Set the file to which the report should render. - * - * @param reportFile the file to set - * @deprecated Use {@link #setReportFile(Path)} - */ - @Deprecated - public void setReportFile(String reportFile) { - this.reportFile = reportFile == null ? null : Paths.get(reportFile); - } - - /** - * Set the file to which the report should render. - * - * @param reportFile the file to set - */ - public void setReportFile(Path reportFile) { - this.reportFile = reportFile; - } - public List getExcludes() { return excludes; } public void setExcludes(List excludes) { - this.excludes = excludes; + this.excludes = Objects.requireNonNull(excludes); } - public boolean isNonRecursive() { - return nonRecursive; + public boolean collectFilesRecursively() { + return collectRecursive; } - public void setNonRecursive(boolean nonRecursive) { - this.nonRecursive = nonRecursive; + public void collectFilesRecursively(boolean collectRecursive) { + this.collectRecursive = collectRecursive; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index e6015a1fb4..f74dd4ff1e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -17,10 +17,6 @@ import java.util.Objects; import java.util.stream.Collectors; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; import net.sourceforge.pmd.Report.GlobalReportBuilderListener; import net.sourceforge.pmd.benchmark.TextTimingReportRenderer; @@ -30,13 +26,12 @@ import net.sourceforge.pmd.benchmark.TimingReportRenderer; import net.sourceforge.pmd.cli.PMDCommandLineInterface; import net.sourceforge.pmd.cli.PmdParametersParseResult; import net.sourceforge.pmd.internal.LogMessages; -import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; +import net.sourceforge.pmd.internal.PmdRootLogger; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.reporting.ReportStats; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.log.MessageReporter; -import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; /** * Entry point for PMD's CLI. Use {@link #runPmd(PMDConfiguration)} @@ -52,10 +47,6 @@ import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; @Deprecated public final class PMD { - private static final String PMD_PACKAGE = "net.sourceforge.pmd"; - // not final, in order to re-initialize logging - private static Logger log = LoggerFactory.getLogger(PMD_PACKAGE); - /** * The line delimiter used by PMD in outputs. Usually the platform specific * line separator. @@ -140,8 +131,8 @@ public final class PMD { // todo these warnings/errors should be output on a PmdRenderer if (!parseResult.getDeprecatedOptionsUsed().isEmpty()) { Entry first = parseResult.getDeprecatedOptionsUsed().entrySet().iterator().next(); - log.warn("Some deprecated options were used on the command-line, including {}", first.getKey()); - log.warn("Consider replacing it with {}", first.getValue()); + PmdRootLogger.log.warn("Some deprecated options were used on the command-line, including {}", first.getKey()); + PmdRootLogger.log.warn("Consider replacing it with {}", first.getValue()); } if (parseResult.isVersion()) { @@ -157,41 +148,19 @@ public final class PMD { return StatusCode.ERROR; } - PMDConfiguration configuration = null; + PMDConfiguration configuration; try { configuration = Objects.requireNonNull( - parseResult.toConfiguration() + parseResult.toConfiguration() ); } catch (IllegalArgumentException e) { System.err.println("Cannot start analysis: " + e); - log.debug(ExceptionUtils.getStackTrace(e)); + PmdRootLogger.log.debug(ExceptionUtils.getStackTrace(e)); return StatusCode.ERROR; } - Level curLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); - boolean resetLogLevel = false; - try { - // only reconfigure logging, if debug flag was used on command line - // otherwise just use whatever is in conf/simplelogger.properties which happens automatically - if (configuration.isDebug()) { - Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.TRACE); - // need to reload the logger with the new configuration - log = LoggerFactory.getLogger(PMD_PACKAGE); - resetLogLevel = true; - } - - MessageReporter pmdReporter = setupMessageReporter(); - configuration.setReporter(pmdReporter); - - return runPmd(configuration); - } finally { - if (resetLogLevel) { - // reset to the previous value - Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(curLogLevel); - log = LoggerFactory.getLogger(PMD_PACKAGE); - } - } + return PmdRootLogger.executeInLoggingContext(configuration, PMD::runPmd); } /** @@ -220,7 +189,7 @@ public final class PMD { return StatusCode.ERROR; } try { - log.debug("Current classpath:\n{}", System.getProperty("java.class.path")); + PmdRootLogger.log.debug("Current classpath:\n{}", System.getProperty("java.class.path")); ReportStats stats = pmd.runAndReturnStats(); if (pmdReporter.numErrors() > 0) { // processing errors are ignored @@ -243,21 +212,6 @@ public final class PMD { } } - private static @NonNull MessageReporter setupMessageReporter() { - - // create a top-level reporter - // TODO CLI errors should also be reported through this - // TODO this should not use the logger as backend, otherwise without - // slf4j implementation binding, errors are entirely ignored. - MessageReporter pmdReporter = new SimpleMessageReporter(log); - // always install java.util.logging to slf4j bridge - Slf4jSimpleConfiguration.installJulBridge(); - // logging, mostly for testing purposes - Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); - log.info("Log level is at {}", defaultLogLevel); - return pmdReporter; - } - private static void finishBenchmarker(PMDConfiguration configuration) { if (configuration.isBenchmark()) { final TimingReport timingReport = TimeTracker.stopGlobalTracking(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 448ade11b3..0526337690 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd; import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -95,6 +96,7 @@ public class PMDConfiguration extends AbstractConfiguration { /** The default suppress marker string. */ public static final String DEFAULT_SUPPRESS_MARKER = "NOPMD"; + protected Path reportFile; // General behavior options private String suppressMarker = DEFAULT_SUPPRESS_MARKER; @@ -590,4 +592,43 @@ public class PMDConfiguration extends AbstractConfiguration { } + /** + * Get the file to which the report should render. + * + * @return The file to which to render. + * @deprecated Use {@link #getReportFilePath()} + */ + @Deprecated + public String getReportFile() { + return reportFile == null ? null : reportFile.toString(); + } + + /** + * Get the file to which the report should render. + * + * @return The file to which to render. + */ + public Path getReportFilePath() { + return reportFile; + } + + /** + * Set the file to which the report should render. + * + * @param reportFile the file to set + * @deprecated Use {@link #setReportFile(Path)} + */ + @Deprecated + public void setReportFile(String reportFile) { + this.reportFile = reportFile == null ? null : Paths.get(reportFile); + } + + /** + * Set the file to which the report should render. + * + * @param reportFile the file to set + */ + public void setReportFile(Path reportFile) { + this.reportFile = reportFile; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java index 7b46c036b8..1200029143 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cli/PMDParameters.java @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; -import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; @@ -57,29 +56,30 @@ public class PMDParameters { @Parameter(names = { "--dir", "-dir", "-d" }, description = "Path to a source file, or directory containing source files to analyze. " - // About the following line: - // In PMD 6, this is only the case for files found in directories. If you - // specify a file directly, and it is unknown, then the Java parser is used. - + "Note that a file is only effectively added if it matches a language known by PMD. " - + "Zip and Jar files are also supported, if they are specified directly " - + "(archive files found while exploring a directory are not recursively expanded). " - + "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. " - + "One of --dir, --file-list or --uri must be provided. ", - variableArity = true) - private List inputPaths = new ArrayList<>(); + // About the following line: + // In PMD 6, this is only the case for files found in directories. If you + // specify a file directly, and it is unknown, then the Java parser is used. + + "Note that a file is only effectively added if it matches a language known by PMD. " + + "Zip and Jar files are also supported, if they are specified directly " + + "(archive files found while exploring a directory are not recursively expanded). " + + "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. " + + "One of --dir, --file-list or --uri must be provided. ", + variableArity = true, + converter = StringToPathConverter.class) + private List inputPaths = new ArrayList<>(); @Parameter(names = { "--file-list", "-filelist" }, description = "Path to a file containing a list of files to analyze, one path per line. " - + "One of --dir, --file-list or --uri must be provided. " - ) - private String fileListPath; + + "One of --dir, --file-list or --uri must be provided. ", + converter = StringToPathConverter.class) + private Path fileListPath; @Parameter(names = { "--ignore-list", "-ignorelist" }, description = "Path to a file containing a list of files to exclude from the analysis, one path per line. " - + "This option can be combined with --dir and --file-list. " - ) - private String ignoreListPath; + + "This option can be combined with --dir and --file-list. ", + converter = StringToPathConverter.class) + private Path ignoreListPath; @Parameter(names = { "--format", "-format", "-f" }, description = "Report format type.") private String format = "text"; // Enhance to support other usage @@ -260,7 +260,7 @@ public class PMDParameters { "Please provide a parameter for source root directory (-dir or -d), database URI (-uri or -u), or file list path (-filelist)."); } PMDConfiguration configuration = new PMDConfiguration(); - configuration.setInputPaths(this.getInputPaths().stream().collect(Collectors.joining(","))); + configuration.setInputPathList(this.getInputPaths()); configuration.setInputFilePath(this.getFileListPath()); configuration.setIgnoreFilePath(this.getIgnoreListPath()); configuration.setInputUri(this.getUri()); @@ -417,7 +417,7 @@ public class PMDParameters { return rulesets; } - public List getInputPaths() { + public List getInputPaths() { return inputPaths; } @@ -426,11 +426,11 @@ public class PMDParameters { return StringUtils.join(inputPaths, ","); } - public String getFileListPath() { + public Path getFileListPath() { return fileListPath; } - public String getIgnoreListPath() { + public Path getIgnoreListPath() { return ignoreListPath; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 1f235b4a9a..6865a95f54 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -8,6 +8,7 @@ import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.nio.charset.Charset; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -20,7 +21,6 @@ import org.slf4j.LoggerFactory; import net.sourceforge.pmd.AbstractConfiguration; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; @@ -35,6 +35,7 @@ public class CPDConfiguration extends AbstractConfiguration { public static final String DEFAULT_RENDERER = "text"; private static final Map> RENDERERS = new HashMap<>(); + protected Path reportFile; static { @@ -142,10 +143,6 @@ public class CPDConfiguration extends AbstractConfiguration { return Collections.unmodifiableSet(RENDERERS.keySet()); } - public void setLanguage(Language language) { - setOnlyRecognizeLanguage(language); - } - public int getMinimumTileSize() { return minimumTileSize; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 6d053655fd..2c840fcdaf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -39,8 +39,8 @@ public final class CpdAnalysis implements AutoCloseable { private @NonNull CPDListener listener = new CPDNullListener(); - public CpdAnalysis(CPDConfiguration config) { - configuration = config; + private CpdAnalysis(CPDConfiguration config) { + this.configuration = config; this.reporter = config.getReporter(); this.files = FileCollector.newCollector( config.getLanguageVersionDiscoverer(), @@ -56,6 +56,18 @@ public final class CpdAnalysis implements AutoCloseable { } } + /** + * Create a new instance from the given configuration. The configuration + * should not be modified after this. + * + * @param config Configuration + * + * @return A new analysis instance + */ + public static CpdAnalysis create(CPDConfiguration config) { + return new CpdAnalysis(config); + } + private static void setPropertyIfMissing(PropertyDescriptor prop, LanguagePropertyBundle sink, T value) { if (sink.hasDescriptor(prop) && !sink.isPropertyOverridden(prop)) { sink.setProperty(prop, value); @@ -111,6 +123,7 @@ public final class CpdAnalysis implements AutoCloseable { Map numberOfTokensPerFile = new HashMap<>(); + boolean hasErrors = false; Tokens tokens = new Tokens(); for (TextFile textFile : sourceManager.getTextFiles()) { TextDocument textDocument = sourceManager.get(textFile); @@ -123,16 +136,21 @@ public final class CpdAnalysis implements AutoCloseable { if (e instanceof TokenMgrError) { // NOPMD ((TokenMgrError) e).setFileName(textFile.getDisplayName()); } - reporter.errorEx("Error while lexing.", e); - // already reported + String message = configuration.isSkipLexicalErrors() ? "Skipping file" : "Error while tokenizing"; + reporter.errorEx(message, e); + hasErrors = true; savedState.restore(tokens); } } - + if (hasErrors && !configuration.isSkipLexicalErrors()) { + // will be caught by CPD command + throw new IllegalStateException("Errors were detected while lexing source, exiting because --skip-lexical-errors is unset."); + } LOGGER.debug("Running match algorithm on {} files...", sourceManager.size()); MatchAlgorithm matchAlgorithm = new MatchAlgorithm(tokens, configuration.getMinimumTileSize()); List matches = matchAlgorithm.findMatches(listener, sourceManager); + tokens = null; // NOPMD null it out before rendering LOGGER.debug("Finished: {} duplicates found", matches.size()); CPDReport cpdReport = new CPDReport(sourceManager, matches, numberOfTokensPerFile); @@ -154,4 +172,5 @@ public final class CpdAnalysis implements AutoCloseable { public void close() throws IOException { // nothing for now } + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 6fcdf61fd3..31184eb545 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -588,7 +588,7 @@ public class GUI implements CPDListener { if (sourceIDs.size() == 1) { String sourceId = sourceIDs.iterator().next(); int separatorPos = sourceId.lastIndexOf(File.separatorChar); - return "..." + sourceId.substring(separatorPos); + return "..." + sourceId.substring(separatorPos); } else { return String.format("(%d separate files)", sourceIDs.size()); } @@ -628,9 +628,9 @@ public class GUI implements CPDListener { LanguageConfig conf = languageConfigFor((String) languageBox.getSelectedItem()); Language language = conf.getLanguage(); - config.setLanguage(language); + config.setOnlyRecognizeLanguage(language); - try (CpdAnalysis cpd = new CpdAnalysis(config)) { + try (CpdAnalysis cpd = CpdAnalysis.create(config)) { cpd.setCpdListener(this); tokenizingFilesBar.setMinimum(0); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java index b25e8a03fc..d7a3f6c34a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/MatchAlgorithm.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -42,21 +41,24 @@ class MatchAlgorithm { } public List findMatches(@NonNull CPDListener cpdListener, SourceManager sourceManager) { - cpdListener.phaseUpdate(CPDListener.HASH); - Map markGroups = hash(); - - cpdListener.phaseUpdate(CPDListener.MATCH); MatchCollector matchCollector = new MatchCollector(this); - for (Iterator i = markGroups.values().iterator(); i.hasNext(); ) { - Object o = i.next(); - if (o instanceof List) { - @SuppressWarnings("unchecked") - List l = (List) o; - Collections.reverse(l); - matchCollector.collect(l); - } - i.remove(); + { + cpdListener.phaseUpdate(CPDListener.HASH); + Map markGroups = hash(); + + cpdListener.phaseUpdate(CPDListener.MATCH); + markGroups.values() + .stream() + .filter(it -> it instanceof List) + .forEach(it -> { + @SuppressWarnings("unchecked") + List l = (List) it; + Collections.reverse(l); + matchCollector.collect(l); + }); + // put markGroups out of scope } + cpdListener.phaseUpdate(CPDListener.GROUPING); List matches = matchCollector.getMatches(); matches.sort(Comparator.naturalOrder()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index c829799ea2..3793ba7fb5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -63,7 +63,7 @@ class SourceManager implements AutoCloseable { @SuppressWarnings("PMD.CloseResource") public Chars getSlice(Mark mark) { TextFile textFile = fileByPathId.get(mark.getToken().getFilePathId()); - assert textFile != null: "No such file " + mark.getToken().getFilePathId(); + assert textFile != null : "No such file " + mark.getToken().getFilePathId(); TextDocument doc = get(textFile); assert doc != null; FileLocation loc = mark.getLocation(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/PmdRootLogger.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/PmdRootLogger.java new file mode 100644 index 0000000000..2ca36f6853 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/PmdRootLogger.java @@ -0,0 +1,70 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal; + +import java.util.function.Function; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +import net.sourceforge.pmd.AbstractConfiguration; +import net.sourceforge.pmd.util.log.MessageReporter; +import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; + +/** + * @author Clรฉment Fournier + */ +public final class PmdRootLogger { + + private static final String PMD_PACKAGE = "net.sourceforge.pmd"; + // not final, in order to re-initialize logging + public static Logger log = LoggerFactory.getLogger(PMD_PACKAGE); + + private PmdRootLogger() { + // utility class + } + + public static R executeInLoggingContext(C conf, Function runnable) { + Level curLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); + boolean resetLogLevel = false; + try { + // only reconfigure logging, if debug flag was used on command line + // otherwise just use whatever is in conf/simplelogger.properties which happens automatically + if (conf.isDebug()) { + Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.TRACE); + // need to reload the logger with the new configuration + log = LoggerFactory.getLogger(PMD_PACKAGE); + resetLogLevel = true; + } + + MessageReporter pmdReporter = setupMessageReporter(); + conf.setReporter(pmdReporter); + return runnable.apply(conf); + } finally { + if (resetLogLevel) { + // reset to the previous value + Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(curLogLevel); + log = LoggerFactory.getLogger(PMD_PACKAGE); + } + } + } + + private static @NonNull MessageReporter setupMessageReporter() { + + // create a top-level reporter + // TODO CLI errors should also be reported through this + // TODO this should not use the logger as backend, otherwise without + // slf4j implementation binding, errors are entirely ignored. + MessageReporter pmdReporter = new SimpleMessageReporter(log); + // always install java.util.logging to slf4j bridge + Slf4jSimpleConfiguration.installJulBridge(); + // logging, mostly for testing purposes + Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); + log.info("Log level is at {}", defaultLogLevel); + return pmdReporter; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java index d955e11b7d..21c6129255 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/FileCollectionUtil.java @@ -37,6 +37,7 @@ public final class FileCollectionUtil { public static void collectFiles(AbstractConfiguration configuration, FileCollector collector) { if (configuration.getSourceEncoding() != null) { collector.setCharset(configuration.getSourceEncoding()); + collector.setRecursive(configuration.collectFilesRecursively()); } @@ -51,14 +52,18 @@ public final class FileCollectionUtil { collectFileList(collector, configuration.getInputFile()); } - if (configuration.getIgnoreFile() != null) { + if (configuration.getIgnoreFile() != null || !configuration.getExcludes().isEmpty()) { // This is to be able to interpret the log (will report 'adding' xxx) LOG.debug("Now collecting files to exclude."); // errors like "excluded file does not exist" are reported as warnings. - // todo better reporting of *where* exactly the path is MessageReporter mutedLog = new ErrorsAsWarningsReporter(collector.getReporter()); try (FileCollector excludeCollector = collector.newCollector(mutedLog)) { - collectFileList(excludeCollector, configuration.getIgnoreFile()); + + if (configuration.getIgnoreFile() != null) { + // todo better reporting of *where* exactly the path is + collectFileList(excludeCollector, configuration.getIgnoreFile()); + } + collectFiles(excludeCollector, configuration.getExcludes()); collector.exclude(excludeCollector); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java index 349df40646..f3b303eeac 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileCollector.java @@ -64,6 +64,7 @@ public final class FileCollector implements AutoCloseable { private final String outerFsDisplayName; private final List relativizeRootPaths = new ArrayList<>(); private boolean closed; + private boolean recursive = true; // construction @@ -73,6 +74,10 @@ public final class FileCollector implements AutoCloseable { this.outerFsDisplayName = outerFsDisplayName; } + public void setRecursive(boolean collectFilesRecursively) { + this.recursive = collectFilesRecursively; + } + /** * Internal API: please use {@link PmdAnalysis#files()} instead of * creating a collector yourself. @@ -337,7 +342,7 @@ public final class FileCollector implements AutoCloseable { * @return True if the directory has been added */ public boolean addDirectory(Path dir) throws IOException { - return addDirectory(dir, true); + return addDirectory(dir, recursive); } public boolean addDirectory(Path dir, boolean recurse) throws IOException { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java index 903818f7e1..b57dbd0e34 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportConfiguration.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.util.treeexport; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Properties; import org.slf4j.Logger; @@ -19,6 +20,7 @@ import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; public class TreeExportConfiguration extends AbstractConfiguration { private static final Logger LOG = LoggerFactory.getLogger(TreeExportConfiguration.class); + protected Path reportFile; private String format = "xml"; private Language language = LanguageRegistry.PMD.getLanguageById("java"); @@ -91,4 +93,44 @@ public class TreeExportConfiguration extends AbstractConfiguration { public void setMessageReporter(MessageReporter messageReporter) { this.messageReporter = messageReporter; } + + /** + * Get the file to which the report should render. + * + * @return The file to which to render. + * @deprecated Use {@link #getReportFilePath()} + */ + @Deprecated + public String getReportFile() { + return reportFile == null ? null : reportFile.toString(); + } + + /** + * Get the file to which the report should render. + * + * @return The file to which to render. + */ + public Path getReportFilePath() { + return reportFile; + } + + /** + * Set the file to which the report should render. + * + * @param reportFile the file to set + * @deprecated Use {@link #setReportFile(Path)} + */ + @Deprecated + public void setReportFile(String reportFile) { + this.reportFile = reportFile == null ? null : Paths.get(reportFile); + } + + /** + * Set the file to which the report should render. + * + * @param reportFile the file to set + */ + public void setReportFile(Path reportFile) { + this.reportFile = reportFile; + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java index c8cc392240..bb3b5ecf62 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/FileSelectorTest.java @@ -41,7 +41,7 @@ class FileSelectorTest { void testUnwantedFile() { LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(DummyLanguageModule.getInstance()); - File javaFile = new File("/path/to/myFile.txt"); + File javaFile = new File("/path/to/myFile.notdummy"); boolean selected = fileSelector.accept(javaFile.getParentFile(), javaFile.getName()); assertFalse(selected, "Not-source file must not be selected!"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java index 679e34375c..c421c0758a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java @@ -53,7 +53,7 @@ class PmdAnalysisTest { @Test void testRendererInteractions() throws IOException { PMDConfiguration config = new PMDConfiguration(); - config.setInputPaths("sample-source/dummy"); + config.addInputPath(Paths.get("sample-source/dummy")); Renderer renderer = spy(Renderer.class); try (PmdAnalysis pmd = PmdAnalysis.create(config)) { pmd.addRenderer(renderer); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java index 6a1c855246..e714d32e49 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/PMDFilelistTest.java @@ -136,8 +136,8 @@ class PMDFilelistTest { @Test void testGetApplicableFilesWithDirAndIgnores() { PMDConfiguration configuration = new PMDConfiguration(); - configuration.setInputPaths(RESOURCE_PREFIX + "src"); - configuration.setIgnoreFilePath(RESOURCE_PREFIX + "ignorelist.txt"); + configuration.addInputPath(Paths.get(RESOURCE_PREFIX + "src")); + configuration.setIgnoreFilePath(Paths.get(RESOURCE_PREFIX + "ignorelist.txt")); FileCollector collector = newCollector(); FileCollectionUtil.collectFiles(configuration, collector); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java index b57ffac5ae..0fc679b6f5 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDFilelistTest.java @@ -25,10 +25,10 @@ class CPDFilelistTest { @Test void testFilelist() throws IOException { CPDConfiguration arguments = new CPDConfiguration(); - arguments.setLanguage(DummyLanguageModule.getInstance()); - arguments.setFileListPath(Paths.get("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist.txt")); + arguments.setOnlyRecognizeLanguage(DummyLanguageModule.getInstance()); + arguments.setInputFilePath(Paths.get("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist.txt")); List paths; - try (CpdAnalysis cpd = new CpdAnalysis(arguments)) { + try (CpdAnalysis cpd = CpdAnalysis.create(arguments)) { paths = CollectionUtil.map(cpd.files().getCollectedFiles(), TextFile::getPathId); } @@ -44,10 +44,10 @@ class CPDFilelistTest { @Test void testFilelistMultipleLines() throws IOException { CPDConfiguration arguments = new CPDConfiguration(); - arguments.setLanguage(DummyLanguageModule.getInstance()); - arguments.setFileListPath(Paths.get("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist2.txt")); + arguments.setOnlyRecognizeLanguage(DummyLanguageModule.getInstance()); + arguments.setInputFilePath(Paths.get("src/test/resources/net/sourceforge/pmd/cpd/cli/filelist2.txt")); List paths; - try (CpdAnalysis cpd = new CpdAnalysis(arguments)) { + try (CpdAnalysis cpd = CpdAnalysis.create(arguments)) { paths = CollectionUtil.map(cpd.files().getCollectedFiles(), TextFile::getPathId); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java index 1d3e3d2426..19be490618 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java @@ -4,7 +4,6 @@ package net.sourceforge.pmd.cpd; -import static net.sourceforge.pmd.util.CollectionUtil.setOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -18,8 +17,7 @@ import org.apache.commons.lang3.SystemUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.PlainTextLanguage; +import net.sourceforge.pmd.lang.DummyLanguageModule; /** * Unit test for {@link CpdAnalysis} @@ -33,11 +31,11 @@ class CpdAnalysisTest { // Symlinks are not well supported under Windows - so the tests are // simply executed only on linux. private boolean canTestSymLinks = SystemUtils.IS_OS_UNIX; - CPDConfiguration config = new CPDConfiguration(new LanguageRegistry(setOf(PlainTextLanguage.getInstance()))); + CPDConfiguration config = new CPDConfiguration(); @BeforeEach void setup() throws Exception { - config.setLanguage(PlainTextLanguage.getInstance()); + config.setOnlyRecognizeLanguage(DummyLanguageModule.getInstance()); config.setMinimumTileSize(10); } @@ -74,7 +72,7 @@ class CpdAnalysisTest { prepareSymLinks(); NoFileAssertListener listener = new NoFileAssertListener(0); - try (CpdAnalysis cpd = new CpdAnalysis(config)) { + try (CpdAnalysis cpd = CpdAnalysis.create(config)) { cpd.setCpdListener(listener); cpd.files().addFile(Paths.get(BASE_TEST_RESOURCE_PATH, "this-is-a-broken-sym-link-for-test")); cpd.performAnalysis(); @@ -95,7 +93,7 @@ class CpdAnalysisTest { prepareSymLinks(); NoFileAssertListener listener = new NoFileAssertListener(1); - try (CpdAnalysis cpd = new CpdAnalysis(config)) { + try (CpdAnalysis cpd = CpdAnalysis.create(config)) { cpd.setCpdListener(listener); cpd.files().addFile(Paths.get(BASE_TEST_RESOURCE_PATH, "real-file.txt")); cpd.files().addFile(Paths.get(BASE_TEST_RESOURCE_PATH, "symlink-for-real-file.txt")); @@ -115,7 +113,7 @@ class CpdAnalysisTest { @Test void testFileAddedWithRelativePath() throws Exception { NoFileAssertListener listener = new NoFileAssertListener(1); - try (CpdAnalysis cpd = new CpdAnalysis(config)) { + try (CpdAnalysis cpd = CpdAnalysis.create(config)) { cpd.setCpdListener(listener); cpd.files().addFile(Paths.get("./" + BASE_TEST_RESOURCE_PATH, "real-file.txt")); cpd.performAnalysis(); @@ -131,7 +129,7 @@ class CpdAnalysisTest { */ @Test void testFileOrderRelevance() throws Exception { - try (CpdAnalysis cpd = new CpdAnalysis(config)) { + try (CpdAnalysis cpd = CpdAnalysis.create(config)) { cpd.files().addFile(Paths.get("./" + BASE_TEST_RESOURCE_PATH, "dup2.java")); cpd.files().addFile(Paths.get("./" + BASE_TEST_RESOURCE_PATH, "dup1.java")); cpd.performAnalysis(report -> { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index 749124c65a..0ed92a9b9a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -30,7 +30,7 @@ public class DummyLanguageModule extends SimpleLanguageModuleBase implements Cpd private static final String PARSER_THROWS = "parserThrows"; public DummyLanguageModule() { - super(LanguageMetadata.withId(TERSE_NAME).name(NAME).extensions("dummy") + super(LanguageMetadata.withId(TERSE_NAME).name(NAME).extensions("dummy", "txt") .addVersion("1.0") .addVersion("1.1") .addVersion("1.2") From 11e2a97c5f7e9f0640762ebe654d802bc92c6745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 20 Feb 2023 14:09:07 +0100 Subject: [PATCH 125/347] Introduce ts language module --- .../net/sourceforge/pmd/cli/CpdCliTest.java | 2 +- .../pmd/lang/ecmascript/TsLanguageModule.java | 25 +++++++++++++++++++ .../net.sourceforge.pmd.lang.Language | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java index efb3a93594..cdb4a32aa3 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java @@ -206,7 +206,7 @@ class CpdCliTest extends BaseCliTest { @Test void jsShouldFindDuplicatesWithDifferentFileExtensions() throws Exception { - runCli(VIOLATIONS_FOUND, "--minimum-tokens", "5", "--language", "ecmascript", + runCli(VIOLATIONS_FOUND, "--minimum-tokens", "5", "--language", "ts", "-d", BASE_RES_PATH + "tsFiles/File1.ts", BASE_RES_PATH + "tsFiles/File2.ts") .checkStdOut(containsString("Found a 9 line (32 tokens) duplication in the following files")); } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java new file mode 100644 index 0000000000..ff06b2c241 --- /dev/null +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java @@ -0,0 +1,25 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ecmascript; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.ecmascript.cpd.EcmascriptTokenizer; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; + +/** + * CPD only language to recognize TypeScript files. + */ +public class TsLanguageModule extends CpdOnlyLanguageModuleBase { + + public TsLanguageModule() { + super(LanguageMetadata.withId("ts").name("TypeScript").extensions("ts")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new EcmascriptTokenizer(); + } +} diff --git a/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language index 671c306c41..6e09a06884 100644 --- a/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language +++ b/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -1 +1,2 @@ net.sourceforge.pmd.lang.ecmascript.EcmascriptLanguageModule +net.sourceforge.pmd.lang.ecmascript.TsLanguageModule From 60f28c5c35da2da707117e644bbd1a24ea31479b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 20 Feb 2023 14:29:05 +0100 Subject: [PATCH 126/347] Fix cli tests --- .../AbstractAnalysisPmdSubcommand.java | 33 ++++++++--- .../pmd/cli/commands/internal/CpdCommand.java | 15 ++--- .../pmd/cli/commands/internal/PmdCommand.java | 14 ++--- .../pmd/cli}/internal/PmdRootLogger.java | 15 +++-- .../net/sourceforge/pmd/cli/CpdCliTest.java | 4 +- .../net/sourceforge/pmd/cli/PmdCliTest.java | 14 ++--- .../pmd/AbstractConfiguration.java | 2 +- .../main/java/net/sourceforge/pmd/PMD.java | 58 +++++++++++++++++-- 8 files changed, 107 insertions(+), 48 deletions(-) rename {pmd-core/src/main/java/net/sourceforge/pmd => pmd-cli/src/main/java/net/sourceforge/pmd/cli}/internal/PmdRootLogger.java (81%) diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java index 4f1cf5c263..7701dedc45 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/AbstractAnalysisPmdSubcommand.java @@ -9,24 +9,27 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import net.sourceforge.pmd.AbstractConfiguration; import net.sourceforge.pmd.cli.commands.mixins.internal.EncodingMixin; +import net.sourceforge.pmd.cli.internal.CliExitCode; +import net.sourceforge.pmd.cli.internal.PmdRootLogger; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; import picocli.CommandLine.ParameterException; import picocli.CommandLine.Parameters; -public abstract class AbstractAnalysisPmdSubcommand extends AbstractPmdSubcommand { +public abstract class AbstractAnalysisPmdSubcommand extends AbstractPmdSubcommand { @Mixin protected EncodingMixin encoding; - + @Option(names = { "--dir", "-d" }, description = "Path to a source file, or directory containing source files to analyze. " - + "Zip and Jar files are also supported, if they are specified directly " - + "(archive files found while exploring a directory are not recursively expanded). " - + "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. " - + "One of --dir, --file-list or --uri must be provided.", + + "Zip and Jar files are also supported, if they are specified directly " + + "(archive files found while exploring a directory are not recursively expanded). " + + "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. " + + "One of --dir, --file-list or --uri must be provided.", arity = "1..*", split = ",") protected List inputPaths; @@ -63,8 +66,22 @@ public abstract class AbstractAnalysisPmdSubcommand extends AbstractPmdSubcomman if ((inputPaths == null || inputPaths.isEmpty()) && uri == null && fileListPath == null) { throw new ParameterException(spec.commandLine(), - "Please provide a parameter for source root directory (--dir or -d), " - + "database URI (--uri or -u), or file list path (--file-list)"); + "Please provide a parameter for source root directory (--dir or -d), " + + "database URI (--uri or -u), or file list path (--file-list)"); } } + + + protected abstract C toConfiguration(); + + protected abstract CliExitCode doExecute(C conf); + + + @Override + protected CliExitCode execute() { + final C configuration = toConfiguration(); + return PmdRootLogger.executeInLoggingContext(configuration, + this::doExecute); + } + } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index 12d400d3b4..2f942712ee 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -20,7 +20,6 @@ import net.sourceforge.pmd.cpd.CPDConfiguration; import net.sourceforge.pmd.cpd.CpdAnalysis; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.internal.LogMessages; -import net.sourceforge.pmd.internal.PmdRootLogger; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.util.StringUtil; @@ -30,7 +29,7 @@ import picocli.CommandLine.ParameterException; @Command(name = "cpd", showDefaultValues = true, description = "Copy/Paste Detector - find duplicate code") -public class CpdCommand extends AbstractAnalysisPmdSubcommand { +public class CpdCommand extends AbstractAnalysisPmdSubcommand { @Option(names = { "--language", "-l" }, description = "The source code language.%nValid values: ${COMPLETION-CANDIDATES}", defaultValue = CPDConfiguration.DEFAULT_LANGUAGE, converter = CpdLanguageTypeSupport.class, completionCandidates = CpdLanguageTypeSupport.class) @@ -112,7 +111,8 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { * * @throws ParameterException if the parameters are inconsistent or incomplete */ - public CPDConfiguration toConfiguration() { + @Override + protected CPDConfiguration toConfiguration() { final CPDConfiguration configuration = new CPDConfiguration(); configuration.setDebug(debug); configuration.setExcludes(excludes); @@ -142,13 +142,8 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand { } @Override - protected CliExitCode execute() { - final CPDConfiguration configuration = toConfiguration(); - - return PmdRootLogger.executeInLoggingContext(configuration, CpdCommand::doExecute); - } - - private static @NonNull CliExitCode doExecute(CPDConfiguration configuration) { + @NonNull + protected CliExitCode doExecute(CPDConfiguration configuration) { try (CpdAnalysis cpd = CpdAnalysis.create(configuration)) { MutableBoolean hasViolations = new MutableBoolean(); diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java index bd71718bc0..68896cd46e 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdCommand.java @@ -31,7 +31,6 @@ import net.sourceforge.pmd.cli.commands.typesupport.internal.PmdLanguageVersionT import net.sourceforge.pmd.cli.internal.CliExitCode; import net.sourceforge.pmd.cli.internal.ProgressBarListener; import net.sourceforge.pmd.internal.LogMessages; -import net.sourceforge.pmd.internal.PmdRootLogger; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -48,7 +47,7 @@ import picocli.CommandLine.ParameterException; @Command(name = "check", showDefaultValues = true, description = "The PMD standard source code analyzer") -public class PmdCommand extends AbstractAnalysisPmdSubcommand { +public class PmdCommand extends AbstractAnalysisPmdSubcommand { private static final Logger LOG = LoggerFactory.getLogger(PmdCommand.class); static { @@ -275,7 +274,8 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand { * * @throws ParameterException if the parameters are inconsistent or incomplete */ - public PMDConfiguration toConfiguration() { + @Override + protected PMDConfiguration toConfiguration() { final PMDConfiguration configuration = new PMDConfiguration(); configuration.setInputPathList(inputPaths); configuration.setInputFilePath(fileListPath); @@ -322,12 +322,8 @@ public class PmdCommand extends AbstractAnalysisPmdSubcommand { } @Override - protected CliExitCode execute() { - final PMDConfiguration configuration = toConfiguration(); - return PmdRootLogger.executeInLoggingContext(configuration, this::doExecute); - } - - private @NonNull CliExitCode doExecute(PMDConfiguration configuration) { + @NonNull + protected CliExitCode doExecute(PMDConfiguration configuration) { if (benchmark) { TimeTracker.startGlobalTracking(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/PmdRootLogger.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/PmdRootLogger.java similarity index 81% rename from pmd-core/src/main/java/net/sourceforge/pmd/internal/PmdRootLogger.java rename to pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/PmdRootLogger.java index 2ca36f6853..ee4817378c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/PmdRootLogger.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/PmdRootLogger.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.internal; +package net.sourceforge.pmd.cli.internal; import java.util.function.Function; @@ -12,17 +12,22 @@ import org.slf4j.LoggerFactory; import org.slf4j.event.Level; import net.sourceforge.pmd.AbstractConfiguration; +import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; import net.sourceforge.pmd.util.log.MessageReporter; import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; /** + * Interacts with slf4j-simple to reconfigure logging levels based on + * the debug flag. + * * @author Clรฉment Fournier */ public final class PmdRootLogger { - private static final String PMD_PACKAGE = "net.sourceforge.pmd"; + private static final String PMD_CLI_LOGGER = "net.sourceforge.pmd.cli"; // not final, in order to re-initialize logging - public static Logger log = LoggerFactory.getLogger(PMD_PACKAGE); + // This logger is used as backend for the MessageReporter currently. + private static Logger log = LoggerFactory.getLogger(PMD_CLI_LOGGER); private PmdRootLogger() { // utility class @@ -37,7 +42,7 @@ public final class PmdRootLogger { if (conf.isDebug()) { Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.TRACE); // need to reload the logger with the new configuration - log = LoggerFactory.getLogger(PMD_PACKAGE); + log = LoggerFactory.getLogger(PMD_CLI_LOGGER); resetLogLevel = true; } @@ -48,7 +53,7 @@ public final class PmdRootLogger { if (resetLogLevel) { // reset to the previous value Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(curLogLevel); - log = LoggerFactory.getLogger(PMD_PACKAGE); + log = LoggerFactory.getLogger(PMD_CLI_LOGGER); } } } diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java index cdb4a32aa3..ccc7e5d3f6 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java @@ -81,13 +81,13 @@ class CpdCliTest extends BaseCliTest { @Test void debugLogging() throws Exception { CliExecutionResult result = runCliSuccessfully("--debug", "--minimum-tokens", "340", "--dir", SRC_DIR); - result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd - Log level is at TRACE")); + result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at TRACE")); } @Test void defaultLogging() throws Exception { CliExecutionResult result = runCliSuccessfully("--minimum-tokens", "340", "--dir", SRC_DIR); - result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd - Log level is at INFO")); + result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at INFO")); } @Test diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java index 56d712ec39..ffe6fa078f 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java @@ -187,7 +187,7 @@ class PmdCliTest extends BaseCliTest { SystemLambda.restoreSystemProperties(() -> { // change working directory System.setProperty("user.dir", srcDir.toString()); - runCliSuccessfully("--dir", ".", "--rulesets", DUMMY_RULESET_WITH_VIOLATIONS) + runCli(VIOLATIONS_FOUND, "--dir", ".", "--rulesets", DUMMY_RULESET_WITH_VIOLATIONS) .verify(res -> res.checkStdOut(containsString("./src/test/resources/net/sourceforge/pmd/cli/src/anotherfile.dummy"))); }); @@ -197,13 +197,13 @@ class PmdCliTest extends BaseCliTest { @Test void debugLogging() throws Exception { CliExecutionResult result = runCliSuccessfully("--debug", "--dir", srcDir.toString(), "--rulesets", RULESET_NO_VIOLATIONS); - result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand - Log level is at TRACE")); + result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at TRACE")); } @Test void defaultLogging() throws Exception { CliExecutionResult result = runCliSuccessfully("--dir", srcDir.toString(), "--rulesets", RULESET_NO_VIOLATIONS); - result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand - Log level is at INFO")); + result.checkStdErr(containsString("[main] INFO net.sourceforge.pmd.cli - Log level is at INFO")); result.checkStdErr(not(containsPattern("Adding file .*"))); // not in debug mode } @@ -382,16 +382,16 @@ class PmdCliTest extends BaseCliTest { // therefore we use the current directory and make sure, we are at the correct place - in pmd-core Path cwd = Paths.get(".").toRealPath(); assertThat(cwd.toString(), endsWith("pmd-cli")); - String relativeSrcDir = IOUtil.normalizePath("src/test/resources/net/sourceforge/pmd/cli/src"); + String relativeSrcDir = "src/test/resources/net/sourceforge/pmd/cli/src"; assertTrue(Files.isDirectory(cwd.resolve(relativeSrcDir))); // use the parent directory - String relativeSrcDirWithParent = relativeSrcDir + File.separator + ".."; + Path relativeSrcDirWithParent = Paths.get(relativeSrcDir, ".."); - runCli(CliExitCode.VIOLATIONS_FOUND, "--dir", relativeSrcDirWithParent, "--rulesets", + runCli(CliExitCode.VIOLATIONS_FOUND, "--dir", relativeSrcDirWithParent.toString(), "--rulesets", DUMMY_RULESET_WITH_VIOLATIONS) .verify(result -> result.checkStdOut( - containsString("\n" + relativeSrcDirWithParent + IOUtil.normalizePath("/src/somefile.dummy")))); + containsString("\n" + relativeSrcDirWithParent + "/src/somefile.dummy"))); } @Test diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java index c86a3cb236..ecc5c0ab59 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/AbstractConfiguration.java @@ -47,7 +47,7 @@ public abstract class AbstractConfiguration { private Path inputFilePath; private Path ignoreFilePath; private List excludes = new ArrayList<>(); - private boolean collectRecursive; + private boolean collectRecursive = true; protected AbstractConfiguration(LanguageRegistry languageRegistry, MessageReporter messageReporter) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index f74dd4ff1e..221e69e217 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -17,6 +17,10 @@ import java.util.Objects; import java.util.stream.Collectors; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; import net.sourceforge.pmd.Report.GlobalReportBuilderListener; import net.sourceforge.pmd.benchmark.TextTimingReportRenderer; @@ -26,12 +30,13 @@ import net.sourceforge.pmd.benchmark.TimingReportRenderer; import net.sourceforge.pmd.cli.PMDCommandLineInterface; import net.sourceforge.pmd.cli.PmdParametersParseResult; import net.sourceforge.pmd.internal.LogMessages; -import net.sourceforge.pmd.internal.PmdRootLogger; +import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.reporting.ReportStats; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.log.MessageReporter; +import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; /** * Entry point for PMD's CLI. Use {@link #runPmd(PMDConfiguration)} @@ -47,6 +52,10 @@ import net.sourceforge.pmd.util.log.MessageReporter; @Deprecated public final class PMD { + private static final String PMD_PACKAGE = "net.sourceforge.pmd"; + // not final, in order to re-initialize logging + private static Logger log = LoggerFactory.getLogger(PMD_PACKAGE); + /** * The line delimiter used by PMD in outputs. Usually the platform specific * line separator. @@ -131,8 +140,8 @@ public final class PMD { // todo these warnings/errors should be output on a PmdRenderer if (!parseResult.getDeprecatedOptionsUsed().isEmpty()) { Entry first = parseResult.getDeprecatedOptionsUsed().entrySet().iterator().next(); - PmdRootLogger.log.warn("Some deprecated options were used on the command-line, including {}", first.getKey()); - PmdRootLogger.log.warn("Consider replacing it with {}", first.getValue()); + log.warn("Some deprecated options were used on the command-line, including {}", first.getKey()); + log.warn("Consider replacing it with {}", first.getValue()); } if (parseResult.isVersion()) { @@ -155,12 +164,34 @@ public final class PMD { ); } catch (IllegalArgumentException e) { System.err.println("Cannot start analysis: " + e); - PmdRootLogger.log.debug(ExceptionUtils.getStackTrace(e)); + log.debug(ExceptionUtils.getStackTrace(e)); return StatusCode.ERROR; } - return PmdRootLogger.executeInLoggingContext(configuration, PMD::runPmd); + Level curLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); + boolean resetLogLevel = false; + try { + // only reconfigure logging, if debug flag was used on command line + // otherwise just use whatever is in conf/simplelogger.properties which happens automatically + if (configuration.isDebug()) { + Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(Level.TRACE); + // need to reload the logger with the new configuration + log = LoggerFactory.getLogger(PMD_PACKAGE); + resetLogLevel = true; + } + + MessageReporter pmdReporter = setupMessageReporter(); + configuration.setReporter(pmdReporter); + + return runPmd(configuration); + } finally { + if (resetLogLevel) { + // reset to the previous value + Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(curLogLevel); + log = LoggerFactory.getLogger(PMD_PACKAGE); + } + } } /** @@ -189,7 +220,7 @@ public final class PMD { return StatusCode.ERROR; } try { - PmdRootLogger.log.debug("Current classpath:\n{}", System.getProperty("java.class.path")); + log.debug("Current classpath:\n{}", System.getProperty("java.class.path")); ReportStats stats = pmd.runAndReturnStats(); if (pmdReporter.numErrors() > 0) { // processing errors are ignored @@ -212,6 +243,21 @@ public final class PMD { } } + private static @NonNull MessageReporter setupMessageReporter() { + + // create a top-level reporter + // TODO CLI errors should also be reported through this + // TODO this should not use the logger as backend, otherwise without + // slf4j implementation binding, errors are entirely ignored. + MessageReporter pmdReporter = new SimpleMessageReporter(log); + // always install java.util.logging to slf4j bridge + Slf4jSimpleConfiguration.installJulBridge(); + // logging, mostly for testing purposes + Level defaultLogLevel = Slf4jSimpleConfiguration.getDefaultLogLevel(); + log.info("Log level is at {}", defaultLogLevel); + return pmdReporter; + } + private static void finishBenchmarker(PMDConfiguration configuration) { if (configuration.isBenchmark()) { final TimingReport timingReport = TimeTracker.stopGlobalTracking(); From 40aa9de6e124cd32c068a9418cebd4ab22499013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 20 Feb 2023 14:55:21 +0100 Subject: [PATCH 127/347] Checkstyle --- .../net/sourceforge/pmd/cli/commands/internal/CpdCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index 2f942712ee..c808e88ee1 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -87,6 +87,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand private List relativizeRootPaths; + @Option(names = { "--relativize-paths-with", "-z"}, description = "Path relative to which directories are rendered in the report. " + "This option allows shortening directories in the report; " + "without it, paths are rendered as mentioned in the source directory (option \"--dir\"). " @@ -142,8 +143,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand } @Override - @NonNull - protected CliExitCode doExecute(CPDConfiguration configuration) { + protected @NonNull CliExitCode doExecute(CPDConfiguration configuration) { try (CpdAnalysis cpd = CpdAnalysis.create(configuration)) { MutableBoolean hasViolations = new MutableBoolean(); From 6eb50863b81b895be775399db0e115a4f92fe7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 20 Feb 2023 15:03:46 +0100 Subject: [PATCH 128/347] Fix last tests --- .../src/main/java/net/sourceforge/pmd/PMDVersion.java | 2 +- .../java/net/sourceforge/pmd/it/BinaryDistributionIT.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java index 96565eca93..cb99738b12 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java @@ -29,7 +29,7 @@ public final class PMDVersion { * Determines the version from maven's generated pom.properties file. */ static { - String pmdVersion = UNKNOWN_VERSION; + String pmdVersion = "7.0.0-SNAPSHOT"; try (InputStream stream = PMDVersion.class.getResourceAsStream("/META-INF/maven/net.sourceforge.pmd/pmd-core/pom.properties")) { if (stream != null) { final Properties properties = new Properties(); diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index cf6304f58a..21e14440b8 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -28,8 +28,8 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest { SUPPORTED_LANGUAGES_CPD = "Valid values: apex, cpp, cs, dart, ecmascript," + System.lineSeparator() + " fortran, gherkin, go, groovy, html, java, jsp," + System.lineSeparator() + " kotlin, lua, matlab, modelica, objectivec, perl," + System.lineSeparator() - + " php, plsql, python, ruby, scala, swift, tsql," + System.lineSeparator() - + " vf, xml"; + + " php, plsql, pom, python, ruby, scala, swift, ts," + System.lineSeparator() + + " tsql, vf, vm, wsdl, xml, xsl"; SUPPORTED_LANGUAGES_PMD = "Valid values: apex-54, ecmascript-ES6, html-," + System.lineSeparator() + " java-1.10, java-1.3, java-1.4, java-1.5, java-1." + System.lineSeparator() + " 6, java-1.7, java-1.8, java-1.9, java-10," + System.lineSeparator() @@ -136,14 +136,14 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest { result = PMDExecutor.runPMD(tempDir, "-d", srcDir, "-R", "src/test/resources/rulesets/sample-ruleset.xml", "-r", createTemporaryReportFile().toString()); result.assertExecutionResult(4); - result.assertErrorOutputContains("[main] INFO net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand - Log level is at INFO"); + result.assertErrorOutputContains("[main] INFO net.sourceforge.pmd.cli - Log level is at INFO"); // now with debug result = PMDExecutor.runPMD(tempDir, "-d", srcDir, "-R", "src/test/resources/rulesets/sample-ruleset.xml", "-r", createTemporaryReportFile().toString(), "--debug"); result.assertExecutionResult(4); - result.assertErrorOutputContains("[main] INFO net.sourceforge.pmd.cli.commands.internal.AbstractPmdSubcommand - Log level is at TRACE"); + result.assertErrorOutputContains("[main] INFO net.sourceforge.pmd.cli - Log level is at TRACE"); } @Test From c44ce2633f80654897e897574e9c8b81029651b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 4 Mar 2023 15:27:49 +0100 Subject: [PATCH 129/347] Revert forgotten thing --- pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java index cb99738b12..87d1855511 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDVersion.java @@ -25,11 +25,11 @@ public final class PMDVersion { private static final String UNKNOWN_VERSION = "unknown"; - /** + /* * Determines the version from maven's generated pom.properties file. */ static { - String pmdVersion = "7.0.0-SNAPSHOT"; + String pmdVersion = UNKNOWN_VERSION; try (InputStream stream = PMDVersion.class.getResourceAsStream("/META-INF/maven/net.sourceforge.pmd/pmd-core/pom.properties")) { if (stream != null) { final Properties properties = new Properties(); From 255fdf050b2621f98dd950e57714ec717ec9d69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 4 Mar 2023 15:38:40 +0100 Subject: [PATCH 130/347] Fix compil --- .../java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java | 3 +-- .../lang/java/rule/xpath/internal/BaseJavaXPathFunction.java | 2 +- .../net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java | 3 +-- .../java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java index a5e9bcdf98..f6c048e5ff 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageModule.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.apex; import net.sourceforge.pmd.cpd.CpdCapableLanguage; import net.sourceforge.pmd.cpd.PmdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguagePropertyBundle; @@ -43,7 +42,7 @@ public class ApexLanguageModule extends LanguageModuleBase implements PmdCapable return new ApexTokenizer((ApexLanguageProperties) bundle); } - public static Language getInstance() { + public static ApexLanguageModule getInstance() { return INSTANCE; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseJavaXPathFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseJavaXPathFunction.java index 447a214bcf..450a33013e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseJavaXPathFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseJavaXPathFunction.java @@ -10,6 +10,6 @@ import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef; abstract class BaseJavaXPathFunction extends AbstractXPathFunctionDef { protected BaseJavaXPathFunction(String localName) { - super(localName, JavaLanguageModule.getInstance().getId()); + super(localName, JavaLanguageModule.getInstance()); } } diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java index e4bf4d3ca9..db35eca33b 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.plsql; import net.sourceforge.pmd.cpd.PLSQLTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; @@ -56,7 +55,7 @@ public class PLSQLLanguageModule extends SimpleLanguageModuleBase { return new PLSQLTokenizer(bundle); } - public static Language getInstance() { + public static PLSQLLanguageModule getInstance() { return INSTANCE; } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java index 825605b43a..506b47028b 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfLanguageModule.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.vf; import net.sourceforge.pmd.cpd.CpdCapableLanguage; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; @@ -39,7 +38,7 @@ public class VfLanguageModule extends SimpleLanguageModuleBase implements CpdCap return new VfLanguageProperties(); } - public static Language getInstance() { + public static VfLanguageModule getInstance() { return INSTANCE; } } From 0f17cc83f6612f746aebb12d5fb186a3e4587ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 11 Mar 2023 17:17:43 +0100 Subject: [PATCH 131/347] Add back default version for CPD languages --- .../pmd/lang/LanguageModuleBase.java | 17 ++++++++++++++++- .../pmd/lang/dart/DartLanguageModule.java | 6 ++---- .../pmd/lang/xml/XmlParsingHelper.java | 3 --- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java index fd997eccd0..c6c98922b7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang; +import static net.sourceforge.pmd.util.CollectionUtil.emptyList; import static net.sourceforge.pmd.util.CollectionUtil.setOf; import java.util.ArrayList; @@ -22,6 +23,8 @@ import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.cpd.PmdCapableLanguage; +import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata.LangVersionMetadata; import net.sourceforge.pmd.util.AssertionUtil; import net.sourceforge.pmd.util.StringUtil; @@ -55,7 +58,13 @@ public abstract class LanguageModuleBase implements Language { LanguageVersion defaultVersion = null; if (metadata.versionMetadata.isEmpty()) { - throw new IllegalStateException("No versions for '" + getId() + "'"); + if (this instanceof PmdCapableLanguage) { + // pmd languages need to have versions + throw new IllegalStateException("No versions for '" + getId() + "'"); + } else { + // for others, a version is declared implicitly + metadata.versionMetadata.add(new LangVersionMetadata()); + } } int i = 0; @@ -374,6 +383,12 @@ public abstract class LanguageModuleBase implements Language { final List aliases; final boolean isDefault; + private LangVersionMetadata() { + this.name = ""; + this.aliases = emptyList(); + this.isDefault = true; + } + private LangVersionMetadata(String name, List aliases, boolean isDefault) { checkVersionName(name); for (String alias : aliases) { diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java index b137f4dece..baffacee31 100644 --- a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java +++ b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/DartLanguageModule.java @@ -14,11 +14,9 @@ import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; */ public class DartLanguageModule extends CpdOnlyLanguageModuleBase { - /** - * Creates a new Dart Language instance. - */ public DartLanguageModule() { - super(LanguageMetadata.withId("dart").name("Dart").extensions("dart")); + super(LanguageMetadata.withId("dart").name("Dart").extensions("dart") + .addDefaultVersion("2")); } @Override diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java index 4595fb7f50..50e75ef852 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParsingHelper.java @@ -4,10 +4,7 @@ package net.sourceforge.pmd.lang.xml; -import java.sql.ParameterMetaData; - import net.sourceforge.pmd.cpd.PmdCapableLanguage; -import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; import net.sourceforge.pmd.lang.xml.ast.internal.XmlParserImpl.RootXmlNode; From d6de5ca52b8c80e5c3f1a0634a94f99efec3aee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 14 Mar 2023 20:49:43 +0100 Subject: [PATCH 132/347] Fix VF module --- .../main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java index c6c98922b7..7eec58993d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageModuleBase.java @@ -349,7 +349,7 @@ public abstract class LanguageModuleBase implements Language { */ public LanguageMetadata addAllVersionsOf(Language language) { for (LanguageVersion version : language.getVersions()) { - versionMetadata.add(new LangVersionMetadata(version.getName(), + versionMetadata.add(new LangVersionMetadata(version.getVersion(), version.getAliases(), version.equals(language.getDefaultVersion()))); } From fae08a8e08ccae08f5c9a9a8f760761db5b3def0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 14 Mar 2023 20:50:44 +0100 Subject: [PATCH 133/347] delete leftover file --- .../resources/META-INF/services/net.sourceforge.pmd.cpd.Language | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 pmd-core/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language diff --git a/pmd-core/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-core/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index e69de29bb2..0000000000 From 590c46bac8b2a7106d1424a5538287559ac1e4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 17 Mar 2023 14:30:01 +0100 Subject: [PATCH 134/347] Fix reported CPD languages test --- .../java/net/sourceforge/pmd/it/BinaryDistributionIT.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index d6071272e4..b538a07373 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -31,8 +31,8 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest { "apex", "cpp", "cs", "dart", "ecmascript", "fortran", "gherkin", "go", "groovy", "html", "java", "jsp", "kotlin", "lua", "matlab", "modelica", "objectivec", "perl", - "php", "plsql", "python", "ruby", "scala", "swift", "tsql", - "vf", "xml" + "php", "plsql", "pom", "python", "ruby", "scala", "swift", "ts", + "tsql", "vf", "vm", "wsdl", "xml", "xsl" ); private static final List SUPPORTED_LANGUAGES_PMD = listOf( From f2dc3805aff35577b924182aea58a04f5afd4134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 20 Mar 2023 15:18:12 +0100 Subject: [PATCH 135/347] Cleanups --- .../net/sourceforge/pmd/lang/apex/ast/ApexParser.java | 2 +- .../sourceforge/pmd/lang/apex/ast/CompilerService.java | 2 +- .../net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java | 3 +-- .../main/java/net/sourceforge/pmd/cpd/TokenFactory.java | 3 +++ .../src/main/java/net/sourceforge/pmd/cpd/Tokens.java | 6 ++++++ .../main/java/net/sourceforge/pmd/lang/ast/Parser.java | 9 +++------ .../java/net/sourceforge/pmd/lang/document/Chars.java | 8 +++++++- .../sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java | 9 +-------- .../net/sourceforge/pmd/lang/html/ast/HtmlParser.java | 2 +- .../java/net/sourceforge/pmd/cpd/ScalaTokenizer.java | 4 ++-- .../net/sourceforge/pmd/lang/scala/ast/ScalaParser.java | 2 +- .../pmd/lang/vf/ast/VfExpressionTypeVisitor.java | 2 +- 12 files changed, 28 insertions(+), 24 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java index 6d95b1858d..ad746e7ec1 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java @@ -33,7 +33,7 @@ public final class ApexParser implements Parser { final ApexTreeBuilder treeBuilder = new ApexTreeBuilder(task, (ApexLanguageProcessor) task.getLanguageProcessor()); return treeBuilder.buildTree(astRoot); } catch (apex.jorje.services.exception.ParseException e) { - FileLocation loc = FileLocation.caret(task.getTextDocument().getFileId(), e.getLoc().getLine(), e.getLoc().getColumn()); + FileLocation loc = FileLocation.caret(task.getFileId(), e.getLoc().getLine(), e.getLoc().getColumn()); throw new ParseException(e).withLocation(loc); } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java index 9270659fce..dec9d10b1d 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java @@ -63,7 +63,7 @@ class CompilerService { public Compilation parseApex(TextDocument document) { SourceFile sourceFile = SourceFile.builder() .setBody(document.getText().toString()) - .setKnownName(document.getFileId().toUriString()) + .setKnownName(document.getFileId().toAbsolutePath()) .build(); ApexCompiler compiler = ApexCompiler.builder().setInput(createCompilationInput(Collections.singletonList(sourceFile))).build(); compiler.compile(CompilerStage.POST_TYPE_RESOLVE); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java index c3afdf91ae..bd9a3a1288 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/cpd/ApexTokenizer.java @@ -16,7 +16,6 @@ import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.apex.ApexJorjeLogging; import net.sourceforge.pmd.lang.apex.ApexLanguageProperties; -import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.TextDocument; import apex.jorje.parser.impl.ApexLexer; @@ -37,7 +36,7 @@ public class ApexTokenizer implements Tokenizer { ApexLexer lexer = new ApexLexer(ass) { @Override public void emitErrorMessage(String msg) { - throw new TokenMgrError(getLine(), getCharPositionInLine(), document.getFileId(), msg, null); + throw tokenEntries.makeLexException(getLine(), getCharPositionInLine(), msg, null); } }; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java index aac1717822..4a7e5a3e49 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/TokenFactory.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.cpd; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextDocument; @@ -42,6 +43,8 @@ public interface TokenFactory extends AutoCloseable { recordToken(image, location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()); } + TokenMgrError makeLexException(int line, int column, String message, @Nullable Throwable cause); + /** * Sets the image of an existing token entry. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 98ba1919fd..da7a152868 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -14,6 +14,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.TextDocument; @@ -106,6 +107,11 @@ public class Tokens { tokens.setImage(entry, newImage); } + @Override + public TokenMgrError makeLexException(int line, int column, String message, @Nullable Throwable cause) { + return new TokenMgrError(line, column, fileId, message, cause); + } + @Override public @Nullable TokenEntry peekLastToken() { if (tokens.size() <= firstToken) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java index ef0cd3aa7e..f577dc04b7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java @@ -9,6 +9,7 @@ import java.util.Objects; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguageProcessorRegistry; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.util.AssertionUtil; @@ -54,12 +55,8 @@ public interface Parser { return textDoc.getLanguageVersion(); } - /** - * The display name for where the file comes from. This should - * not be interpreted, it may not be a file-system path. - */ - public String getFileDisplayName() { - return textDoc.getFileId().getOriginalPath(); + public FileId getFileId() { + return textDoc.getFileId(); } /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java index f07f905fca..a350fefa69 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/Chars.java @@ -40,7 +40,11 @@ import net.sourceforge.pmd.util.IteratorUtil.AbstractIterator; */ public final class Chars implements CharSequence { - public static final Chars EMPTY = wrap(""); + /** + * An empty Chars instance. + */ + public static final Chars EMPTY = new Chars("", 0, 0); + /** * Special sentinel used by {@link #lines()}. */ @@ -83,6 +87,8 @@ public final class Chars implements CharSequence { public static Chars wrap(CharSequence chars) { if (chars instanceof Chars) { return (Chars) chars; + } else if (chars.length() == 0) { + return EMPTY; } return new Chars(chars.toString(), 0, chars.length()); } diff --git a/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java index ed2938c0e1..c7dacd2a7a 100644 --- a/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java +++ b/pmd-groovy/src/main/java/net/sourceforge/pmd/lang/groovy/cpd/GroovyTokenizer.java @@ -9,10 +9,7 @@ import org.codehaus.groovy.antlr.parser.GroovyLexer; import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.lang.document.CpdCompat; -import net.sourceforge.pmd.lang.document.FileId; import groovyjarjarantlr.Token; import groovyjarjarantlr.TokenStream; @@ -50,11 +47,7 @@ public class GroovyTokenizer implements Tokenizer { token = tokenStream.nextToken(); } } catch (TokenStreamException err) { - // Wrap exceptions of the Groovy tokenizer in a TokenMgrError, so - // they are correctly handled - // when CPD is executed with the '--skipLexicalErrors' command line - // option - throw new TokenMgrError(lexer.getLine(), lexer.getColumn(), document.getFileId(), err.getMessage(), err); + throw tokens.makeLexException(lexer.getLine(), lexer.getColumn(), err.getMessage(), err); } } } diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java index db3d09d393..de61a0e17a 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlParser.java @@ -14,7 +14,7 @@ public final class HtmlParser implements net.sourceforge.pmd.lang.ast.Parser { @Override public ASTHtmlDocument parse(ParserTask task) { - Document doc = Parser.xmlParser().parseInput(task.getTextDocument().getText().newReader(), ""); + Document doc = Parser.xmlParser().parseInput(task.getTextDocument().newReader(), task.getFileId().toUriString()); HtmlTreeBuilder builder = new HtmlTreeBuilder(); return builder.build(doc, task, new HashMap<>()); } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 84921b674f..01cd8cba12 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -68,8 +68,8 @@ public class ScalaTokenizer implements Tokenizer { // cannot catch it as it's a checked exception and Scala sneaky throws TokenizeException tokE = (TokenizeException) e; Position pos = tokE.pos(); - throw new TokenMgrError( - pos.startLine() + 1, pos.startColumn() + 1, document.getFileId(), "Scalameta threw", tokE); + throw tokenEntries.makeLexException( + pos.startLine() + 1, pos.startColumn() + 1, "Scalameta threw", tokE); } else { throw e; } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaParser.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaParser.java index c74efc68b7..51d541036f 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaParser.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaParser.java @@ -22,7 +22,7 @@ public final class ScalaParser implements Parser { @Override public ASTSource parse(ParserTask task) throws ParseException { - Input.VirtualFile virtualFile = new Input.VirtualFile(task.getFileDisplayName(), task.getSourceText()); + Input.VirtualFile virtualFile = new Input.VirtualFile(task.getFileId().toAbsolutePath(), task.getSourceText()); Dialect dialect = ScalaLanguageModule.dialectOf(task.getLanguageVersion()); Source src = new ScalametaParser(virtualFile, dialect).parseSource(); ASTSource root = (ASTSource) new ScalaTreeBuilder().build(src); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java index edb73c6ec3..e4f4dc7573 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java @@ -47,7 +47,7 @@ class VfExpressionTypeVisitor extends VfVisitorBase { private final List objectsDirectories; VfExpressionTypeVisitor(ParserTask task, VfLanguageProperties vfProperties) { - this.fileId = task.getTextDocument().getFileId(); + this.fileId = task.getFileId(); this.apexDirectories = vfProperties.getProperty(VfLanguageProperties.APEX_DIRECTORIES_DESCRIPTOR); this.objectsDirectories = vfProperties.getProperty(VfLanguageProperties.OBJECTS_DIRECTORIES_DESCRIPTOR); this.apexClassNames = new ArrayList<>(); From b2975387216e943ad30dbfac3730618570bf527e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 29 Apr 2023 19:28:47 +0200 Subject: [PATCH 136/347] Fix merge --- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 2 +- .../sourceforge/pmd/cpd/SourceManager.java | 2 +- .../pmd/cpd/impl/AntlrTokenizer.java | 2 +- .../sourceforge/pmd/cpd/XMLRendererTest.java | 4 +-- .../pmd/lang/ecmascript/TsLanguageModule.java | 25 -------------- .../pmd/lang/typescript/TsLanguageModule.java | 32 +++++++++++++++++ .../typescript/cpd/TypeScriptLanguage.java | 17 ---------- .../net.sourceforge.pmd.lang.Language | 2 +- .../cpd/TypeScriptTokenizerTest.java | 16 ++------- .../pmd/lang/julia/JuliaLanguageModule.java | 33 ++++++++++++++++++ .../pmd/lang/julia/cpd/JuliaLanguage.java | 20 ----------- .../services/net.sourceforge.pmd.cpd.Language | 1 - .../net.sourceforge.pmd.lang.Language | 1 + .../pmd/cpd/JuliaTokenizerTest.java | 34 ------------------- .../lang/julia/cpd/JuliaTokenizerTest.java | 21 ++++++++++++ 15 files changed, 95 insertions(+), 117 deletions(-) delete mode 100644 pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java create mode 100644 pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/TsLanguageModule.java delete mode 100644 pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptLanguage.java create mode 100644 pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/JuliaLanguageModule.java delete mode 100644 pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaLanguage.java delete mode 100644 pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language create mode 100644 pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language delete mode 100644 pmd-julia/src/test/java/net/sourceforge/pmd/cpd/JuliaTokenizerTest.java create mode 100644 pmd-julia/src/test/java/net/sourceforge/pmd/lang/julia/cpd/JuliaTokenizerTest.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 7545d7a70a..bd0785639e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -101,7 +101,7 @@ public final class CpdAnalysis implements AutoCloseable { } private int doTokenize(TextDocument document, Tokenizer tokenizer, Tokens tokens) throws IOException, TokenMgrError { - LOGGER.trace("Tokenizing {}", document.getFileId().toAbsolutePath()); + LOGGER.trace("Tokenizing {}", document.getFileId().getAbsolutePath()); int lastTokenSize = tokens.size(); Tokenizer.tokenize(tokenizer, document, tokens); return tokens.size() - lastTokenSize - 1; /* EOF */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index 8d520bcb5d..e399d7d422 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -26,7 +26,7 @@ class SourceManager implements AutoCloseable { private final Map> files = new ConcurrentHashMap<>(); private final Map fileByPathId = new HashMap<>(); private final List textFiles; - private FileNameRenderer fileNameRenderer = FileId::toAbsolutePath; + private FileNameRenderer fileNameRenderer = FileId::getAbsolutePath; SourceManager(List files) { textFiles = new ArrayList<>(files); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java index db7a5d6f8d..07e2610d06 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenizer.java @@ -22,7 +22,7 @@ import net.sourceforge.pmd.lang.document.TextDocument; public abstract class AntlrTokenizer extends TokenizerBase { @Override protected final TokenManager makeLexerImpl(TextDocument doc) throws IOException { - CharStream charStream = CharStreams.fromReader(doc.newReader(), doc.getFileId().toAbsolutePath()); + CharStream charStream = CharStreams.fromReader(doc.newReader(), doc.getFileId().getAbsolutePath()); return new AntlrTokenManager(getLexerForSource(charStream), doc); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index dacf2196c4..7bbefe3ff7 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -77,7 +77,7 @@ class XMLRendererTest { } if (file != null) { assertEquals("1", file.getAttributes().getNamedItem("line").getNodeValue()); - assertEquals(foo1.toAbsolutePath(), file.getAttributes().getNamedItem("path").getNodeValue()); + assertEquals(foo1.getAbsolutePath(), file.getAttributes().getNamedItem("path").getNodeValue()); assertEquals("6", file.getAttributes().getNamedItem("endline").getNodeValue()); assertEquals("1", file.getAttributes().getNamedItem("column").getNodeValue()); assertEquals("1", file.getAttributes().getNamedItem("endcolumn").getNodeValue()); @@ -146,7 +146,7 @@ class XMLRendererTest { } if (file != null) { assertEquals("1", file.getAttributes().getNamedItem("line").getNodeValue()); - assertEquals(fileName.toAbsolutePath(), file.getAttributes().getNamedItem("path").getNodeValue()); + assertEquals(fileName.getAbsolutePath(), file.getAttributes().getNamedItem("path").getNodeValue()); assertEquals("2", file.getAttributes().getNamedItem("endline").getNodeValue()); assertEquals("2", file.getAttributes().getNamedItem("column").getNodeValue()); assertEquals("3", file.getAttributes().getNamedItem("endcolumn").getNodeValue()); diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java deleted file mode 100644 index ff06b2c241..0000000000 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/TsLanguageModule.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.ecmascript; - -import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.lang.LanguagePropertyBundle; -import net.sourceforge.pmd.lang.ecmascript.cpd.EcmascriptTokenizer; -import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; - -/** - * CPD only language to recognize TypeScript files. - */ -public class TsLanguageModule extends CpdOnlyLanguageModuleBase { - - public TsLanguageModule() { - super(LanguageMetadata.withId("ts").name("TypeScript").extensions("ts")); - } - - @Override - public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { - return new EcmascriptTokenizer(); - } -} diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/TsLanguageModule.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/TsLanguageModule.java new file mode 100644 index 0000000000..26e22b4f3d --- /dev/null +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/TsLanguageModule.java @@ -0,0 +1,32 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.typescript; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.typescript.cpd.TypeScriptTokenizer; + +/** + * @author pguyot@kallisys.net + */ +public class TsLanguageModule extends CpdOnlyLanguageModuleBase { + + public TsLanguageModule() { + super(LanguageMetadata.withId("typescript") + .name("TypeScript") + .extensions("ts")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new TypeScriptTokenizer(); + } + + public static TsLanguageModule getInstance() { + return (TsLanguageModule) LanguageRegistry.CPD.getLanguageById("typescript"); + } +} diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptLanguage.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptLanguage.java deleted file mode 100644 index 9191e65cc2..0000000000 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptLanguage.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.typescript.cpd; - -import net.sourceforge.pmd.cpd.AbstractLanguage; - -/** - * @author pguyot@kallisys.net - */ -public class TypeScriptLanguage extends AbstractLanguage { - - public TypeScriptLanguage() { - super("TypeScript", "typescript", new TypeScriptTokenizer(), ".ts"); - } -} diff --git a/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language index 6e09a06884..4b7d330499 100644 --- a/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language +++ b/pmd-javascript/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -1,2 +1,2 @@ net.sourceforge.pmd.lang.ecmascript.EcmascriptLanguageModule -net.sourceforge.pmd.lang.ecmascript.TsLanguageModule +net.sourceforge.pmd.lang.typescript.TsLanguageModule diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptTokenizerTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptTokenizerTest.java index 96c7e660ac..27ada122a6 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptTokenizerTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/typescript/cpd/TypeScriptTokenizerTest.java @@ -4,27 +4,15 @@ package net.sourceforge.pmd.lang.typescript.cpd; -import java.util.Properties; - import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.typescript.TsLanguageModule; class TypeScriptTokenizerTest extends CpdTextComparisonTest { TypeScriptTokenizerTest() { - super(".ts"); - } - - @Override - public Tokenizer newTokenizer(Properties properties) { - return new TypeScriptTokenizer(); - } - - @Override - protected String getResourcePrefix() { - return "../cpd/testdata"; + super(TsLanguageModule.getInstance(), ".ts"); } @Test diff --git a/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/JuliaLanguageModule.java b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/JuliaLanguageModule.java new file mode 100644 index 0000000000..4ffd85e29f --- /dev/null +++ b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/JuliaLanguageModule.java @@ -0,0 +1,33 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.julia; + +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; +import net.sourceforge.pmd.lang.julia.cpd.JuliaTokenizer; + +/** + * Language implementation for Julia. + */ +public class JuliaLanguageModule extends CpdOnlyLanguageModuleBase { + + /** + * Creates a new Julia Language instance. + */ + public JuliaLanguageModule() { + super(LanguageMetadata.withId("julia").name("Julia").extensions("jl")); + } + + @Override + public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) { + return new JuliaTokenizer(); + } + + public static JuliaLanguageModule getInstance() { + return (JuliaLanguageModule) LanguageRegistry.CPD.getLanguageById("julia"); + } +} diff --git a/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaLanguage.java b/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaLanguage.java deleted file mode 100644 index d6bd001607..0000000000 --- a/pmd-julia/src/main/java/net/sourceforge/pmd/lang/julia/cpd/JuliaLanguage.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.julia.cpd; - -import net.sourceforge.pmd.cpd.AbstractLanguage; - -/** - * Language implementation for Julia. - */ -public class JuliaLanguage extends AbstractLanguage { - - /** - * Creates a new Julia Language instance. - */ - public JuliaLanguage() { - super("Julia", "julia", new JuliaTokenizer(), ".jl"); - } -} diff --git a/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language b/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language deleted file mode 100644 index bd23fbae93..0000000000 --- a/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.cpd.Language +++ /dev/null @@ -1 +0,0 @@ -net.sourceforge.pmd.lang.julia.cpd.JuliaLanguage diff --git a/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language new file mode 100644 index 0000000000..3d5af03ece --- /dev/null +++ b/pmd-julia/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language @@ -0,0 +1 @@ +net.sourceforge.pmd.lang.julia.JuliaLanguageModule diff --git a/pmd-julia/src/test/java/net/sourceforge/pmd/cpd/JuliaTokenizerTest.java b/pmd-julia/src/test/java/net/sourceforge/pmd/cpd/JuliaTokenizerTest.java deleted file mode 100644 index e09f2f12f3..0000000000 --- a/pmd-julia/src/test/java/net/sourceforge/pmd/cpd/JuliaTokenizerTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd; - -import java.util.Properties; - -import org.junit.jupiter.api.Test; - -import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; -import net.sourceforge.pmd.lang.julia.cpd.JuliaTokenizer; - -class JuliaTokenizerTest extends CpdTextComparisonTest { - JuliaTokenizerTest() { - super(".jl"); - } - - @Override - protected String getResourcePrefix() { - return "../lang/julia/cpd/testdata"; - } - - @Override - public Tokenizer newTokenizer(Properties properties) { - JuliaTokenizer tok = new JuliaTokenizer(); - return tok; - } - - @Test - void testMathExample() { - doTest("mathExample"); - } -} diff --git a/pmd-julia/src/test/java/net/sourceforge/pmd/lang/julia/cpd/JuliaTokenizerTest.java b/pmd-julia/src/test/java/net/sourceforge/pmd/lang/julia/cpd/JuliaTokenizerTest.java new file mode 100644 index 0000000000..3c493a5544 --- /dev/null +++ b/pmd-julia/src/test/java/net/sourceforge/pmd/lang/julia/cpd/JuliaTokenizerTest.java @@ -0,0 +1,21 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.julia.cpd; + +import org.junit.jupiter.api.Test; + +import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; +import net.sourceforge.pmd.lang.julia.JuliaLanguageModule; + +class JuliaTokenizerTest extends CpdTextComparisonTest { + JuliaTokenizerTest() { + super(JuliaLanguageModule.getInstance(), ".jl"); + } + + @Test + void testMathExample() { + doTest("mathExample"); + } +} From 72740a8151989acc84a60ccf88a17c1c77ab5a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 29 Apr 2023 19:45:09 +0200 Subject: [PATCH 137/347] Lint --- .../java/net/sourceforge/pmd/lang/LanguageRegistry.java | 6 +++--- .../net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java | 1 - .../main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java index 8add12abab..881b94e273 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageRegistry.java @@ -65,9 +65,9 @@ public final class LanguageRegistry implements Iterable { */ public LanguageRegistry(Set languages) { this.languages = languages.stream() - .sorted(Comparator.comparing(Language::getTerseName, String::compareToIgnoreCase)) + .sorted(Comparator.comparing(Language::getId, String::compareToIgnoreCase)) .collect(CollectionUtil.toUnmodifiableSet()); - this.languagesById = CollectionUtil.associateBy(languages, Language::getTerseName); + this.languagesById = CollectionUtil.associateBy(languages, Language::getId); this.languagesByFullName = CollectionUtil.associateBy(languages, Language::getName); } @@ -129,7 +129,7 @@ public final class LanguageRegistry implements Iterable { public static @NonNull LanguageRegistry loadLanguages(ClassLoader classLoader) { // sort languages by terse name. Avoiding differences in the order of languages // across JVM versions / OS. - Set languages = new TreeSet<>(Comparator.comparing(Language::getTerseName, String::compareToIgnoreCase)); + Set languages = new TreeSet<>(Comparator.comparing(Language::getId, String::compareToIgnoreCase)); ServiceLoader languageLoader = ServiceLoader.load(Language.class, classLoader); Iterator iterator = languageLoader.iterator(); while (true) { diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java index bd073ce936..26149963cb 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/HtmlTokenizer.java @@ -13,7 +13,6 @@ import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguageProcessorRegistry; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; -import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.html.HtmlLanguageModule; diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 01cd8cba12..79b59833a5 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -10,9 +10,7 @@ import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; import scala.collection.Iterator; From d49178ae5f251fa0d7868f88fd70fedbc1061079 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 14 May 2023 11:05:30 +0200 Subject: [PATCH 138/347] [java] New Rule: Use Explicit Types Fixes #2847 --- docs/pages/release_notes.md | 2 + docs/pages/release_notes_pmd7.md | 1 + .../main/resources/rulesets/releases/700.xml | 1 + .../resources/category/java/bestpractices.xml | 28 +++++++++++ .../bestpractices/UseExplicitTypesTest.java | 11 +++++ .../bestpractices/xml/UseExplicitTypes.xml | 48 +++++++++++++++++++ 6 files changed, 91 insertions(+) create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseExplicitTypesTest.java create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseExplicitTypes.xml diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 40a27a2f68..f27eaac12c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -182,6 +182,7 @@ Contributors: [Wener](https://github.com/wener-tiobe) (@wener-tiobe) * {% rule apex/design/UnusedMethod %} finds unused methods in your code. **Java** +* {% rule java/bestpractices/UseExplicitTypes %} reports usages of `var` keyword, which was introduced with Java 10. * {% rule java/codestyle/UnnecessaryBoxing %} reports boxing and unboxing conversions that may be made implicit. **Kotlin** @@ -453,6 +454,7 @@ Language specific fixes: * [#2806](https://github.com/pmd/pmd/issues/2806): \[java] SwitchStmtsShouldHaveDefault false-positive with Java 14 switch non-fallthrough branches * [#2822](https://github.com/pmd/pmd/issues/2822): \[java] LooseCoupling rule: Extend to cover user defined implementations and interfaces * [#2843](https://github.com/pmd/pmd/pull/2843): \[java] Fix UnusedAssignment FP with field accesses + * [#2847](https://github.com/pmd/pmd/issues/2847): \[java] New Rule: Use Explicit Types * [#2882](https://github.com/pmd/pmd/issues/2882): \[java] UseTryWithResources - false negative for explicit close * [#2883](https://github.com/pmd/pmd/issues/2883): \[java] JUnitAssertionsShouldIncludeMessage false positive with method call * [#2890](https://github.com/pmd/pmd/issues/2890): \[java] UnusedPrivateMethod false positive with generics diff --git a/docs/pages/release_notes_pmd7.md b/docs/pages/release_notes_pmd7.md index 76df0bb2a0..93e75fe377 100644 --- a/docs/pages/release_notes_pmd7.md +++ b/docs/pages/release_notes_pmd7.md @@ -537,6 +537,7 @@ Related issue: [[core] Explicitly name all language versions (#4120)](https://gi * {% rule apex/design/UnusedMethod %} finds unused methods in your code. **Java** +* {% rule java/bestpractices/UseExplicitTypes %} reports usages of `var` keyword, which was introduced with Java 10. * {% rule java/codestyle/UnnecessaryBoxing %} reports boxing and unboxing conversions that may be made implicit. **Kotlin** diff --git a/pmd-core/src/main/resources/rulesets/releases/700.xml b/pmd-core/src/main/resources/rulesets/releases/700.xml index 4646553133..fb1e1fe959 100644 --- a/pmd-core/src/main/resources/rulesets/releases/700.xml +++ b/pmd-core/src/main/resources/rulesets/releases/700.xml @@ -10,6 +10,7 @@ This ruleset contains links to rules that are new in PMD v7.0.0 + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 728ab98c9b..cb8612daca 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1688,6 +1688,34 @@ public class Foo { + + +Java 10 introduced the `var` keyword. This reduces the amount of typing but decreases the reading comprehension of the +code. + + 3 + + + + + + + + + + + + + + + quadrat = (var x) -> x*x; + } + + private String getFoo() { + return "a"; + } +} +]]> + + + No vars anywhere + 4 + 5,6,8,9 + + + + + Allow literals + true + 2 + 8,9 + + + + + Allow constructor calls + true + 3 + 5,6,8 + + + From 665187633f40140a0f29e15ae029df2dd10a0ec6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 14 May 2023 11:11:09 +0200 Subject: [PATCH 139/347] [java] Update quickstart.xml ruleset --- pmd-java/src/main/resources/rulesets/java/quickstart.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pmd-java/src/main/resources/rulesets/java/quickstart.xml b/pmd-java/src/main/resources/rulesets/java/quickstart.xml index 6be4cfb7e3..9fcec4dc26 100644 --- a/pmd-java/src/main/resources/rulesets/java/quickstart.xml +++ b/pmd-java/src/main/resources/rulesets/java/quickstart.xml @@ -51,6 +51,7 @@ + From 9b098b8803b43cdf2a2e05b781029418f9ca2a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 28 May 2023 13:47:48 +0200 Subject: [PATCH 140/347] Update release notes --- docs/pages/release_notes.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index cb3394e5f4..6b502af48f 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -228,6 +228,13 @@ Contributors: [Wener](https://github.com/wener-tiobe) (@wener-tiobe) literals were ignored. The new option additional ignores identifiers as well in sequences. * See [PR #4470](https://github.com/pmd/pmd/pull/4470) for details. + +#### Changed: Rule properties + +* The old deprecated classes like `IntProperty` and `StringProperty` have been removed. Please use {% jdoc core::properties.PropertyFactory %} to create properties. +* All properties which accept multiple values now use a comma (`,`) as a delimiter. The previous default was a pipe character (`|`). The delimiter is not configurable anymore. +* The `min` and `max` attributes in property definitions in the XML are now optional and can appear separately or be omitted. + ### ๐ŸŒŸ New and changed rules #### New Rules From ae452c058858598c3e6455ad09c562b6bc2c8d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 28 May 2023 13:59:53 +0200 Subject: [PATCH 141/347] Update docs --- .../userdocs/extending/defining_properties.md | 59 ++++++------------- .../pmd/properties/PropertyFactory.java | 5 -- .../AvoidReassigningLoopVariablesRule.java | 13 +--- 3 files changed, 21 insertions(+), 56 deletions(-) diff --git a/docs/pages/pmd/userdocs/extending/defining_properties.md b/docs/pages/pmd/userdocs/extending/defining_properties.md index 0805a8486c..4a64949462 100644 --- a/docs/pages/pmd/userdocs/extending/defining_properties.md +++ b/docs/pages/pmd/userdocs/extending/defining_properties.md @@ -27,7 +27,7 @@ The basic thing you need to do as a developer is to define a **property descript * Its *description*, for documentation purposes; * Its *default value* -Don't worry, all of these attributes can be specified in a single Java statement (or xml element for XPath rules). +All of these attributes can be specified in a single Java statement (or XML element for XPath rules). ## For Java rules @@ -43,7 +43,7 @@ You can then retrieve the value of the property at any time using {% jdoc !a!pro 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 +string property, you would call ```java PropertyFactory.stringProperty("myProperty") .desc("This is my property") @@ -53,51 +53,42 @@ PropertyFactory.stringProperty("myProperty") 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='As of version 6.10.0, all property concrete classes are deprecated for -removal in 7.0.0. See the detailed list of planned removals for -information about how to migrate.' %} - - For **numeric properties**, you can add constraints on the range of acceptable values, e.g. ```java PropertyFactory.intProperty("myIntProperty") .desc("This is my property") .defaultValue(3) - .require(positive()) - .range(0, 100) + .require(positive()) // must be > 0 + .require(below(100)) // must be <= 100 .build(); ``` -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. +Predefined constraints such as `positive` and `below` are available in the class {% jdoc props::NumericConstraints %}. +A custom constraint can be implemented by implementing the interface {% jdoc props::PropertyConstraint %}. -**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, +**Enumerated properties** do not 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 map = new HashMap<>(); - -static { - map.put("easyMode", new EasyStrategy()); - map.put("hardMode", new HardStrategy()); +enum Mode { + Easy, Hard } -static PropertyDescriptor modeProperty - = PropertyFactory.enumProperty("modeProperty", map) +// Using this method, the labels are the `toString` of each enum constant. +// To customize this look at the overloads of `enumProperty`. +static PropertyDescriptor modeProperty + = PropertyFactory.enumProperty("modeProperty", Mode.class) .desc("This is my property") - .defaultValue(new EasyStrategy()) + .defaultValue(Mode.Easy) .build(); ``` + ### Example -You can see an example of properties used in a PMD rule [here](https://github.com/pmd/pmd/blob/d06b01785a712e61d33f366520f37c2473f5bd1a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java#L43-L52). +You can see an example of properties used in a PMD rule [here](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidReassigningLoopVariablesRule.java#L40). There are several things to notice here: * 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; @@ -124,13 +115,6 @@ XPath rules can also define their own properties. To do so, you must add a `prop |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. @@ -160,20 +144,15 @@ 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. - - +`List[Boolean]` or `List[Character]`, with every above type allowed. These properties +make use of the **sequence datatype** provided by XPath 2.0 and above. ```xml - (name, PropertyParsingUtil.BOOLEAN); } - // We can add more useful factories with Java 8. - // * We don't really need a Map, just a Function. - // * We could have a factory taking a Class> - // and a Function to build a mapper for a whole enum. - /** * Returns a builder for an enumerated property. Such a property can be * defined for any type {@code }, provided the possible values can be diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidReassigningLoopVariablesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidReassigningLoopVariablesRule.java index a27232407a..a5a052fd99 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidReassigningLoopVariablesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidReassigningLoopVariablesRule.java @@ -4,11 +4,8 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import static java.util.Arrays.asList; import static net.sourceforge.pmd.properties.PropertyFactory.enumProperty; -import static net.sourceforge.pmd.util.CollectionUtil.associateBy; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -39,20 +36,14 @@ import net.sourceforge.pmd.util.StringUtil.CaseConvention; public class AvoidReassigningLoopVariablesRule extends AbstractJavaRulechainRule { - private static final Map FOREACH_REASSIGN_VALUES = - associateBy(asList(ForeachReassignOption.values()), ForeachReassignOption::getDisplayName); - private static final PropertyDescriptor FOREACH_REASSIGN - = enumProperty("foreachReassign", FOREACH_REASSIGN_VALUES) + = enumProperty("foreachReassign", ForeachReassignOption.class, ForeachReassignOption::getDisplayName) .defaultValue(ForeachReassignOption.DENY) .desc("how/if foreach control variables may be reassigned") .build(); - private static final Map FOR_REASSIGN_VALUES = - associateBy(asList(ForReassignOption.values()), ForReassignOption::getDisplayName); - private static final PropertyDescriptor FOR_REASSIGN - = enumProperty("forReassign", FOR_REASSIGN_VALUES) + = enumProperty("forReassign", ForReassignOption.class, ForReassignOption::getDisplayName) .defaultValue(ForReassignOption.DENY) .desc("how/if for control variables may be reassigned") .build(); From aa716acebb8780ad0574daf3610584e2dc08f47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 28 May 2023 15:04:36 +0200 Subject: [PATCH 142/347] Fix CPD renderer tests on windows Note that those removed tests about form feeds are related to the fact that form feeds are invalid path characters on windows, so we can't construct a file id containing them anyway. --- .../net/sourceforge/pmd/util/StringUtil.java | 6 +----- .../sourceforge/pmd/cpd/CSVRendererTest.java | 6 ++++-- .../sourceforge/pmd/cpd/XMLRendererTest.java | 21 ++++++++----------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java index c6413b59ee..0a4bd90b49 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java @@ -25,11 +25,7 @@ import net.sourceforge.pmd.lang.document.Chars; public final class StringUtil { - private static final Pattern XML_10_INVALID_CHARS = Pattern.compile( - "\\x00|\\x01|\\x02|\\x03|\\x04|\\x05|\\x06|\\x07|\\x08|" - + "\\x0b|\\x0c|\\x0e|\\x0f|" - + "\\x10|\\x11|\\x12|\\x13|\\x14|\\x15|\\x16|\\x17|\\x18|" - + "\\x19|\\x1a|\\x1b|\\x1c|\\x1d|\\x1e|\\x1f"); + private static final Pattern XML_10_INVALID_CHARS = Pattern.compile("[[\\x00-\\x1F]&&[^\\x09\\x0A\\x0D]]"); private StringUtil() { } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java index a8535a6463..8f253917f5 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java @@ -29,7 +29,8 @@ class CSVRendererTest { renderer.render(builder.build(), sw); String report = sw.toString(); String expectedReport = "tokens,occurrences" + System.lineSeparator() - + "75,2,48,10,/var/Foo.java,73,20,/var/Bar.java" + System.lineSeparator(); + + "75,2,48,10," + CpdTestUtils.FOO_FILE_ID.getAbsolutePath() + ",73,20," + + CpdTestUtils.BAR_FILE_ID.getAbsolutePath() + System.lineSeparator(); assertEquals(expectedReport, report); } @@ -48,7 +49,8 @@ class CSVRendererTest { renderer.render(builder.build(), sw); String report = sw.toString(); String expectedReport = "lines,tokens,occurrences" + System.lineSeparator() - + "10,75,2,48,\"/var,with,commas/Foo.java\",73,\"/var,with,commas/Bar.java\"" + System.lineSeparator(); + + "10,75,2,48,\"" + foo.getAbsolutePath() + "\",73,\"" + bar.getAbsolutePath() + "\"" + + System.lineSeparator(); assertEquals(expectedReport, report); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index 7bbefe3ff7..eb48c85f2a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -6,9 +6,8 @@ package net.sourceforge.pmd.cpd; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -169,17 +168,15 @@ class XMLRendererTest { void testRendererEncodedPath() throws IOException { CPDReportRenderer renderer = new XMLRenderer(); CpdReportBuilder builder = new CpdReportBuilder(); - final String espaceChar = "<"; - Mark mark1 = builder.createMark("public", FileId.fromPathLikeString("/var/A\";")); - assertFalse(report.contains("x=\"]]>\";")); // must be escaped + assertThat(report, not(containsString("x=\"]]>\";"))); // must be escaped } } From ac33663e94329a43234f73f30052f075343397ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 29 May 2023 14:28:30 +0200 Subject: [PATCH 143/347] Fix CPD cli tests --- .../net/sourceforge/pmd/cli/CpdCliTest.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java index ccc7e5d3f6..e751ebf741 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java @@ -32,12 +32,13 @@ class CpdCliTest extends BaseCliTest { private static final String BASE_RES_PATH = "src/test/resources/net/sourceforge/pmd/cli/cpd/"; private static final String SRC_DIR = BASE_RES_PATH + "files/"; + private static final Path SRC_PATH = Paths.get(SRC_DIR).toAbsolutePath(); private static final Map NUMBER_OF_TOKENS = ImmutableMap.of( - Paths.get(SRC_DIR, "dup1.java").toString(), 89, - Paths.get(SRC_DIR, "dup2.java").toString(), 89, - Paths.get(SRC_DIR, "file_with_ISO-8859-1_encoding.java").toString(), 8, - Paths.get(SRC_DIR, "file_with_utf8_bom.java").toString(), 9 + SRC_PATH.resolve("dup1.java").toString(), 89, + SRC_PATH.resolve("dup2.java").toString(), 89, + SRC_PATH.resolve("file_with_ISO-8859-1_encoding.java").toString(), 8, + SRC_PATH.resolve("file_with_utf8_bom.java").toString(), 9 ); @TempDir private Path tempDir; @@ -146,17 +147,17 @@ class CpdCliTest extends BaseCliTest { @Test void testNoDuplicatesResultRendering() throws Exception { - final Path srcDir = Paths.get(SRC_DIR); + final Path srcDir = Paths.get(SRC_DIR).toAbsolutePath(); String expectedReport = "\n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + "\n"; @@ -206,7 +207,7 @@ class CpdCliTest extends BaseCliTest { @Test void jsShouldFindDuplicatesWithDifferentFileExtensions() throws Exception { - runCli(VIOLATIONS_FOUND, "--minimum-tokens", "5", "--language", "ts", + runCli(VIOLATIONS_FOUND, "--minimum-tokens", "5", "--language", "typescript", "-d", BASE_RES_PATH + "tsFiles/File1.ts", BASE_RES_PATH + "tsFiles/File2.ts") .checkStdOut(containsString("Found a 9 line (32 tokens) duplication in the following files")); } From bd42296c0c64ca395822cb66a84a6c4901a77961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 29 May 2023 15:25:41 +0200 Subject: [PATCH 144/347] Fix distribution IT --- .../test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index c22c5601a9..2a146dce04 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -33,7 +33,7 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest { "fortran", "gherkin", "go", "groovy", "html", "java", "jsp", "julia", "kotlin", "lua", "matlab", "modelica", "objectivec", "perl", - "php", "plsql", "pom", "python", "ruby", "scala", "swift", "ts", + "php", "plsql", "pom", "python", "ruby", "scala", "swift", "tsql", "typescript", "vf", "vm", "wsdl", "xml", "xsl" ); From 5c4a5666899eaa2fe18377d323f02bf4eeec091e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 29 May 2023 15:57:04 +0200 Subject: [PATCH 145/347] Doc for CPD --- .../net/sourceforge/pmd/cpd/CPDReport.java | 13 ++++- .../net/sourceforge/pmd/cpd/CSVRenderer.java | 52 ++++++++++++++++--- .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 10 ++-- .../sourceforge/pmd/cpd/SourceManager.java | 4 ++ .../pmd/cpd/renderer/CPDReportRenderer.java | 20 +++++++ .../sourceforge/pmd/cpd/CPDReportTest.java | 14 ++--- 6 files changed, 93 insertions(+), 20 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java index 7274ae6bc0..a0c0251773 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java @@ -12,10 +12,13 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileId; /** + * The result of a CPD analysis. This is rendered by a {@link CPDReportRenderer}. + * * @since 6.48.0 */ public class CPDReport { @@ -32,10 +35,13 @@ public class CPDReport { this.numberOfTokensPerFile = Collections.unmodifiableMap(new TreeMap<>(numberOfTokensPerFile)); } + /** Return the list of duplication matches found by the CPD analysis. */ public List getMatches() { return matches; } + /** Return a map containing the number of tokens by processed file. */ + public Map getNumberOfTokensPerFile() { return numberOfTokensPerFile; } @@ -52,7 +58,8 @@ public class CPDReport { /** * Creates a new CPD report taking all the information from this report, - * but filtering the matches. + * but filtering the matches. Note that the {@linkplain #getNumberOfTokensPerFile() token count map} + * is not filtered. * * @param filter when true, the match will be kept. * @@ -65,7 +72,9 @@ public class CPDReport { return new CPDReport(sourceManager, filtered, this.getNumberOfTokensPerFile()); } - + /** + * Return the display name of the given file. + */ public String getDisplayName(FileId fileId) { return sourceManager.getFileDisplayName(fileId); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java index 691a515932..a9e4fd9e74 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java @@ -13,6 +13,49 @@ import org.apache.commons.lang3.StringEscapeUtils; import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.FileLocation; +/** + * Renders a report to CSV. The CSV format renders each match (duplication) + * as a single line with the following columns: + *
    + *
  • lines (optional): The number of lines the first mark of a match spans. + * Only output if the {@code lineCountPerFile} is disabled (see ctor params).
  • + *
  • tokens: The number of duplicated tokens in a match (size of the match).
  • + *
  • occurrences: The number of duplicates in a match (number of times the tokens were found in distinct places).
  • + *
+ * + *

Trailing each line are pairs (or triples, if {@code lineCountPerFile} is enabled) + * of fields describing each file where the duplication was found in the format + * {@code (start line, line count (optional), file path)}. These repeat at least twice. + * + *

Examples

+ *

+ * Example without {@code lineCountPerFile}: + *

{@code
+ * lines,tokens,occurrences
+ * 10,75,2,48,/var/file1,73,/var/file2
+ * }
+ * This describes one match with the following characteristics: + *
    + *
  • The first duplicate instance is 10 lines long; + *
  • 75 duplicated tokens; + *
  • 2 duplicate instances; + *
  • The first duplicate instance is in file {@code /var/file1} and starts at line 48;
  • + *
  • The second duplicate instance is in file {@code /var/file2} and starts at line 73.
  • + *
+ *

+ * Example with {@code lineCountPerFile}: + *

{@code
+ * tokens,occurrences
+ * 75,2,48,10,/var/file1,73,12,/var/file2
+ * }
+ * This describes one match with the following characteristics: + *
    + *
  • 75 duplicated tokens + *
  • 2 duplicate instances + *
  • The first duplicate instance is in file {@code /var/file1}, starts at line 48, and is 10 lines long;
  • + *
  • The second duplicate instance is in file {@code /var/file2}, starts at line 73, and is 12 lines long.
  • + *
+ */ public class CSVRenderer implements CPDReportRenderer { private final char separator; @@ -40,21 +83,18 @@ public class CSVRenderer implements CPDReportRenderer { @Override public void render(CPDReport report, Writer writer) throws IOException { - Iterator matches = report.getMatches().iterator(); if (!lineCountPerFile) { writer.append("lines").append(separator); } writer.append("tokens").append(separator).append("occurrences").append(System.lineSeparator()); - while (matches.hasNext()) { - Match match = matches.next(); - + for (Match match : report.getMatches()) { if (!lineCountPerFile) { writer.append(String.valueOf(match.getLineCount())).append(separator); } writer.append(String.valueOf(match.getTokenCount())).append(separator) - .append(String.valueOf(match.getMarkCount())).append(separator); - for (Iterator marks = match.iterator(); marks.hasNext();) { + .append(String.valueOf(match.getMarkCount())).append(separator); + for (Iterator marks = match.iterator(); marks.hasNext(); ) { Mark mark = marks.next(); FileLocation loc = mark.getLocation(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index bd0785639e..f3285027e2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.cpd; import java.io.IOException; +import java.io.Writer; import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; @@ -22,6 +23,7 @@ import net.sourceforge.pmd.internal.util.FileCollectionUtil; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguagePropertyBundle; +import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.document.FileCollector; import net.sourceforge.pmd.lang.document.FileId; @@ -134,8 +136,8 @@ public final class CpdAnalysis implements AutoCloseable { numberOfTokensPerFile.put(textDocument.getFileId(), newTokens); listener.addedFile(1); } catch (TokenMgrError | IOException e) { - if (e instanceof TokenMgrError) { // NOPMD - ((TokenMgrError) e).setFileId(textFile.getFileId()); + if (e instanceof FileAnalysisException) { // NOPMD + ((FileAnalysisException) e).setFileId(textFile.getFileId()); } String message = configuration.isSkipLexicalErrors() ? "Skipping file" : "Error while tokenizing"; reporter.errorEx(message, e); @@ -157,7 +159,9 @@ public final class CpdAnalysis implements AutoCloseable { CPDReport cpdReport = new CPDReport(sourceManager, matches, numberOfTokensPerFile); if (renderer != null) { - renderer.render(cpdReport, IOUtil.createWriter(Charset.defaultCharset(), null)); + try (Writer writer = IOUtil.createWriter(Charset.defaultCharset(), null)) { + renderer.render(cpdReport, writer); + } } consumer.accept(cpdReport); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java index e399d7d422..9796ee82a6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SourceManager.java @@ -21,6 +21,10 @@ import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.reporting.FileNameRenderer; +/** + * Maps {@link FileId} to {@link TextDocument}, reusing documents with + * {@link SoftReference} if they have not been replaced yet. + */ class SourceManager implements AutoCloseable { private final Map> files = new ConcurrentHashMap<>(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDReportRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDReportRenderer.java index 30a694bf40..692c610939 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDReportRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDReportRenderer.java @@ -9,9 +9,29 @@ import java.io.StringWriter; import java.io.Writer; import net.sourceforge.pmd.cpd.CPDReport; +import net.sourceforge.pmd.cpd.CSVRenderer; +import net.sourceforge.pmd.cpd.SimpleRenderer; +import net.sourceforge.pmd.cpd.VSRenderer; +import net.sourceforge.pmd.cpd.XMLRenderer; +/** + * Render a {@link CPDReport} to a file. + * + * @see CSVRenderer + * @see XMLRenderer + * @see SimpleRenderer + * @see VSRenderer + */ public interface CPDReportRenderer { + /** + * Write out the contents of the report to the given writer. + * + * @param report The report to write + * @param writer A writer for the report file + * + * @throws IOException If the writer throws + */ void render(CPDReport report, Writer writer) throws IOException; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java index 502c9634ce..083ce1db74 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDReportTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.CpdTestUtils.CpdReportBuilder; import net.sourceforge.pmd.lang.document.FileId; +import net.sourceforge.pmd.util.CollectionUtil; class CPDReportTest { @@ -31,15 +32,10 @@ class CPDReportTest { assertEquals(3, original.getMatches().size()); CPDReport filtered = original.filterMatches( - match -> { - // only keep file1.java - for (Mark mark : match) { - if (mark.getLocation().getFileId().equals(file1)) { - return true; - } - } - return false; - }); + // only keep file1.java + match -> CollectionUtil.any(match, mark -> mark.getLocation().getFileId().equals(file1)) + ); + assertEquals(2, filtered.getMatches().size()); for (Match match : filtered.getMatches()) { boolean containsFile1 = From 5c436c7bca3245fdd1330f95959947b634fb82cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 29 May 2023 16:04:12 +0200 Subject: [PATCH 146/347] Fix cpd outputting unix paths on windows --- .../java/net/sourceforge/pmd/cli/PmdCliTest.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java index cdf607808c..be585879ee 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/PmdCliTest.java @@ -188,7 +188,9 @@ class PmdCliTest extends BaseCliTest { // change working directory System.setProperty("user.dir", srcDir.toString()); runCli(VIOLATIONS_FOUND, "--dir", ".", "--rulesets", DUMMY_RULESET_WITH_VIOLATIONS) - .verify(res -> res.checkStdOut(containsString("./src/test/resources/net/sourceforge/pmd/cli/src/anotherfile.dummy"))); + .verify(res -> res.checkStdOut(containsString( + "./src/test/resources/net/sourceforge/pmd/cli/src/anotherfile.dummy".replace('/', File.separatorChar) + ))); }); } @@ -364,7 +366,7 @@ class PmdCliTest extends BaseCliTest { @Test void testNoRelativizeWithRelativeSrcDir() throws Exception { // Note, that we can't reliably change the current working directory for the current java process - // therefore we use the current directory and make sure, we are at the correct place - in pmd-core + // therefore we use the current directory and make sure, we are at the correct place - in pmd-cli Path cwd = Paths.get(".").toRealPath(); assertThat(cwd.toString(), endsWith("pmd-cli")); String relativeSrcDir = "src/test/resources/net/sourceforge/pmd/cli/src"; @@ -379,7 +381,7 @@ class PmdCliTest extends BaseCliTest { @Test void testNoRelativizeWithRelativeSrcDirParent() throws Exception { // Note, that we can't reliably change the current working directory for the current java process - // therefore we use the current directory and make sure, we are at the correct place - in pmd-core + // therefore we use the current directory and make sure, we are at the correct place - in pmd-cli Path cwd = Paths.get(".").toRealPath(); assertThat(cwd.toString(), endsWith("pmd-cli")); String relativeSrcDir = "src/test/resources/net/sourceforge/pmd/cli/src"; @@ -388,16 +390,16 @@ class PmdCliTest extends BaseCliTest { // use the parent directory Path relativeSrcDirWithParent = Paths.get(relativeSrcDir, ".."); + String expectedFile = "\n" + relativeSrcDirWithParent.resolve("src/somefile.dummy"); runCli(VIOLATIONS_FOUND, "--dir", relativeSrcDirWithParent.toString(), "--rulesets", - DUMMY_RULESET_WITH_VIOLATIONS) - .verify(result -> result.checkStdOut( - containsString("\n" + relativeSrcDirWithParent + "/src/somefile.dummy"))); + DUMMY_RULESET_WITH_VIOLATIONS) + .verify(result -> result.checkStdOut(containsString(expectedFile))); } @Test void testRelativizeWithRootRelativeSrcDir() throws Exception { // Note, that we can't reliably change the current working directory for the current java process - // therefore we use the current directory and make sure, we are at the correct place - in pmd-core + // therefore we use the current directory and make sure, we are at the correct place - in pmd-cli Path cwd = Paths.get(".").toRealPath(); assertThat(cwd.toString(), endsWith("pmd-cli")); String relativeSrcDir = "src/test/resources/net/sourceforge/pmd/cli/src"; From 885ab6cbf0a84ab5c6be93ae3c113e52edc117b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 29 May 2023 16:29:58 +0200 Subject: [PATCH 147/347] Lint --- pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java index a9e4fd9e74..9b6a9215b0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java @@ -94,7 +94,7 @@ public class CSVRenderer implements CPDReportRenderer { } writer.append(String.valueOf(match.getTokenCount())).append(separator) .append(String.valueOf(match.getMarkCount())).append(separator); - for (Iterator marks = match.iterator(); marks.hasNext(); ) { + for (Iterator marks = match.iterator(); marks.hasNext();) { Mark mark = marks.next(); FileLocation loc = mark.getLocation(); From 8a89a4c7861a2fecb3710c0242ded6f3958c2250 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 1 Jun 2023 17:29:24 +0200 Subject: [PATCH 148/347] Use explicit encoding in ruleset files --- .../resources/category/apex/bestpractices.xml | 2 +- .../resources/category/apex/codestyle.xml | 2 +- .../main/resources/category/apex/design.xml | 2 +- .../resources/category/apex/documentation.xml | 2 +- .../resources/category/apex/errorprone.xml | 2 +- .../category/apex/multithreading.xml | 2 +- .../resources/category/apex/performance.xml | 2 +- .../main/resources/category/apex/security.xml | 2 +- .../pmd/lang/apex/DefaultRulesetTest.java | 9 ++++- .../resources/category/html/bestpractices.xml | 2 +- .../resources/category/html/codestyle.xml | 2 +- .../main/resources/category/html/design.xml | 2 +- .../resources/category/html/documentation.xml | 2 +- .../resources/category/html/errorprone.xml | 2 +- .../category/html/multithreading.xml | 2 +- .../resources/category/html/performance.xml | 2 +- .../main/resources/category/html/security.xml | 2 +- .../resources/category/java/bestpractices.xml | 2 +- .../resources/category/java/codestyle.xml | 2 +- .../main/resources/category/java/design.xml | 2 +- .../resources/category/java/documentation.xml | 2 +- .../resources/category/java/errorprone.xml | 2 +- .../category/java/multithreading.xml | 2 +- .../resources/category/java/performance.xml | 2 +- .../main/resources/category/java/security.xml | 2 +- .../resources/rulesets/java/quickstart.xml | 2 +- .../pmd/lang/java/QuickstartRulesetTest.java | 9 ++++- .../category/ecmascript/bestpractices.xml | 2 +- .../category/ecmascript/codestyle.xml | 2 +- .../resources/category/ecmascript/design.xml | 2 +- .../category/ecmascript/documentation.xml | 2 +- .../category/ecmascript/errorprone.xml | 2 +- .../category/ecmascript/multithreading.xml | 2 +- .../category/ecmascript/performance.xml | 2 +- .../category/ecmascript/security.xml | 2 +- .../resources/category/jsp/bestpractices.xml | 2 +- .../main/resources/category/jsp/codestyle.xml | 2 +- .../main/resources/category/jsp/design.xml | 2 +- .../resources/category/jsp/documentation.xml | 2 +- .../resources/category/jsp/errorprone.xml | 2 +- .../resources/category/jsp/multithreading.xml | 2 +- .../resources/category/jsp/performance.xml | 2 +- .../main/resources/category/jsp/security.xml | 2 +- .../category/kotlin/bestpractices.xml | 2 +- .../resources/category/kotlin/errorprone.xml | 2 +- .../category/modelica/bestpractices.xml | 2 +- .../category/plsql/bestpractices.xml | 2 +- .../resources/category/plsql/codestyle.xml | 2 +- .../main/resources/category/plsql/design.xml | 2 +- .../category/plsql/documentation.xml | 2 +- .../resources/category/plsql/errorprone.xml | 2 +- .../category/plsql/multithreading.xml | 2 +- .../resources/category/plsql/performance.xml | 2 +- .../resources/category/plsql/security.xml | 2 +- .../category/scala/bestpractices.xml | 2 +- .../resources/category/scala/codestyle.xml | 2 +- .../main/resources/category/scala/design.xml | 2 +- .../category/scala/documentation.xml | 2 +- .../resources/category/scala/errorprone.xml | 12 +++---- .../category/scala/multithreading.xml | 2 +- .../resources/category/scala/performance.xml | 2 +- .../resources/category/scala/security.xml | 2 +- .../category/swift/bestpractices.xml | 2 +- .../resources/category/swift/codestyle.xml | 2 +- .../main/resources/category/swift/design.xml | 2 +- .../category/swift/documentation.xml | 4 +-- .../resources/category/swift/errorprone.xml | 2 +- .../category/swift/multithreading.xml | 4 +-- .../resources/category/swift/performance.xml | 2 +- .../resources/category/swift/security.xml | 2 +- .../pmd/AbstractRuleSetFactoryTest.java | 35 +++++++++++++++++-- .../resources/category/vf/bestpractices.xml | 2 +- .../main/resources/category/vf/codestyle.xml | 2 +- .../src/main/resources/category/vf/design.xml | 2 +- .../resources/category/vf/documentation.xml | 2 +- .../main/resources/category/vf/errorprone.xml | 2 +- .../resources/category/vf/multithreading.xml | 2 +- .../resources/category/vf/performance.xml | 2 +- .../main/resources/category/vf/security.xml | 2 +- .../resources/category/vm/bestpractices.xml | 2 +- .../main/resources/category/vm/codestyle.xml | 2 +- .../src/main/resources/category/vm/design.xml | 2 +- .../resources/category/vm/documentation.xml | 2 +- .../main/resources/category/vm/errorprone.xml | 2 +- .../resources/category/vm/multithreading.xml | 2 +- .../resources/category/vm/performance.xml | 2 +- .../main/resources/category/vm/security.xml | 2 +- .../resources/category/pom/bestpractices.xml | 2 +- .../main/resources/category/pom/codestyle.xml | 2 +- .../main/resources/category/pom/design.xml | 2 +- .../resources/category/pom/documentation.xml | 2 +- .../resources/category/pom/errorprone.xml | 2 +- .../resources/category/pom/multithreading.xml | 2 +- .../resources/category/pom/performance.xml | 2 +- .../main/resources/category/pom/security.xml | 2 +- .../resources/category/wsdl/bestpractices.xml | 2 +- .../resources/category/wsdl/codestyle.xml | 2 +- .../main/resources/category/wsdl/design.xml | 2 +- .../resources/category/wsdl/documentation.xml | 2 +- .../resources/category/wsdl/errorprone.xml | 2 +- .../category/wsdl/multithreading.xml | 2 +- .../resources/category/wsdl/performance.xml | 2 +- .../main/resources/category/wsdl/security.xml | 2 +- .../resources/category/xml/bestpractices.xml | 2 +- .../main/resources/category/xml/codestyle.xml | 2 +- .../main/resources/category/xml/design.xml | 2 +- .../resources/category/xml/documentation.xml | 2 +- .../resources/category/xml/errorprone.xml | 2 +- .../resources/category/xml/multithreading.xml | 2 +- .../resources/category/xml/performance.xml | 2 +- .../main/resources/category/xml/security.xml | 2 +- .../resources/category/xsl/bestpractices.xml | 2 +- .../main/resources/category/xsl/codestyle.xml | 2 +- .../main/resources/category/xsl/design.xml | 2 +- .../resources/category/xsl/documentation.xml | 2 +- .../resources/category/xsl/errorprone.xml | 2 +- .../resources/category/xsl/multithreading.xml | 2 +- .../resources/category/xsl/performance.xml | 2 +- .../main/resources/category/xsl/security.xml | 2 +- 119 files changed, 171 insertions(+), 128 deletions(-) diff --git a/pmd-apex/src/main/resources/category/apex/bestpractices.xml b/pmd-apex/src/main/resources/category/apex/bestpractices.xml index 6763f71d9a..178f94e82e 100644 --- a/pmd-apex/src/main/resources/category/apex/bestpractices.xml +++ b/pmd-apex/src/main/resources/category/apex/bestpractices.xml @@ -1,4 +1,4 @@ - + + + + + + + + { - RuleSet ruleset = rulesetLoader().loadFromResource("rulesets/apex/quickstart.xml"); + RuleSet ruleset = rulesetLoader().loadFromResource(QUICKSTART_RULESET); assertNotNull(ruleset); }); assertTrue(log.isEmpty(), "No Logging expected"); } + @Test + void correctEncoding() throws Exception { + assertTrue(AbstractRuleSetFactoryTest.hasCorrectEncoding(QUICKSTART_RULESET)); + } + private RuleSetLoader rulesetLoader() { return new RuleSetLoader().enableCompatibility(false); } diff --git a/pmd-html/src/main/resources/category/html/bestpractices.xml b/pmd-html/src/main/resources/category/html/bestpractices.xml index 19c07662e9..775e366565 100644 --- a/pmd-html/src/main/resources/category/html/bestpractices.xml +++ b/pmd-html/src/main/resources/category/html/bestpractices.xml @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + { - RuleSet quickstart = ruleSetLoader.loadFromResource("rulesets/java/quickstart.xml"); + RuleSet quickstart = ruleSetLoader.loadFromResource(QUICKSTART_RULESET); assertFalse(quickstart.getRules().isEmpty()); }); assertTrue(errorOutput.isEmpty()); } + + @Test + void correctEncoding() throws Exception { + assertTrue(AbstractRuleSetFactoryTest.hasCorrectEncoding(QUICKSTART_RULESET)); + } } diff --git a/pmd-javascript/src/main/resources/category/ecmascript/bestpractices.xml b/pmd-javascript/src/main/resources/category/ecmascript/bestpractices.xml index 4c76991f98..3e476dacdd 100644 --- a/pmd-javascript/src/main/resources/category/ecmascript/bestpractices.xml +++ b/pmd-javascript/src/main/resources/category/ecmascript/bestpractices.xml @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> - + Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. - - diff --git a/pmd-scala-modules/pmd-scala-common/src/main/resources/category/scala/multithreading.xml b/pmd-scala-modules/pmd-scala-common/src/main/resources/category/scala/multithreading.xml index 88c22dfc8b..0681a5d987 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/resources/category/scala/multithreading.xml +++ b/pmd-scala-modules/pmd-scala-common/src/main/resources/category/scala/multithreading.xml @@ -1,4 +1,4 @@ - + + + + + + + Rules that are related to code documentation. - \ No newline at end of file + diff --git a/pmd-swift/src/main/resources/category/swift/errorprone.xml b/pmd-swift/src/main/resources/category/swift/errorprone.xml index e4dab5022d..7bf277656b 100644 --- a/pmd-swift/src/main/resources/category/swift/errorprone.xml +++ b/pmd-swift/src/main/resources/category/swift/errorprone.xml @@ -1,4 +1,4 @@ - + + Rules that flag issues when dealing with multiple threads of execution. - \ No newline at end of file + diff --git a/pmd-swift/src/main/resources/category/swift/performance.xml b/pmd-swift/src/main/resources/category/swift/performance.xml index be669f985c..f791f2f69f 100644 --- a/pmd-swift/src/main/resources/category/swift/performance.xml +++ b/pmd-swift/src/main/resources/category/swift/performance.xml @@ -1,4 +1,4 @@ - + + ruleSetFileNames = getRuleSetFileNames(); + StringBuilder messages = new StringBuilder(); + for (String fileName : ruleSetFileNames) { + boolean valid = hasCorrectEncoding(fileName); + allValid = allValid && valid; + if (!valid) { + messages.append("RuleSet ") + .append(fileName) + .append(" is missing XML encoding or not using UTF8\n"); + } + } + assertTrue(allValid, "All XML must use correct XML encoding\n" + messages); + } + + public static boolean hasCorrectEncoding(String fileName) throws IOException { + try (InputStream inputStream = loadResourceAsStream(fileName)) { + // first bytes must be: + byte[] expectedBytes = "".getBytes(StandardCharsets.UTF_8); + byte[] bytes = new byte[expectedBytes.length]; + int count = inputStream.read(bytes); + if (count != expectedBytes.length || !Arrays.equals(expectedBytes, bytes)) { + return false; + } + } + return true; + } + /** * Verifies that all rulesets are valid XML according to the DTD. * @@ -381,8 +412,8 @@ public abstract class AbstractRuleSetFactoryTest { } } - private InputStream loadResourceAsStream(String resource) { - return getClass().getClassLoader().getResourceAsStream(resource); + private static InputStream loadResourceAsStream(String resource) { + return AbstractRuleSetFactoryTest.class.getClassLoader().getResourceAsStream(resource); } private void testRuleSet(String fileName) throws IOException, SAXException { diff --git a/pmd-visualforce/src/main/resources/category/vf/bestpractices.xml b/pmd-visualforce/src/main/resources/category/vf/bestpractices.xml index 58e7fd43a3..126b820901 100644 --- a/pmd-visualforce/src/main/resources/category/vf/bestpractices.xml +++ b/pmd-visualforce/src/main/resources/category/vf/bestpractices.xml @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Thu, 1 Jun 2023 17:35:43 +0200 Subject: [PATCH 149/347] Use explicit encoding in internal ruleset files --- .ci/files/all-regression-rules.xml | 2 +- .../src/main/resources/rulesets/internal/all-ecmascript.xml | 2 +- pmd-core/src/main/resources/rulesets/internal/all-java.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/33.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/34.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/35.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/36.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/37-jsp.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/37.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/38.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/39.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/40rc1.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/41.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/42.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/50.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/501.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/510.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/512.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/520.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/540.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/550.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/551.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/552.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/553.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/554.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/560.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/580.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/600.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6100.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6110.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6120.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6130.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6150.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6160.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6180.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/620.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6220.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6230.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6240.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6250.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6260.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6270.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6290.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/630.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6310.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6340.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6350.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6360.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6370.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/640.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6400.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6420.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6450.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6460.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/650.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6510.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/6520.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/660.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/670.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/680.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/690.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/700.xml | 2 +- 62 files changed, 62 insertions(+), 62 deletions(-) diff --git a/.ci/files/all-regression-rules.xml b/.ci/files/all-regression-rules.xml index 12fceb6fec..95a1423ca0 100644 --- a/.ci/files/all-regression-rules.xml +++ b/.ci/files/all-regression-rules.xml @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Fri, 2 Jun 2023 18:09:09 +0200 Subject: [PATCH 150/347] [doc] Add migration guide for PMD 7 --- docs/_data/sidebars/pmd_sidebar.yml | 3 + docs/pages/pmd/userdocs/migrating_to_pmd7.md | 98 ++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 docs/pages/pmd/userdocs/migrating_to_pmd7.md diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index 41ea0109ce..6f3a7abe63 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -34,6 +34,9 @@ entries: - title: User Documentation output: web, pdf folderitems: + - title: Migration Guide for PMD 7 + url: /pmd_userdocs_migrating_to_pmd7.html + output: web, pdf - title: Installation and basic CLI usage url: /pmd_userdocs_installation.html output: web, pdf diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md new file mode 100644 index 0000000000..82d8a4e1f5 --- /dev/null +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -0,0 +1,98 @@ +--- +title: Migration Guide for PMD 7 +tags: [pmd, userdocs] +summary: "Migrating to PMD 7 from PMD 6.x" +permalink: pmd_userdocs_migrating_to_pmd7.html +author: Andreas Dangel +--- + +## In general + +Update to the latest PMD 6.x version and try to fix deprecation warnings + +Deprecations in PMD 6: +* Properties: + StringProperty.named(...) -> PropertyFactory.stringProperty(...) + uiOrder is gone +* addViolation(data, node, ...) -> asCtx(data).addViolation(node, ...) +* deprecated cli params + -no-cache --> --no-cache + -failOnViolation --> --fail-on-violation + -reportfile --> --report-file + -language --> --use-version +* deprecated XPath attributes +WARNING: Use of deprecated attribute 'VariableDeclaratorId/@Image' by XPath rule 'VariableNaming' (in ruleset 'VariableNamingRule'), please use @Name instead + + + +## Use cases + +### I'm using only built-in rules + +check whether the ruleset/rules are still available, +have the same properties, etc. + +### I'm using custom rules + +make sure to have good test coverage for your custom rules first. +if XPath, need to migrate to XPath 2.0. if Java, some APIs/ASTs might have changed. + +### I've extended PMD with a custom language... + +### I've extended PMD with a custom feature... + +## Special topics + +### CLI Changes + +run.sh pmd -> pmd check + +Message: [main] ERROR net.sourceforge.pmd.cli.commands.internal.PmdCommand - No such file false +--> comes from "--fail-on-violation false" -> "--no-fail-on-violation" + +### Custom distribution packages +needs pmd-cli dependencies +needs cyclonedx plugin +additional config needed to include conf/simpelogger.properties + +### Migrate rulechain rules: + +* RuleChain API changes, see [core] Simplify the rulechain #2490 +* addRuleChainVisit() -> buildTargetSelector() + +### Rule tests + +Nice to have - not immediately required: +Should replace junit4 with junit5 +But both would work. + +### Endcolumn + +CPD: End Columns of Tokens are exclusive on PMD 7, +but inclusive on PMD 6.x. See 5b7ed58 + + +### XPath + +Differences between XPath 1.0 and 2.0 (focused on practicality, eg how to transition) + +This is already in place: [Writing XPath rules - Migrating from 1.0 to 2.0](pmd_userdocs_extending_writing_xpath_rules.html#migrating-from-10-to-20) +That means: The section migrating from XPath 1.0 -> XPath 2.0 should be moved to the migration guide PMD6->7 +since only XPath 2.0 (actually XPath 3.1) will be supported with PMD 7. + +Custom function "pmd:matches" has been removed, use the built in function from XPath 2.0+. + +### AST Navigation in general + +Methods like Node::getFirstChildOfType... use replacement either, NodeStream or in that case Node::firstChild(...). + +### Java AST +* See also [Java Clean Changes](https://github.com/pmd/pmd/wiki/Java_clean_changes) + +### Language versions + +For some languages, that previously hadn't any version, now there are versions, e.g. plsql. + +### Build Tools + +maven... From e342bfd7f8d23ea8cfba6dc3bf26c2d8943da36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 5 Jun 2023 19:49:26 -0300 Subject: [PATCH 151/347] Add failing use case for #4578 --- .../xml/CommentDefaultAccessModifier.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml index 6a42aa94f3..8ce22f0ebe 100755 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/CommentDefaultAccessModifier.xml @@ -564,6 +564,22 @@ class C { @AfterSuite void afterSuite() {} +} + ]]> + + + + #4578 failure with comment after annotation + 0 + From d1daf9e3fd061256191530fb7e8de23e35ac09af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 5 Jun 2023 19:49:53 -0300 Subject: [PATCH 152/347] Allow comment parsing to include comments after MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This is safe even for the last token - I don't love the semantics of thisโ€ฆ get leading comments vs token in, and actually allowing tokens "around"โ€ฆ a lot of this is for the AccessNode special case and what we consider "leading" to be for those --- .../net/sourceforge/pmd/lang/java/ast/JavaComment.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java index d88681e3ab..b4afdfbf77 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java @@ -132,8 +132,9 @@ public class JavaComment implements Reportable { return line.subSequence(subseqFrom, line.length()).trim(); } - private static Stream getSpecialCommentsIn(JjtreeNode node) { - return GenericToken.streamRange(node.getFirstToken(), node.getLastToken()) + private static Stream getSpecialTokensIn(JjtreeNode node) { + // Consider one more token to include also comments immediately after the node + return GenericToken.streamRange(node.getFirstToken(), node.getLastToken().getNext()) .flatMap(it -> IteratorUtil.toStream(GenericToken.previousSpecials(it).iterator())); } @@ -141,7 +142,7 @@ public class JavaComment implements Reportable { if (node instanceof AccessNode) { node = ((AccessNode) node).getModifiers(); } - return getSpecialCommentsIn(node).filter(JavaComment::isComment) + return getSpecialTokensIn(node).filter(JavaComment::isComment) .map(JavaComment::toComment); } From 1d6f0a43b53af29d200f049fa077a978562a9d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 5 Jun 2023 23:02:59 -0300 Subject: [PATCH 153/347] Be more explicit as to when we want to extend the range --- .../pmd/lang/java/ast/JavaComment.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java index b4afdfbf77..d2655ae5e0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaComment.java @@ -133,16 +133,26 @@ public class JavaComment implements Reportable { } private static Stream getSpecialTokensIn(JjtreeNode node) { - // Consider one more token to include also comments immediately after the node - return GenericToken.streamRange(node.getFirstToken(), node.getLastToken().getNext()) + return GenericToken.streamRange(node.getFirstToken(), node.getLastToken()) .flatMap(it -> IteratorUtil.toStream(GenericToken.previousSpecials(it).iterator())); } public static Stream getLeadingComments(JavaNode node) { + Stream specialTokens; + if (node instanceof AccessNode) { node = ((AccessNode) node).getModifiers(); + specialTokens = getSpecialTokensIn(node); + + // if this was a non-implicit empty modifier node, we should also consider comments immediately after + if (!node.getFirstToken().isImplicit()) { + specialTokens = Stream.concat(specialTokens, getSpecialTokensIn(node.getNextSibling())); + } + } else { + specialTokens = getSpecialTokensIn(node); } - return getSpecialTokensIn(node).filter(JavaComment::isComment) + + return specialTokens.filter(JavaComment::isComment) .map(JavaComment::toComment); } From e9ee4a94c601e3d490e6cfa4699b22ff4d9691a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 5 Jun 2023 23:05:07 -0300 Subject: [PATCH 154/347] Update changelog, refs #4578 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 3e25409b69..c76f9c9c0f 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -45,6 +45,8 @@ The remaining section describes the complete release notes for 7.0.0. * [#4582](https://github.com/pmd/pmd/issues/4582): \[dist] Download link broken * java * [#4401](https://github.com/pmd/pmd/issues/4401): \[java] PMD 7 fails to build under Java 19 +* java-codestyle + * [#4578](https://github.com/pmd/pmd/issues/4578): \[java] CommentDefaultAccessModifier comment needs to be before annotation if present #### API Changes From f2c7da364cf46c66d023657ab78dbb16989d0489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 5 Jun 2023 23:58:39 -0300 Subject: [PATCH 155/347] Expose XML Declaratoin as attributes --- .../lang/xml/ast/internal/XmlParserImpl.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java index ca39c5c118..b61ca15903 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java @@ -8,6 +8,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -22,6 +23,9 @@ import org.xml.sax.SAXException; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.rule.xpath.Attribute; +import net.sourceforge.pmd.lang.rule.xpath.NoAttribute; +import net.sourceforge.pmd.lang.rule.xpath.impl.AttributeAxisIterator; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.xml.ast.XmlNode; @@ -108,6 +112,31 @@ public final class XmlParserImpl { public Document getNode() { return (Document) super.getNode(); } + + public String getXmlEncoding() { + return getNode().getXmlEncoding(); + } + + public boolean isXmlStandalone() { + return getNode().getXmlStandalone(); + } + + public String getXmlVersion() { + return getNode().getXmlVersion(); + } + + // Hide the image attribute + @NoAttribute + @Override + public String getImage() { + return super.getImage(); + } + + @Override + public Iterator getXPathAttributesIterator() { + // Expose this node's attributes through reflection + return new AttributeAxisIterator(this); + } } } From 59fca83d1018c95d7d4f6a67cef3ce9c532f36d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 5 Jun 2023 23:59:10 -0300 Subject: [PATCH 156/347] Add a new rule for MissingEncoding - Enforce XML files have an encoding set in the XML Declaration --- .../resources/category/xml/bestpractices.xml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pmd-xml/src/main/resources/category/xml/bestpractices.xml b/pmd-xml/src/main/resources/category/xml/bestpractices.xml index 58e7fd43a3..09d54fd660 100644 --- a/pmd-xml/src/main/resources/category/xml/bestpractices.xml +++ b/pmd-xml/src/main/resources/category/xml/bestpractices.xml @@ -8,4 +8,31 @@ Rules which enforce generally accepted best practices. + + + + When the character encoding is missing from the XML declaration, + the parser may produce garbled text. + + This is completely dependent on how the parser is set up + and the content of the XML file, so it may be hard to reproduce. + + Providing an explicit encoding ensures accurate and consistent + parsing. + + 3 + + + + + + + + + From 34a3f792c9d14e118f60e16dc35de148348818a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 5 Jun 2023 23:59:41 -0300 Subject: [PATCH 157/347] Add test cases --- .../bestpractices/MissingEncodingTest.java | 11 ++++ .../bestpractices/xml/MissingEncoding.xml | 60 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/bestpractices/MissingEncodingTest.java create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/bestpractices/xml/MissingEncoding.xml diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/bestpractices/MissingEncodingTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/bestpractices/MissingEncodingTest.java new file mode 100644 index 0000000000..ba1fd2964b --- /dev/null +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/bestpractices/MissingEncodingTest.java @@ -0,0 +1,11 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.xml.rule.bestpractices; + +import net.sourceforge.pmd.testframework.PmdRuleTst; + +class MissingEncodingTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/bestpractices/xml/MissingEncoding.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/bestpractices/xml/MissingEncoding.xml new file mode 100644 index 0000000000..17660ac978 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/rule/bestpractices/xml/MissingEncoding.xml @@ -0,0 +1,60 @@ + + + + + No XML Declaration + 1 + + + + ]]> + + + + XML Declaration without encoding + 1 + + + + + ]]> + + + + XML Declaration with UTF-8 encoding + 0 + + + + + ]]> + + + + XML Declaration with ISO-8859-1 encoding + 0 + + + + + ]]> + + + + XML Declaration with UTF-8 encoding and standalone + 0 + + + + + ]]> + + From 7f66265c1c5c1b1df0fbde274bda1660c97ebaf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Tue, 6 Jun 2023 23:07:20 -0300 Subject: [PATCH 158/347] Update AST tests --- .../net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt | 2 +- .../net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt | 2 +- .../net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt index cbb8c4a578..21f24c5545 100644 --- a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt @@ -1,4 +1,4 @@ -+- document[] ++- document[@XmlEncoding = "UTF-8", @XmlStandalone = false, @XmlVersion = "1.0"] +- deployment-plan[@global-variables = "false", @xmlns = "http://xmlns.oracle.com/weblogic/deployment-plan", @xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance", @xsi:schemaLocation = "http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd"] +- text[@Image = "\n "] +- application-name[] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt index 79fe784f37..1765de537e 100644 --- a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt @@ -1,4 +1,4 @@ -+- document[] ++- document[@XmlEncoding = null, @XmlStandalone = false, @XmlVersion = "1.0"] +- comment[] +- rootElement[] +- rootElement[] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt index 40035dc0ef..df7007759b 100644 --- a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt @@ -1,4 +1,4 @@ -+- document[] ++- document[@XmlEncoding = null, @XmlStandalone = false, @XmlVersion = "1.0"] +- pmd:rootElement[@xmlns:pmd = "http://pmd.sf.net"] +- text[@Image = "\n "] +- comment[] From 3bac2315169527927b56a4af9420a63ddad3a1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Wed, 7 Jun 2023 00:51:15 -0300 Subject: [PATCH 159/347] Fix import order --- .../sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java index b61ca15903..e838b8bf89 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java @@ -10,6 +10,7 @@ import java.io.StringReader; import java.util.HashMap; import java.util.Iterator; import java.util.Map; + import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -23,10 +24,10 @@ import org.xml.sax.SAXException; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.rule.xpath.Attribute; import net.sourceforge.pmd.lang.rule.xpath.NoAttribute; import net.sourceforge.pmd.lang.rule.xpath.impl.AttributeAxisIterator; -import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.xml.ast.XmlNode; public final class XmlParserImpl { From febd279be212aa0faf19f119a069ab2456dc83bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Wed, 7 Jun 2023 10:10:12 -0300 Subject: [PATCH 160/347] Fix import order --- .../net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java index e838b8bf89..f307ed97af 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java @@ -10,7 +10,6 @@ import java.io.StringReader; import java.util.HashMap; import java.util.Iterator; import java.util.Map; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; From 5a55af46d35e74ba80a3e2015ad9f3c9d2026def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Thu, 8 Jun 2023 00:28:58 -0300 Subject: [PATCH 161/347] Change completion generation to runtime - We no longer ship a pre-built completion script - A hidden subcommand is available to generate it dynamically based on actually available languages. - We update docs everywhere accordingly. --- docs/pages/pmd/userdocs/installation.md | 2 +- docs/pages/release_notes_pmd7.md | 3 +- pmd-cli/pom.xml | 49 ------------------- .../java/net/sourceforge/pmd/cli/PmdCli.java | 12 +++-- .../cli/commands/internal/PmdRootCommand.java | 4 +- .../src/main/resources/assemblies/pmd-bin.xml | 13 ----- .../pmd/it/BinaryDistributionIT.java | 1 - .../java/rule/design/xml/LawOfDemeter.xml | 36 ++++++++++++++ 8 files changed, 49 insertions(+), 71 deletions(-) diff --git a/docs/pages/pmd/userdocs/installation.md b/docs/pages/pmd/userdocs/installation.md index eaa4115bce..da419f375d 100644 --- a/docs/pages/pmd/userdocs/installation.md +++ b/docs/pages/pmd/userdocs/installation.md @@ -47,7 +47,7 @@ On Windows this is achieved by: PMD ships with built-in completion support for Bash / Zsh. -To enable it, simply add `source *path_to_pmd*/shell/pmd-completion.sh` to your `~/.bashrc` / `~/.zshrc` file. +To enable it, simply add `source <(*path_to_pmd*/bin/pmd generate-completion)` to your `~/.bashrc` / `~/.zshrc` file. ## Running PMD via command line diff --git a/docs/pages/release_notes_pmd7.md b/docs/pages/release_notes_pmd7.md index 3a57818bf4..db850ce333 100644 --- a/docs/pages/release_notes_pmd7.md +++ b/docs/pages/release_notes_pmd7.md @@ -416,11 +416,10 @@ current progress of the analysis. This can be disabled with the `--no-progress` flag. Finally, we now provide a completion script for Bash/Zsh to further help daily usage. -This script can be found under `shell/pmd-completion.sh` in the binary distribution. To use it, edit your `~/.bashrc` / `~/.zshrc` file and add the following line: ``` -source *path_to_pmd*/shell/pmd-completion.sh +source <(*path_to_pmd*/bin/pmd generate-completion) ``` Contributors: [Juan Martรญn Sotuyo Dodero](https://github.com/jsotuyod) (@jsotuyod) diff --git a/pmd-cli/pom.xml b/pmd-cli/pom.xml index 2df635e272..a217e76abd 100644 --- a/pmd-cli/pom.xml +++ b/pmd-cli/pom.xml @@ -20,55 +20,6 @@ pmd-cli-checkstyle-suppressions.xml - - - org.codehaus.mojo - exec-maven-plugin - - - generate-autocompletion-script - package - - exec - - - - - java - - -Dpicocli.autocomplete.systemExitOnError - -cp - - picocli.AutoComplete - --force - --completionScript - ${project.build.directory}/pmd_completion.sh - net.sourceforge.pmd.cli.commands.internal.PmdRootCommand - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-completion-artifact - - attach-artifact - - - - - ${project.build.directory}/pmd_completion.sh - sh - completion - - - - - - diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/PmdCli.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/PmdCli.java index 1de2edc64a..1b246b86f1 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/PmdCli.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/PmdCli.java @@ -13,9 +13,13 @@ public final class PmdCli { private PmdCli() { } public static void main(String[] args) { - final int exitCode = new CommandLine(new PmdRootCommand()) - .setCaseInsensitiveEnumValuesAllowed(true) - .execute(args); - System.exit(exitCode); + final CommandLine cli = new CommandLine(new PmdRootCommand()) + .setCaseInsensitiveEnumValuesAllowed(true); + + // Don't show autocomplete subcommand in help by default + cli.getSubcommands().get("generate-completion") + .getCommandSpec().usageMessage().hidden(true); + + System.exit(cli.execute(args)); } } diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java index ef15d504fd..d596745bfc 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cli.commands.internal; import net.sourceforge.pmd.PMDVersion; +import picocli.AutoComplete.GenerateCompletion; import picocli.CommandLine.Command; import picocli.CommandLine.IVersionProvider; @@ -14,7 +15,8 @@ import picocli.CommandLine.IVersionProvider; exitCodeListHeading = "Exit Codes:%n", exitCodeList = { "0:Successful analysis, no violations found", "1:An unexpected error occurred during execution", "2:Usage error, please refer to the command help", "4:Successful analysis, at least 1 violation found" }, - subcommands = { PmdCommand.class, CpdCommand.class, DesignerCommand.class, CpdGuiCommand.class, TreeExportCommand.class }) + subcommands = { PmdCommand.class, CpdCommand.class, DesignerCommand.class, + CpdGuiCommand.class, TreeExportCommand.class, GenerateCompletion.class }) public class PmdRootCommand { } diff --git a/pmd-dist/src/main/resources/assemblies/pmd-bin.xml b/pmd-dist/src/main/resources/assemblies/pmd-bin.xml index a69cde3c66..f33bf93348 100644 --- a/pmd-dist/src/main/resources/assemblies/pmd-bin.xml +++ b/pmd-dist/src/main/resources/assemblies/pmd-bin.xml @@ -67,19 +67,6 @@ - - - runtime - - net.sourceforge.pmd:pmd-cli:sh:completion:* - - pmd-completion.sh - shell - 0755 - 0644 - false - - runtime diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java index 04f87ac700..bd863c44a3 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistributionIT.java @@ -85,7 +85,6 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest { result.add(basedir + "bin/pmd"); result.add(basedir + "bin/pmd.bat"); result.add(basedir + "conf/simplelogger.properties"); - result.add(basedir + "shell/pmd-completion.sh"); result.add(basedir + "lib/pmd-core-" + PMDVersion.VERSION + ".jar"); result.add(basedir + "lib/pmd-java-" + PMDVersion.VERSION + ".jar"); result.add(basedir + "sbom/pmd-" + PMDVersion.VERSION + "-cyclonedx.xml"); diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LawOfDemeter.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LawOfDemeter.xml index c977a3e509..6c6eceac33 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LawOfDemeter.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/LawOfDemeter.xml @@ -1222,6 +1222,42 @@ class LawOfDemeterFields { return null; } } +]]> + + + + sadasd conditional self assignment of fields + 2 + 12,18 + { + TreeNode root = (TreeNode) tree.getModel().getRoot(); // NOT report + visitAll(tree, new TreePath(root), true); + })); + box.add(new JButton(new AbstractAction("collapse") { + @Override + public void actionPerformed(ActionEvent e) { + TreeNode root = (TreeNode) tree.getModel().getRoot(); // report LawOfDemeter(method chain calls) + visitAll(tree, new TreePath(root), false); + } + })); + box.add(Box.createVerticalGlue()); + JPanel p = new JPanel(new BorderLayout()); + p.add(box, BorderLayout.EAST); + p.add(new JScrollPane(tree)); + return p; + } +} ]]> From 7062d795777b10ff77130cb389425d848e4bf277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Thu, 8 Jun 2023 10:13:46 -0300 Subject: [PATCH 162/347] Fix indentation --- .../sourceforge/pmd/cli/commands/internal/PmdRootCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java index d596745bfc..169690f98a 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/PmdRootCommand.java @@ -16,7 +16,7 @@ import picocli.CommandLine.IVersionProvider; exitCodeList = { "0:Successful analysis, no violations found", "1:An unexpected error occurred during execution", "2:Usage error, please refer to the command help", "4:Successful analysis, at least 1 violation found" }, subcommands = { PmdCommand.class, CpdCommand.class, DesignerCommand.class, - CpdGuiCommand.class, TreeExportCommand.class, GenerateCompletion.class }) + CpdGuiCommand.class, TreeExportCommand.class, GenerateCompletion.class }) public class PmdRootCommand { } From 2fc4cb9929a396305bccaa466705ec56528bba4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Fri, 9 Jun 2023 11:42:54 -0300 Subject: [PATCH 163/347] Remove completion dependency from dist --- pmd-dist/pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 9e824be8a3..44c745feff 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -132,14 +132,6 @@ pmd-cli ${project.version} - - - net.sourceforge.pmd - pmd-cli - ${project.version} - sh - completion - net.sourceforge.pmd pmd-ant From d0817da19c8c63adda61b1f2b0232231f62f8cee Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 9 Jun 2023 20:44:21 +0200 Subject: [PATCH 164/347] Update migration guide --- docs/pages/pmd/userdocs/migrating_to_pmd7.md | 55 ++++++++++++++------ docs/pages/release_notes_pmd7.md | 2 +- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md index 82d8a4e1f5..9f8d69e664 100644 --- a/docs/pages/pmd/userdocs/migrating_to_pmd7.md +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -6,31 +6,54 @@ permalink: pmd_userdocs_migrating_to_pmd7.html author: Andreas Dangel --- -## In general +## Before you update -Update to the latest PMD 6.x version and try to fix deprecation warnings +Before updating to PMD 7, you should first update to the latest PMD 6 version 6.55.0 and try to fix all +deprecation warnings. -Deprecations in PMD 6: -* Properties: - StringProperty.named(...) -> PropertyFactory.stringProperty(...) - uiOrder is gone -* addViolation(data, node, ...) -> asCtx(data).addViolation(node, ...) -* deprecated cli params - -no-cache --> --no-cache - -failOnViolation --> --fail-on-violation - -reportfile --> --report-file - -language --> --use-version -* deprecated XPath attributes -WARNING: Use of deprecated attribute 'VariableDeclaratorId/@Image' by XPath rule 'VariableNaming' (in ruleset 'VariableNamingRule'), please use @Name instead +There are a couple of deprecated things in PMD 6, you might encounter: +* Properties: In order to define property descriptors, you should use {% jdoc core::properties.PropertyFactory %} now. + This factory can create properties of any type. E.g. instead of `StringProperty.named(...)` use + `PropertyFactory.stringProperty(...)`. + Also note, that `uiOrder` is gone. You can just remove it. + + See also [Defining rule properties](pmd_userdocs_extending_defining_properties.html) + +* When reporting a violation, you might see a deprecation of the `addViolation` methods. These methods have been moved + to {% jdoc core::RuleContext %}. E.g. instead of `addViolation(data, node, ...)` use `asCtx(data).addViolation(node, ...)`. + +* When you are calling PMD from CLI, you need to stop using deprecated CLI params, e.g. + * `-no-cache` --> `--no-cache` + * `-failOnViolation` --> `--fail-on-violation` + * `-reportfile` --> `--report-file` + * `-language` --> `--use-version` + +* If you have written custom XPath rule, look out for warning about deprecated XPath attributes. These warnings + might look like + ``` + WARNING: Use of deprecated attribute 'VariableDeclaratorId/@Image' by XPath rule 'VariableNaming' (in ruleset 'VariableNamingRule'), please use @Name instead + ``` + and often suggest already an alternative. ## Use cases ### I'm using only built-in rules -check whether the ruleset/rules are still available, -have the same properties, etc. +When you are using only built-in rules, then you should check, whether you use any deprecated rule. With PMD 7 +many deprecated rules are finally removed. You can see a complete list of the [removed rules](pmd_release_notes_pmd7.html#removed-rules) +in the release notes for PMD 7. +The release notes also mention the replacement rule, that should be used instead. For some rules, there is no +replacement. + +Then many rules have been changed or improved. New properties have been added to make the further configurable or +properties have been removed, if they are not necessary anymore. See [changed rules](pmd_release_notes_pmd7.html#changed-rules) +in the release notes for PMD 7. + +A handful rules are new with PMD 7. You might want to check these out: [new rules](pmd_release_notes_pmd7.html#new-rules). + +Once you have reviewed your ruleset(s), you can switch to PMD 7. ### I'm using custom rules diff --git a/docs/pages/release_notes_pmd7.md b/docs/pages/release_notes_pmd7.md index 3a57818bf4..809a1fbd51 100644 --- a/docs/pages/release_notes_pmd7.md +++ b/docs/pages/release_notes_pmd7.md @@ -679,7 +679,7 @@ Related issue: [[core] Explicitly name all language versions (#4120)](https://gi ### Deprecated Rules -In PMD 7.0.0, there are now deprecated rules. +In PMD 7.0.0, there are no deprecated rules. ### Removed Rules From 9b1431df7d53edc9f512c1c14577016d8adc95a1 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 10 Jun 2023 11:45:27 +0200 Subject: [PATCH 165/347] Update migration guide (xpath rules) --- .../userdocs/extending/writing_xpath_rules.md | 68 +++---------- docs/pages/pmd/userdocs/migrating_to_pmd7.md | 97 +++++++++++++++---- 2 files changed, 87 insertions(+), 78 deletions(-) diff --git a/docs/pages/pmd/userdocs/extending/writing_xpath_rules.md b/docs/pages/pmd/userdocs/extending/writing_xpath_rules.md index cff0a747de..23e0c5be0c 100644 --- a/docs/pages/pmd/userdocs/extending/writing_xpath_rules.md +++ b/docs/pages/pmd/userdocs/extending/writing_xpath_rules.md @@ -44,69 +44,23 @@ defined on. Concretely, this means: To represent attributes, we must map Java values to [XPath Data Model (XDM)](https://www.w3.org/TR/xpath-datamodel/) values. In the following table we refer to the type conversion function as `conv`, a function from Java types to XDM types. -| Java type `T` | XSD type `conv(T)` -|-----------------|---------------------| -|`int` | `xs:integer` -|`long` | `xs:integer` -|`double` | `xs:decimal` -|`float` | `xs:decimal` -|`boolean` | `xs:boolean` -|`String` | `xs:string` -|`Character` | `xs:string` -|`Enum` | `xs:string` (uses `Object::toString`) -|`List` | `conv(E)*` (a sequence type) - +| Java type `T` | XSD type `conv(T)` | +|---------------|---------------------------------------| +| `int` | `xs:integer` | +| `long` | `xs:integer` | +| `double` | `xs:decimal` | +| `float` | `xs:decimal` | +| `boolean` | `xs:boolean` | +| `String` | `xs:string` | +| `Character` | `xs:string` | +| `Enum` | `xs:string` (uses `Object::toString`) | +| `List` | `conv(E)*` (a sequence type) | The same `conv` function is used to translate rule property values to XDM values. {% include warning.html content="Lists are only supported for rule properties, not attributes." %} -## Migrating from 1.0 to 2.0 - - - -XPath 1.0 and 2.0 have some incompatibilities. The [XPath 2.0 specification](https://www.w3.org/TR/xpath20/#id-incompat-in-false-mode) -describes them precisely. Those are however mostly corner cases and XPath -rules usually don't feature any of them. - -The incompatibilities that are most relevant to migrating your rules are not -caused by the specification, but by the different engines we use to run -XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: - -* The namespace prefixes `fn:` and `string:` should not be mentioned explicitly. -In XPath 2.0 mode, the engine will complain about an undeclared namespace, but -the functions are in the default namespace. Removing the namespace prefixes fixes it. - * fn:substring("Foo", 1) → `substring("Foo", 1)` -* Conversely, calls to custom PMD functions like `typeIs` *must* be prefixed -with the namespace of the declaring module (`pmd-java`). - * `typeIs("Foo")` → pmd-java:typeIs("Foo") -* Boolean attribute values on our 1.0 engine are represented as the string values -`"true"` and `"false"`. In 2.0 mode though, boolean values are truly represented -as boolean values, which in XPath may only be obtained through the functions -`true()` and `false()`. -If your XPath 1.0 rule tests an attribute like `@Private="true"`, then it just -needs to be changed to `@Private=true()` when migrating. A type error will warn -you that you must update the comparison. More is explained on [issue #1244](https://github.com/pmd/pmd/issues/1244). - * `"true"`, `'true'` → `true()` - * `"false"`, `'false'` → `false()` - -* In XPath 1.0, comparing a number to a string coerces the string to a number. -In XPath 2.0, a type error occurs. Like for boolean values, numeric values are -represented by our 1.0 implementation as strings, meaning that `@BeginLine > "1"` -worked ---that's not the case in 2.0 mode. - * @ArgumentCount > '1' → `@ArgumentCount > 1` - -* In XPath 1.0, the expression `/Foo` matches the *children* of the root named `Foo`. -In XPath 2.0, that expression matches the root, if it is named `Foo`. Consider the following tree: -```java -Foo -โ””โ”€ Foo -โ””โ”€ Foo -``` -Then `/Foo` will match the root in XPath 2, and the other nodes (but not the root) in XPath 1. -See eg [an issue caused by this](https://github.com/pmd/pmd/issues/1919#issuecomment-512865434) in Apex, -with nested classes. ## Rule properties diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md index 9f8d69e664..85867b6371 100644 --- a/docs/pages/pmd/userdocs/migrating_to_pmd7.md +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -25,10 +25,10 @@ There are a couple of deprecated things in PMD 6, you might encounter: to {% jdoc core::RuleContext %}. E.g. instead of `addViolation(data, node, ...)` use `asCtx(data).addViolation(node, ...)`. * When you are calling PMD from CLI, you need to stop using deprecated CLI params, e.g. - * `-no-cache` --> `--no-cache` - * `-failOnViolation` --> `--fail-on-violation` - * `-reportfile` --> `--report-file` - * `-language` --> `--use-version` + * `-no-cache` โžก๏ธ `--no-cache` + * `-failOnViolation` โžก๏ธ `--fail-on-violation` + * `-reportfile` โžก๏ธ `--report-file` + * `-language` โžก๏ธ `--use-version` * If you have written custom XPath rule, look out for warning about deprecated XPath attributes. These warnings might look like @@ -57,8 +57,31 @@ Once you have reviewed your ruleset(s), you can switch to PMD 7. ### I'm using custom rules -make sure to have good test coverage for your custom rules first. -if XPath, need to migrate to XPath 2.0. if Java, some APIs/ASTs might have changed. +Ideally, you have written good tests already for your custom rules - see [Testing your rules](pmd_userdocs_extending_testing.html). +This helps to identify problems early on. + +If you have **XPath based** rules, the first step will be to migrate to XPath 2.0, which is available in PMD 6 already. +With PMD 7, XPath 1.0 won't be supported anymore and the default XPath version is actually 3.1. But the difference +from XPath 2.0 and XPath 3.1 is not big. So the migration path is to simply migrate to XPath 2.0. +After you have migrated your XPath rules to XPath 2.0, remove the "version" property, since that will be removed +with PMD 7. PMD 7 by default uses XPath 3.1. +See below [XPath](#xpath-migrating-from-10-to-20) for details. + +If you have **Java based rules**, and you are using rulechain, this works a bit different now. The RuleChain API +has changed, see [\[core] Simplify the rulechain #2490](https://github.com/pmd/pmd/pull/2490) for the full details. +But in short, you don't call `addRuleChainVisit(...)` in the rule's constructor anymore. Instead, you +override the method {% jdoc core::lang.rule.AbstractRule#buildTargetSelector %}: + +```java + protected RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTVariableDeclaratorId.class); + } +``` + +Additionally, if you have created rules for **Java** - regardless whether it is a XPath based rule or a Java based +rule - you might need to adjust your queries or visitor methods. The Java AST has been refactored substantially. +The easiest way is to use the [PMD Rule Designer](pmd_userdocs_extending_designer_reference.html) to see the structure +of the AST. See the section [Java AST](#java-ast) below for details. ### I've extended PMD with a custom language... @@ -78,10 +101,6 @@ needs pmd-cli dependencies needs cyclonedx plugin additional config needed to include conf/simpelogger.properties -### Migrate rulechain rules: - -* RuleChain API changes, see [core] Simplify the rulechain #2490 -* addRuleChainVisit() -> buildTargetSelector() ### Rule tests @@ -94,21 +113,57 @@ But both would work. CPD: End Columns of Tokens are exclusive on PMD 7, but inclusive on PMD 6.x. See 5b7ed58 - -### XPath - -Differences between XPath 1.0 and 2.0 (focused on practicality, eg how to transition) - -This is already in place: [Writing XPath rules - Migrating from 1.0 to 2.0](pmd_userdocs_extending_writing_xpath_rules.html#migrating-from-10-to-20) -That means: The section migrating from XPath 1.0 -> XPath 2.0 should be moved to the migration guide PMD6->7 -since only XPath 2.0 (actually XPath 3.1) will be supported with PMD 7. - -Custom function "pmd:matches" has been removed, use the built in function from XPath 2.0+. - ### AST Navigation in general Methods like Node::getFirstChildOfType... use replacement either, NodeStream or in that case Node::firstChild(...). +### XPath: Migrating from 1.0 to 2.0 + +XPath 1.0 and 2.0 have some incompatibilities. The [XPath 2.0 specification](https://www.w3.org/TR/xpath20/#id-incompat-in-false-mode) +describes them precisely. Those are however mostly corner cases and XPath +rules usually don't feature any of them. + +The incompatibilities that are most relevant to migrating your rules are not +caused by the specification, but by the different engines we use to run +XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: + +* The namespace prefixes `fn:` and `string:` should not be mentioned explicitly. + In XPath 2.0 mode, the engine will complain about an undeclared namespace, but + the functions are in the default namespace. Removing the namespace prefixes fixes it. + * fn:substring("Foo", 1) → `substring("Foo", 1)` +* Conversely, calls to custom PMD functions like `typeIs` *must* be prefixed + with the namespace of the declaring module (`pmd-java`). + * `typeIs("Foo")` → pmd-java:typeIs("Foo") +* Boolean attribute values on our 1.0 engine are represented as the string values + `"true"` and `"false"`. In 2.0 mode though, boolean values are truly represented + as boolean values, which in XPath may only be obtained through the functions + `true()` and `false()`. + If your XPath 1.0 rule tests an attribute like `@Private="true"`, then it just + needs to be changed to `@Private=true()` when migrating. A type error will warn + you that you must update the comparison. More is explained on [issue #1244](https://github.com/pmd/pmd/issues/1244). + * `"true"`, `'true'` → `true()` + * `"false"`, `'false'` → `false()` + +* In XPath 1.0, comparing a number to a string coerces the string to a number. + In XPath 2.0, a type error occurs. Like for boolean values, numeric values are + represented by our 1.0 implementation as strings, meaning that `@BeginLine > "1"` + worked ---that's not the case in 2.0 mode. + * @ArgumentCount > '1' → `@ArgumentCount > 1` + +* In XPath 1.0, the expression `/Foo` matches the *children* of the root named `Foo`. + In XPath 2.0, that expression matches the root, if it is named `Foo`. Consider the following tree: + ```java + Foo + โ””โ”€ Foo + โ””โ”€ Foo + ``` + Then `/Foo` will match the root in XPath 2.0, and the other nodes (but not the root) in XPath 1.0. + See e.g. [an issue caused by this](https://github.com/pmd/pmd/issues/1919#issuecomment-512865434) in Apex, + with nested classes. + +* The custom function "pmd:matches" has been removed, since there is a built-in function available since XPath 2.0 + which can be used instead. + ### Java AST * See also [Java Clean Changes](https://github.com/pmd/pmd/wiki/Java_clean_changes) From 4ef43e961777cf5da1593324dd695fa537576cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 10 Jun 2023 14:12:02 +0200 Subject: [PATCH 166/347] Fixups --- .../src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java | 2 -- pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 77a52f9d77..9f8695cece 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -8,7 +8,6 @@ import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.nio.charset.Charset; -import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -35,7 +34,6 @@ public class CPDConfiguration extends AbstractConfiguration { public static final String DEFAULT_RENDERER = "text"; private static final Map> RENDERERS = new HashMap<>(); - protected Path reportFile; static { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index f3285027e2..9e660fb110 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -85,6 +85,7 @@ public final class CpdAnalysis implements AutoCloseable { setPropertyIfMissing(Tokenizer.CPD_IGNORE_METADATA, props, configuration.isIgnoreAnnotations()); setPropertyIfMissing(Tokenizer.CPD_IGNORE_IMPORTS, props, configuration.isIgnoreUsings()); setPropertyIfMissing(Tokenizer.CPD_IGNORE_LITERAL_SEQUENCES, props, configuration.isIgnoreLiteralSequences()); + setPropertyIfMissing(Tokenizer.CPD_IGNORE_LITERAL_AND_IDENTIFIER_SEQUENCES, props, configuration.isIgnoreIdentifierAndLiteralSequences()); if (!configuration.isNoSkipBlocks()) { PropertyDescriptor skipBlocks = (PropertyDescriptor) props.getPropertyDescriptor("cpdSkipBlocksPattern"); setPropertyIfMissing(skipBlocks, props, configuration.getSkipBlocksPattern()); From 629e3b415ce097b8c7826b27e576daa4d31096bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 10 Jun 2023 14:17:24 +0200 Subject: [PATCH 167/347] Consolidate CPD packages --- .../java/net/sourceforge/pmd/ant/CPDTask.java | 2 +- .../sourceforge/pmd/cpd/CPDConfiguration.java | 1 - .../net/sourceforge/pmd/cpd/CPDReport.java | 1 - .../cpd/{renderer => }/CPDReportRenderer.java | 10 ++------- .../net/sourceforge/pmd/cpd/CSVRenderer.java | 1 - .../net/sourceforge/pmd/cpd/CpdAnalysis.java | 2 -- .../java/net/sourceforge/pmd/cpd/GUI.java | 1 - .../sourceforge/pmd/cpd/SimpleRenderer.java | 1 - .../java/net/sourceforge/pmd/cpd/Tokens.java | 6 +++--- .../net/sourceforge/pmd/cpd/VSRenderer.java | 1 - .../net/sourceforge/pmd/cpd/XMLRenderer.java | 1 - .../cpd/{token => impl}/AntlrTokenFilter.java | 5 ++--- .../internal => impl}/BaseTokenFilter.java | 7 +++---- .../{token => impl}/JavaCCTokenFilter.java | 5 ++--- .../pmd/cpd/impl/JavaCCTokenizer.java | 4 ++++ .../pmd/cpd/impl/TokenizerBase.java | 4 +++- .../pmd/cpd/impl/package-info.java | 8 +++++++ .../net/sourceforge/pmd/cpd/package-info.java | 11 ++++++++++ .../pmd/cpd/token/TokenFilter.java | 21 ------------------- .../pmd/cpd/CPDConfigurationTest.java | 2 -- .../sourceforge/pmd/cpd/CSVRendererTest.java | 1 - .../sourceforge/pmd/cpd/XMLRendererTest.java | 1 - .../BaseTokenFilterTest.java | 4 ++-- .../net/sourceforge/pmd/cpd/CPPTokenizer.java | 2 +- .../pmd/lang/cs/cpd/CsTokenizer.java | 4 ++-- .../pmd/lang/dart/cpd/DartTokenizer.java | 4 ++-- .../pmd/lang/java/cpd/JavaTokenizer.java | 2 +- .../pmd/lang/kotlin/cpd/KotlinTokenizer.java | 2 +- .../pmd/lang/lua/cpd/LuaTokenizer.java | 2 +- .../lang/modelica/ModelicaLanguageModule.java | 2 +- .../modelica}/cpd/ModelicaTokenizer.java | 6 +++--- .../sourceforge/pmd/cpd/ScalaTokenizer.java | 2 +- 32 files changed, 54 insertions(+), 72 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/cpd/{renderer => }/CPDReportRenderer.java (79%) rename pmd-core/src/main/java/net/sourceforge/pmd/cpd/{token => impl}/AntlrTokenFilter.java (86%) rename pmd-core/src/main/java/net/sourceforge/pmd/cpd/{token/internal => impl}/BaseTokenFilter.java (97%) rename pmd-core/src/main/java/net/sourceforge/pmd/cpd/{token => impl}/JavaCCTokenFilter.java (86%) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/package-info.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/package-info.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java rename pmd-core/src/test/java/net/sourceforge/pmd/cpd/{token/internal => impl}/BaseTokenFilterTest.java (99%) rename pmd-modelica/src/main/java/net/sourceforge/pmd/{ => lang/modelica}/cpd/ModelicaTokenizer.java (95%) diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index 02d80ca6b3..693dd20101 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -25,12 +25,12 @@ import org.apache.tools.ant.types.FileSet; import net.sourceforge.pmd.cpd.CPDConfiguration; import net.sourceforge.pmd.cpd.CPDReport; +import net.sourceforge.pmd.cpd.CPDReportRenderer; import net.sourceforge.pmd.cpd.CSVRenderer; import net.sourceforge.pmd.cpd.CpdAnalysis; import net.sourceforge.pmd.cpd.SimpleRenderer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.XMLRenderer; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 9f8695cece..e2089fee2c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -19,7 +19,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.AbstractConfiguration; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java index a0c0251773..30af2fcdba 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReport.java @@ -12,7 +12,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import net.sourceforge.pmd.annotation.Experimental; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileId; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDReportRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReportRenderer.java similarity index 79% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDReportRenderer.java rename to pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReportRenderer.java index 692c610939..c6a10bea54 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/renderer/CPDReportRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDReportRenderer.java @@ -1,19 +1,13 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.renderer; +package net.sourceforge.pmd.cpd; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -import net.sourceforge.pmd.cpd.CPDReport; -import net.sourceforge.pmd.cpd.CSVRenderer; -import net.sourceforge.pmd.cpd.SimpleRenderer; -import net.sourceforge.pmd.cpd.VSRenderer; -import net.sourceforge.pmd.cpd.XMLRenderer; - /** * Render a {@link CPDReport} to a file. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java index 9b6a9215b0..7ff1a53562 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CSVRenderer.java @@ -10,7 +10,6 @@ import java.util.Iterator; import org.apache.commons.lang3.StringEscapeUtils; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.FileLocation; /** diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 9e660fb110..89898e7ec2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -18,7 +18,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.internal.util.FileCollectionUtil; import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.lang.Language; @@ -167,7 +166,6 @@ public final class CpdAnalysis implements AutoCloseable { consumer.accept(cpdReport); } catch (Exception e) { - e.printStackTrace(); reporter.errorEx("Exception while running CPD", e); } // source manager is closed and closes all text files now. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 20ca74bb11..d2d6705d64 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -61,7 +61,6 @@ import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import net.sourceforge.pmd.PMDVersion; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata; import net.sourceforge.pmd.lang.LanguagePropertyBundle; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java index 59f05e5184..d7d2e6e229 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/SimpleRenderer.java @@ -9,7 +9,6 @@ import java.io.PrintWriter; import java.io.Writer; import java.util.Iterator; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.util.StringUtil; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index da7a152868..30291d3213 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -35,11 +35,11 @@ public class Tokens { this.tokens.add(tokenEntry); } - void addEof(FileId filePathId, int line, int column) { + private void addEof(FileId filePathId, int line, int column) { add(new TokenEntry(filePathId, line, column)); } - void setImage(TokenEntry entry, String newImage) { + private void setImage(TokenEntry entry, String newImage) { int i = getImageId(newImage); entry.setImageIdentifier(i); } @@ -52,7 +52,7 @@ public class Tokens { return images.entrySet().stream().filter(it -> it.getValue() == i).findFirst().map(Entry::getKey).orElse(null); } - TokenEntry peekLastToken() { + private TokenEntry peekLastToken() { return tokens.isEmpty() ? null : getToken(size() - 1); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java index bf61cfff43..58298785ff 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/VSRenderer.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.cpd; import java.io.IOException; import java.io.Writer; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.FileLocation; public class VSRenderer implements CPDReportRenderer { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java index b42fbb9e9b..5365d1827a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/XMLRenderer.java @@ -20,7 +20,6 @@ import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.FileLocation; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenFilter.java similarity index 86% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java rename to pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenFilter.java index 4de4c5bc82..665ea9a58c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/AntlrTokenFilter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/AntlrTokenFilter.java @@ -1,10 +1,9 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.token; +package net.sourceforge.pmd.cpd.impl; -import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/BaseTokenFilter.java similarity index 97% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java rename to pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/BaseTokenFilter.java index d4d6e7c90b..22598140de 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/BaseTokenFilter.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.token.internal; +package net.sourceforge.pmd.cpd.impl; import static net.sourceforge.pmd.util.IteratorUtil.AbstractIterator; @@ -10,7 +10,6 @@ import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedList; -import net.sourceforge.pmd.cpd.token.TokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.GenericToken; @@ -18,7 +17,7 @@ import net.sourceforge.pmd.lang.ast.GenericToken; * A generic filter for PMD token managers that allows to use comments * to enable / disable analysis of parts of the stream */ -public class BaseTokenFilter> implements TokenFilter { +public class BaseTokenFilter> implements TokenManager { private final TokenManager tokenManager; private final LinkedList unprocessedTokens; // NOPMD - used both as Queue and List diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/JavaCCTokenFilter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenFilter.java similarity index 86% rename from pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/JavaCCTokenFilter.java rename to pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenFilter.java index b4683347df..3c8c6bb837 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/JavaCCTokenFilter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenFilter.java @@ -1,10 +1,9 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.token; +package net.sourceforge.pmd.cpd.impl; -import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenizer.java index e6feb5bed9..319d9dd0f3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/JavaCCTokenizer.java @@ -4,8 +4,12 @@ package net.sourceforge.pmd.cpd.impl; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; +/** + * Base class for a {@link Tokenizer} for a language implemented by a JavaCC tokenizer. + */ public abstract class JavaCCTokenizer extends TokenizerBase { } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java index 69a3a99117..e43627bba7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/TokenizerBase.java @@ -8,11 +8,13 @@ import java.io.IOException; import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; -import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.GenericToken; import net.sourceforge.pmd.lang.document.TextDocument; +/** + * Generic base class for a {@link Tokenizer}. + */ public abstract class TokenizerBase> implements Tokenizer { protected abstract TokenManager makeLexerImpl(TextDocument doc) throws IOException; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/package-info.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/package-info.java new file mode 100644 index 0000000000..7e2f66591f --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/impl/package-info.java @@ -0,0 +1,8 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/** + * Utilities to implement a CPD {@link net.sourceforge.pmd.cpd.Tokenizer}. + */ +package net.sourceforge.pmd.cpd.impl; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/package-info.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/package-info.java new file mode 100644 index 0000000000..ecb7637667 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/package-info.java @@ -0,0 +1,11 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +/** + * Token-based copy-paste detection. + * + * @see net.sourceforge.pmd.cpd.CpdAnalysis + * @see net.sourceforge.pmd.cpd.Tokenizer + */ +package net.sourceforge.pmd.cpd; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java deleted file mode 100644 index 469b33d89f..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/token/TokenFilter.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.cpd.token; - -import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.GenericToken; - -/** - * Defines filter to be applied to the token stream during CPD analysis - */ -public interface TokenFilter> extends TokenManager { - - /** - * Retrieves the next token to pass the filter - * @return The next token to pass the filter, or null if the end of the stream was reached - */ - @Override - T getNextToken(); -} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java index 68427f6781..defad177a5 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDConfigurationTest.java @@ -16,8 +16,6 @@ import java.util.Map; import org.junit.jupiter.api.Test; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; - class CPDConfigurationTest { @Test diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java index 8f253917f5..8ee85e12e0 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CSVRendererTest.java @@ -12,7 +12,6 @@ import java.io.StringWriter; import org.junit.jupiter.api.Test; import net.sourceforge.pmd.cpd.CpdTestUtils.CpdReportBuilder; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.FileId; class CSVRendererTest { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java index eb48c85f2a..eb96c32af9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/XMLRendererTest.java @@ -24,7 +24,6 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import net.sourceforge.pmd.cpd.CpdTestUtils.CpdReportBuilder; -import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer; import net.sourceforge.pmd.lang.document.FileId; /** diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/impl/BaseTokenFilterTest.java similarity index 99% rename from pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java rename to pmd-core/src/test/java/net/sourceforge/pmd/cpd/impl/BaseTokenFilterTest.java index 67489f0ae7..e295ba9e98 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/token/internal/BaseTokenFilterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/impl/BaseTokenFilterTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd.token.internal; +package net.sourceforge.pmd.cpd.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java index 5e43f4c2fc..0a917bde1f 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java @@ -8,8 +8,8 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenFilter; import net.sourceforge.pmd.cpd.impl.TokenizerBase; -import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java index 1719019e4c..c126ce274e 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/lang/cs/cpd/CsTokenizer.java @@ -8,9 +8,9 @@ import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenFilter; import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; -import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; -import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; +import net.sourceforge.pmd.cpd.impl.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; diff --git a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java index 2daf6ec443..9ac4a74b71 100644 --- a/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java +++ b/pmd-dart/src/main/java/net/sourceforge/pmd/lang/dart/cpd/DartTokenizer.java @@ -7,9 +7,9 @@ package net.sourceforge.pmd.lang.dart.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenFilter; import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; -import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; -import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; +import net.sourceforge.pmd.cpd.impl.BaseTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; import net.sourceforge.pmd.lang.dart.ast.DartLexer; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java index ab91910c0c..c945292e52 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/cpd/JavaTokenizer.java @@ -10,8 +10,8 @@ import java.util.LinkedList; import net.sourceforge.pmd.cpd.TokenEntry; import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenFilter; import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; -import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java index c46c8be666..149f7c5940 100644 --- a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/cpd/KotlinTokenizer.java @@ -7,8 +7,8 @@ package net.sourceforge.pmd.lang.kotlin.cpd; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenFilter; import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; -import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; import net.sourceforge.pmd.lang.kotlin.ast.KotlinLexer; diff --git a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java index 6d7942247c..8f1318d84c 100644 --- a/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java +++ b/pmd-lua/src/main/java/net/sourceforge/pmd/lang/lua/cpd/LuaTokenizer.java @@ -8,8 +8,8 @@ import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.cpd.impl.AntlrTokenFilter; import net.sourceforge.pmd.cpd.impl.AntlrTokenizer; -import net.sourceforge.pmd.cpd.token.AntlrTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken; diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java index 7fbaefc322..974d6bf95b 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaLanguageModule.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.lang.modelica; -import net.sourceforge.pmd.cpd.ModelicaTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; +import net.sourceforge.pmd.lang.modelica.cpd.ModelicaTokenizer; public class ModelicaLanguageModule extends SimpleLanguageModuleBase { public static final String NAME = "Modelica"; diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/cpd/ModelicaTokenizer.java similarity index 95% rename from pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java rename to pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/cpd/ModelicaTokenizer.java index a51a8518b4..83d3d3ad9b 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/cpd/ModelicaTokenizer.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/cpd/ModelicaTokenizer.java @@ -1,11 +1,11 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.modelica.cpd; +import net.sourceforge.pmd.cpd.impl.JavaCCTokenFilter; import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; -import net.sourceforge.pmd.cpd.token.JavaCCTokenFilter; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 79b59833a5..d2be64ec60 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.cpd; import org.apache.commons.lang3.StringUtils; -import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; +import net.sourceforge.pmd.cpd.impl.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.TokenManager; From 287a9a275ce9d36b6b81349f6a49ec62568b1fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 10 Jun 2023 14:32:47 +0200 Subject: [PATCH 168/347] Move forgotten things into language specific packages --- .../sourceforge/pmd/lang/cpp/CppLanguageModule.java | 2 +- .../pmd/{ => lang/cpp}/cpd/CPPTokenizer.java | 3 ++- .../pmd/{ => lang/cpp}/cpd/CppBlockSkipper.java | 2 +- .../pmd/{ => lang/cpp}/cpd/CppEscapeTranslator.java | 2 +- .../pmd/{ => lang/cpp}/cpd/CPPTokenizerTest.java | 11 ++++------- .../pmd/{ => lang/cpp}/cpd/CppCharStreamTest.java | 2 +- .../pmd/lang/plsql/PLSQLLanguageModule.java | 2 +- .../pmd/{ => lang/plsql}/cpd/PLSQLTokenizer.java | 3 ++- .../pmd/{ => lang/plsql}/cpd/PLSQLTokenizerTest.java | 10 ++-------- 9 files changed, 15 insertions(+), 22 deletions(-) rename pmd-cpp/src/main/java/net/sourceforge/pmd/{ => lang/cpp}/cpd/CPPTokenizer.java (98%) rename pmd-cpp/src/main/java/net/sourceforge/pmd/{ => lang/cpp}/cpd/CppBlockSkipper.java (96%) rename pmd-cpp/src/main/java/net/sourceforge/pmd/{ => lang/cpp}/cpd/CppEscapeTranslator.java (96%) rename pmd-cpp/src/test/java/net/sourceforge/pmd/{ => lang/cpp}/cpd/CPPTokenizerTest.java (97%) rename pmd-cpp/src/test/java/net/sourceforge/pmd/{ => lang/cpp}/cpd/CppCharStreamTest.java (97%) rename pmd-plsql/src/main/java/net/sourceforge/pmd/{ => lang/plsql}/cpd/PLSQLTokenizer.java (96%) rename pmd-plsql/src/test/java/net/sourceforge/pmd/{ => lang/plsql}/cpd/PLSQLTokenizerTest.java (80%) diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java index 9025d9cf8b..ed6ec5c7c1 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.lang.cpp; -import net.sourceforge.pmd.cpd.CPPTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.cpp.cpd.CPPTokenizer; import net.sourceforge.pmd.lang.impl.CpdOnlyLanguageModuleBase; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CPPTokenizer.java similarity index 98% rename from pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java rename to pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CPPTokenizer.java index 0a917bde1f..a642ce9b84 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CPPTokenizer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CPPTokenizer.java @@ -2,12 +2,13 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.cpp.cpd; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.impl.JavaCCTokenFilter; import net.sourceforge.pmd.cpd.impl.TokenizerBase; import net.sourceforge.pmd.lang.LanguagePropertyBundle; diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppBlockSkipper.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppBlockSkipper.java similarity index 96% rename from pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppBlockSkipper.java rename to pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppBlockSkipper.java index 35857ef375..35c6208392 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppBlockSkipper.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppBlockSkipper.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.cpp.cpd; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppEscapeTranslator.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppEscapeTranslator.java similarity index 96% rename from pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppEscapeTranslator.java rename to pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppEscapeTranslator.java index b89904ffaf..b6d555db71 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/cpd/CppEscapeTranslator.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppEscapeTranslator.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.cpp.cpd; import net.sourceforge.pmd.lang.ast.impl.javacc.BackslashEscapeTranslator; import net.sourceforge.pmd.lang.document.Chars; diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CPPTokenizerTest.java similarity index 97% rename from pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java rename to pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CPPTokenizerTest.java index cf35e091b4..dd1afd76a4 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CPPTokenizerTest.java @@ -1,14 +1,16 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.cpp.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.cpd.Tokenizer; +import net.sourceforge.pmd.cpd.Tokens; import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest; import net.sourceforge.pmd.cpd.test.LanguagePropertyConfig; import net.sourceforge.pmd.lang.cpp.CppLanguageModule; @@ -19,11 +21,6 @@ class CPPTokenizerTest extends CpdTextComparisonTest { super(CppLanguageModule.getInstance(), ".cpp"); } - @Override - protected String getResourcePrefix() { - return "../lang/cpp/cpd/testdata"; - } - @Override public @NonNull LanguagePropertyConfig defaultProperties() { return dontSkipBlocks(); diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CppCharStreamTest.java similarity index 97% rename from pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java rename to pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CppCharStreamTest.java index e2aeeff22f..e3e760d081 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CppCharStreamTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CppCharStreamTest.java @@ -2,7 +2,7 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.cpp.cpd; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java index db35eca33b..6cae4c0dcf 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLLanguageModule.java @@ -4,10 +4,10 @@ package net.sourceforge.pmd.lang.plsql; -import net.sourceforge.pmd.cpd.PLSQLTokenizer; import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase; +import net.sourceforge.pmd.lang.plsql.cpd.PLSQLTokenizer; /** * Created by christoferdutz on 20.09.14. diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/cpd/PLSQLTokenizer.java similarity index 96% rename from pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java rename to pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/cpd/PLSQLTokenizer.java index bc35e3c957..cddc68ad18 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/cpd/PLSQLTokenizer.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/cpd/PLSQLTokenizer.java @@ -2,8 +2,9 @@ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.plsql.cpd; +import net.sourceforge.pmd.cpd.Tokenizer; import net.sourceforge.pmd.cpd.impl.JavaCCTokenizer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/cpd/PLSQLTokenizerTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/cpd/PLSQLTokenizerTest.java similarity index 80% rename from pmd-plsql/src/test/java/net/sourceforge/pmd/cpd/PLSQLTokenizerTest.java rename to pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/cpd/PLSQLTokenizerTest.java index 280c1a0eeb..8247c98abd 100644 --- a/pmd-plsql/src/test/java/net/sourceforge/pmd/cpd/PLSQLTokenizerTest.java +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/cpd/PLSQLTokenizerTest.java @@ -1,8 +1,8 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ -package net.sourceforge.pmd.cpd; +package net.sourceforge.pmd.lang.plsql.cpd; import org.junit.jupiter.api.Test; @@ -15,12 +15,6 @@ class PLSQLTokenizerTest extends CpdTextComparisonTest { super(PLSQLLanguageModule.getInstance(), ".sql"); } - @Override - protected String getResourcePrefix() { - return "../lang/plsql/cpd/testdata"; - } - - @Test void testSimple() { doTest("sample-plsql"); From 6f6608dad96a9e60fe2a960dcacdc647171b4e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 10 Jun 2023 14:35:11 +0200 Subject: [PATCH 169/347] Delete cpp default version --- .../java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java index ed6ec5c7c1..3f3adc0d17 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java @@ -33,7 +33,6 @@ public class CppLanguageModule extends CpdOnlyLanguageModuleBase { public CppLanguageModule() { super(LanguageMetadata.withId("cpp") .name("C++") - .addDefaultVersion("any") .extensions("h", "hpp", "hxx", "c", "cpp", "cxx", "cc", "C")); } From d07240c872cd65bfa29c1a9889b3d414376bf8d7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 10 Jun 2023 14:55:26 +0200 Subject: [PATCH 170/347] Update migration guide (Node and NodeStream) --- docs/pages/pmd/userdocs/migrating_to_pmd7.md | 94 +++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md index 85867b6371..ae31564a2c 100644 --- a/docs/pages/pmd/userdocs/migrating_to_pmd7.md +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -78,6 +78,10 @@ override the method {% jdoc core::lang.rule.AbstractRule#buildTargetSelector %}: } ``` +The API to **navigate the AST** also changed significantly: +* Tree traversal using [Node API](#node-api) +* Consider using the new [NodeStream API](#nodestream-api) to navigate with null-safety. This is optional. + Additionally, if you have created rules for **Java** - regardless whether it is a XPath based rule or a Java based rule - you might need to adjust your queries or visitor methods. The Java AST has been refactored substantially. The easiest way is to use the [PMD Rule Designer](pmd_userdocs_extending_designer_reference.html) to see the structure @@ -113,9 +117,95 @@ But both would work. CPD: End Columns of Tokens are exclusive on PMD 7, but inclusive on PMD 6.x. See 5b7ed58 -### AST Navigation in general +### Node API -Methods like Node::getFirstChildOfType... use replacement either, NodeStream or in that case Node::firstChild(...). +Starting from one node in the AST, you can navigate to children or parents with the following methods. This is +the "traditional" way for simple cases. For more complex cases, consider to use the new [NodeStream API](#nodestream-api). + +Many methods available in PMD 6 have been deprecated and removed for a slicker API with consistent naming, +that also integrates tightly with the NodeStream API. + +* `getNthParent(n)` โžก๏ธ `ancestors().get(n - 1)` +* `getFirstParentOfType(parentType)` โžก๏ธ `ancestors(parentType).first()` +* `getParentsOfType(parentType)` โžก๏ธ `ancestors(parentType).toList()` +* `findChildrenOfType(childType)` โžก๏ธ `children(childType).toList()` +* `findDescendantsOfType(targetType)` โžก๏ธ `descendants(targetType).toList()` +* `getFirstChildOfType(childType)` โžก๏ธ `firstChild(childType)` +* `getFirstDescendantOfType(descendantType)` โžก๏ธ `descendants(descendantType).first()` +* `hasDescendantOfType(type)` โžก๏ธ `descendants(type).nonEmpty()` + +{% include tip.html content="First use PMD 7.0.0-rc3, which still has these methods. These methods are marked as +deprecated, so you can then start to change them. The replacement method is usually provided in the javadocs. +That way you avoid being confronted with just compile errors." %} + +Unchanged methods that work as before: +* {% jdoc core::lang.ast.Node#getParent() %} +* {% jdoc core::lang.ast.Node#getChild(int) %} +* {% jdoc core::lang.ast.Node#getNumChildren() %} +* {% jdoc core::lang.ast.Node#getIndexInParent() %} + +New methods: +* {% jdoc core::lang.ast.Node#getFirstChild() %} +* {% jdoc core::lang.ast.Node#getLastChild() %} +* {% jdoc core::lang.ast.Node#getPreviousSibling() %} +* {% jdoc core::lang.ast.Node#getNextSibling() %} +* {% jdoc core::lang.ast.Node#getRoot() %} + +New methods that integrate with NodeStream: +* {% jdoc core::lang.ast.Node#children() %} - returns a NodeStream containing all the children of this node. + Note: in PMD 6, this method returned an `Iterable` +* {% jdoc core::lang.ast.Node#descendants() %} +* {% jdoc core::lang.ast.Node#descendantsOrSelf() %} +* {% jdoc core::lang.ast.Node#ancestors() %} +* {% jdoc core::lang.ast.Node#ancestorsOrSelf() %} +* {% jdoc core::lang.ast.Node#children(java.lang.Class) %} +* {% jdoc core::lang.ast.Node#firstChild(java.lang.Class) %} +* {% jdoc core::lang.ast.Node#descendants(java.lang.Class) %} +* {% jdoc core::lang.ast.Node#ancestors(java.lang.Class) %} + +Methods removed completely: +* `getFirstParentOfAnyType(parentTypes)`:๏ธ There is no direct replacement, but something along the lines: + ```java + ancestors() + .filter(n -> Arrays.stream(classes) + .map(c -> c.isInstance(n)) + .anyMatch(Boolean::booleanValue)) + .first(); + ``` +* `findChildNodesWithXPath`: Has been removed, because it is very inefficient. Use NodeStream instead. +* `hasDescendantMatchingXPath`: Has been removed, because it is very inefficient. Use NodeStream instead. +* `jjt*` like `jjtGetParent`. These methods were implementation specific. Use the equivalent methods like `getParent()`. + +See {% jdoc core::lang.ast.Node %} for the details. + +### NodeStream API + +In java rule implementations, you often need to navigate the AST to find the interesting nodes. In PMD 6, this +was often done by calling `jjtGetChild(int)` or `jjtGetParent(int)` and then checking the node type +with `instanceof`. There are also helper methods available, like `getFirstChildOfType(Class)` or +`findDescendantsOfType(Class)`. These methods might return `null` and you need to check this for every +level. + +The new **NodeStream API** provides easy to use methods that follow the Java Stream API (`java.util.stream`). + +Many complex predicates about nodes can be expressed by testing the emptiness of a node stream. +E.g. the following tests if the node is a variable declarator id initialized to the value `0`: + +Example: + +```java + + NodeStream.of(someNode) // the stream here is empty if the node is null + .filterIs(ASTVariableDeclaratorId.class)// the stream here is empty if the node was not a variable declarator id + .followingSiblings() // the stream here contains only the siblings, not the original node + .children(ASTNumericLiteral.class) + .filter(ASTNumericLiteral::isIntLiteral) + .filterMatching(ASTNumericLiteral::getValueAsInt, 0) + .nonEmpty(); // If the stream is non empty here, then all the pipeline matched +``` + +See {% jdoc core::lang.ast.NodeStream %} for the details. +Note: This was implemented via [PR #1622 [core] NodeStream API](https://github.com/pmd/pmd/pull/1622) ### XPath: Migrating from 1.0 to 2.0 From a43a3d7c8fb4cb146a44005bb20232f22f7c5035 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 10 Jun 2023 16:05:24 +0200 Subject: [PATCH 171/347] Update migration guide (Java AST) --- docs/pages/pmd/userdocs/migrating_to_pmd7.md | 1641 +++++++++++++++++- 1 file changed, 1639 insertions(+), 2 deletions(-) diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md index ae31564a2c..cf129bcfaf 100644 --- a/docs/pages/pmd/userdocs/migrating_to_pmd7.md +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -194,7 +194,6 @@ E.g. the following tests if the node is a variable declarator id initialized to Example: ```java - NodeStream.of(someNode) // the stream here is empty if the node is null .filterIs(ASTVariableDeclaratorId.class)// the stream here is empty if the node was not a variable declarator id .followingSiblings() // the stream here contains only the siblings, not the original node @@ -255,7 +254,1645 @@ XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: which can be used instead. ### Java AST -* See also [Java Clean Changes](https://github.com/pmd/pmd/wiki/Java_clean_changes) + +{% comment %} +"โ””โ”€ ", +"โ”œโ”€ ", +"โ”‚ ", +" " +{% endcomment %} + +#### Annotations + +* What: Annotations are consolidated into a single node. SingleMemberAnnotation, NormalAnnotation and MarkerAnnotation + are removed in favour of Annotation. The Name node is removed, replaced by a ClassOrInterfaceType. +* Why: Those different node types implement a syntax-only distinction, that only makes semantically equivalent + annotations have different possible representations. For example, `@A` and `@A()` are semantically equivalent, + yet they were parsed as MarkerAnnotation resp. NormalAnnotation. Similarly, `@A("")` and `@A(value="")` were parsed + as SingleMemberAnnotation resp. NormalAnnotation. This also makes parsing much simpler. The nested ClassOrInterface + type is used to share the disambiguation logic. +* [#2282 [java] Use single node for annotations](https://github.com/pmd/pmd/pull/2282) + + + + + + + + + + + + + + + + + + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +@A +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation + โ””โ”€ MarkerAnnotation + โ””โ”€ Name "A" +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation + โ””โ”€ ClassOrInterfaceType "A" +{% endhighlight %} +
+{% highlight java %} +@A() +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation + โ””โ”€ NormalAnnotation + โ””โ”€ Name "A" +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation "A" + โ”œโ”€ ClassOrInterfaceType "A" + โ””โ”€ AnnotationMemberList +{% endhighlight %} +
+{% highlight java %} +@A(value="v") +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation + โ””โ”€ NormalAnnotation + โ”œโ”€ Name "A" + โ””โ”€ MemberValuePairs + โ””โ”€ MemberValuePair "value" + โ””โ”€ MemberValue + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Literal '"v"' +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation "A" + โ”œโ”€ ClassOrInterfaceType "A" + โ””โ”€ AnnotationMemberList + โ””โ”€ MemberValuePair "value" [ @Shorthand = false() ] + โ””โ”€ StringLiteral '"v"' +{% endhighlight %} +
+{% highlight java %} +@A("v") +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation + โ””โ”€ SingleMemberAnnotation + โ”œโ”€ Name "A" + โ””โ”€ MemberValue + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Literal '"v"' +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation "A" + โ”œโ”€ ClassOrInterfaceType "A" + โ””โ”€ AnnotationMemberList + โ””โ”€ MemberValuePair "value" [ @Shorthand = true() ] + โ””โ”€ StringLiteral '"v"' +{% endhighlight %} +
+{% highlight java %} +@A(value="v", on=true) +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation + โ””โ”€ NormalAnnotation + โ”œโ”€ Name "A" + โ””โ”€ MemberValuePairs + โ”œโ”€ MemberValuePair "value" + โ”‚ โ””โ”€ MemberValue + โ”‚ โ””โ”€ PrimaryExpression + โ”‚ โ””โ”€ PrimaryPrefix + โ”‚ โ””โ”€ Literal '"v"' + โ””โ”€ MemberValuePair "on" + โ””โ”€ MemberValue + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Literal + โ””โ”€ BooleanLiteral [ @True = true() ] +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Annotation "A" + โ”œโ”€ ClassOrInterfaceType "A" + โ””โ”€ AnnotationMemberList + โ”œโ”€ MemberValuePair "value" [ @Shorthand = false() ] + โ”‚ โ””โ”€ StringLiteral '"v"' + โ””โ”€ MemberValuePair "on" + โ””โ”€ BooleanLiteral [ @True = true() ] +{% endhighlight %} +
+ +##### Annotation nesting + +* What: Annotations are now nested within the node, to which they are applied to. E.g. if a method is annotated, the Annotation node is now a child of a ModifierList, inside the MethodDeclaration. +* Why: Fixes a lot of inconsistencies, where sometimes the annotations were inside the node, and sometimes just somewhere in the parent, with no real structure. +* [#1875 [java] Move annotations inside the node they apply to](https://github.com/pmd/pmd/pull/1875) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeOld ASTNew AST
+Method + +{% highlight java %} +@A +public void set(int x) { } +{% endhighlight %} + +{% highlight js %} +โ””โ”€ ClassOrInterfaceBodyDeclaration + โ”œโ”€ Annotation + โ”‚ โ””โ”€ MarkerAnnotation + โ”‚ โ””โ”€ Name "A" + โ””โ”€ MethodDeclaration + โ”œโ”€ ResultType[@Void=true] + โ”œโ”€ ... +{% endhighlight %} + +{% highlight js %} +โ””โ”€ MethodDeclaration + โ”œโ”€ ModifierList + โ”‚ โ””โ”€ Annotation "A" + โ”œโ”€ VoidType + โ”œโ”€ ... +{% endhighlight %} +
+Top-level type declaration + +{% highlight java %} +@A class C {} +{% endhighlight %} + +{% highlight js %} +โ””โ”€ TypeDeclaration + โ”œโ”€ Annotation + โ”‚ โ””โ”€ MarkerAnnotation + โ”‚ โ””โ”€ Name "A" + โ””โ”€ ClassOrInterfaceDeclaration + โ””โ”€ ClassOrInterfaceBody +{% endhighlight %} + +{% highlight js %} +โ””โ”€ TypeDeclaration + โ””โ”€ ClassOrInterfaceDeclaration + โ”œโ”€ ModifierList + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ ClassOrInterfaceBody +{% endhighlight %} +
+Cast expression + +{% highlight java %} +(@A T.@B S) expr +{% endhighlight %} + +N/A (Parse error) + +{% highlight js %} +โ””โ”€ CastExpression + โ”œโ”€ ClassOrInterfaceType "S" + โ”‚ โ””โ”€ Annotation "B" + โ”‚ โ””โ”€ ClassOrInterfaceType "T" + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ (Expression `expr`) +{% endhighlight %} +
+Cast expression with intersection + +{% highlight java %} +(@A T & S) expr +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CastExpression + โ”œโ”€ MarkerAnnotation "A" + โ”œโ”€ ClassOrInterfaceType "T" + โ”œโ”€ ClassOrInterfaceType "S" + โ””โ”€ (Expression `expr`) +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CastExpression + โ”œโ”€ IntersectionType + โ”‚ โ”œโ”€ ClassOrInterfaceType "T" + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ ClassOrInterfaceType "S" + โ””โ”€ (Expression `expr`) +{% endhighlight %} + +Notice @A binds to T, not T & S + +
+Constructor call + +{% highlight java %} +new @A T() +{% endhighlight %} + +{% highlight js %} +โ””โ”€ AllocationExpression + โ”œโ”€ MarkerAnnotation "A" + โ”œโ”€ Type + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "T" + โ””โ”€ Arguments +{% endhighlight %} + +{% highlight js %} +โ””โ”€ ConstructorCall + โ”œโ”€ ClassOrInterfaceType "T" + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ ArgumentsList +{% endhighlight %} +
+Array allocation + +{% highlight java %} +new @A int[0] +{% endhighlight %} + +{% highlight js %} +โ””โ”€ AllocationExpression + โ”œโ”€ MarkerAnnotation "A" + โ”œโ”€ Type + โ”‚ โ””โ”€ PrimitiveType "int" + โ””โ”€ ArrayDimsAndInits + โ””โ”€ Expression + โ””โ”€ PrimaryExpression + โ””โ”€ Literal "0" +{% endhighlight %} + +{% highlight js %} +โ””โ”€ ArrayAllocation + โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ ArrayAllocationDims + โ””โ”€ ArrayDimExpr + โ””โ”€ NumericLiteral "0" +{% endhighlight %} +
+Array type + +{% highlight java %} +@A int @B[] +{% endhighlight %} + +N/A (parse error) + +{% highlight js %} +โ””โ”€ ArrayType + โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ ArrayTypeDims + โ””โ”€ ArrayTypeDim + โ””โ”€ Annotation "B" +{% endhighlight %} + +Notice @A binds to int, not int[] + +
+Type parameters + +{% highlight java %} +<@A T, @B S extends @C Object> +{% endhighlight %} + +{% highlight js %} +โ””โ”€ TypeParameters + โ”œโ”€ MarkerAnnotation "A" + โ”œโ”€ TypeParameter "T" + โ”œโ”€ MarkerAnnotation "B" + โ””โ”€ TypeParameter "S" + โ”œโ”€ MarkerAnnotation "C" + โ””โ”€ TypeBound + โ””โ”€ ReferenceType + โ””โ”€ ClassOrInterfaceType "Object" +{% endhighlight %} + +{% highlight js %} +โ””โ”€ TypeParameters + โ””โ”€ TypeParameter "T" + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ TypeParameter "S" + โ”œโ”€ Annotation "B" + โ””โ”€ ClassOrInterfaceType "Object" + โ””โ”€ Annotation "C" +{% endhighlight %} + +
    +
  • TypeParameters now only can have TypeParameter as a child
  • +
  • Annotations that apply to the param are in the param
  • +
  • Annotations that apply to the bound are in the type
  • +
  • This removes the need for TypeBound, because annotations are cleanly placed.
  • +
+ +
+Enum constants + +{% highlight java %} +enum { + @A E1, @B E2 +} +{% endhighlight %} + +{% highlight js %} +โ””โ”€ EnumBody + โ”œโ”€ MarkerAnnotation "A" + โ”œโ”€ EnumConstant "E1" + โ”œโ”€ MarkerAnnotation "B" + โ””โ”€ EnumConstant "E2" +{% endhighlight %} + +{% highlight js %} +โ””โ”€ EnumBody + โ”œโ”€ EnumConstant "E1" + โ”‚ โ”œโ”€ ModifierList + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ VariableDeclaratorId "E1" + โ””โ”€ EnumConstant "E2" + โ”œโ”€ ModifierList + โ”‚ โ””โ”€ Annotation "B" + โ””โ”€ VariableDeclaratorId "E1" +{% endhighlight %} + +
    +
  • Annotations are not just randomly in the enum body anymore
  • +
+
+ +#### Types + +##### Type and ReferenceType + +* What: those two nodes are turned into interfaces, implemented by concrete syntax nodes. See their javadoc for exactly what nodes implement them. +* Why: + * some syntactic contexts only allow reference types, other allow any kind of type. If you want to match all types of a program, then matching Type would be the intuitive solution. But in 6.0.x, it wouldn't have sufficed, since in some contexts, no Type node was pushed, only a ReferenceType + * Regardless of the original syntactic context, any reference type *is* a type, and searching for ASTType should yield all the types in the tree. + * Using interfaces allows to abstract behaviour and make a nicer and safer API. + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +// in the context of a variable declaration +List strs; +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Type (1) + โ””โ”€ ReferenceType + โ””โ”€ ClassOrInterfaceType "List" + โ””โ”€ TypeArguments + โ””โ”€ TypeArgument + โ””โ”€ ReferenceType (2) + โ””โ”€ ClassOrInterfaceType "String" +{% endhighlight %} +
    +
  1. Notice that there is a Type node here, since a local var can have a primitive type
  2. +
  3. In contrast, notice that there is no Type here, since only reference types are allowed as type arguments
  4. +
+
+{% highlight js %} +โ””โ”€ ClassOrInterfaceType "List" + โ””โ”€ TypeArguments + โ””โ”€ ClassOrInterfaceType "String" +{% endhighlight %} +ClassOrInterfaceType implements ASTReferenceType, which implements ASTType. +
+ +##### Array changes + +What: Additional nodes `ArrayType`, `ArrayTypeDim`, `ArrayTypeDims`, `ArrayAllocation`. +Why: Support annotated array types ([#997 Java8 parsing corner case with annotated array types](https://github.com/pmd/pmd/issues/997)) +* [#1981 [java] Simplify array allocation expressions](https://github.com/pmd/pmd/pull/1981) + +Examples: + + + + + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +String[][] myArray; +{% endhighlight %} + +{% highlight js %} +โ””โ”€ Type + โ””โ”€ ReferenceType[ @Array = true() ][ @ArrayDepth = 2 ] + โ””โ”€ ClassOrInterfaceType +{% endhighlight %} + +{% highlight js %} +โ””โ”€ ArrayType[ @ArrayDepth = 2 ] + โ”œโ”€ ClassOrInterfaceType + โ””โ”€ ArrayDimensions[ @Size = 2 ] + โ”œโ”€ ArrayTypeDim + โ””โ”€ ArrayTypeDim +{% endhighlight %} +
+{% highlight java %} +String @Annotation1[] @Annotation2[] myArray; +{% endhighlight %} + +n/a (parse error) + +{% highlight js %} +โ””โ”€ ArrayType + โ”œโ”€ ClassOrInterfaceType + โ””โ”€ ArrayDimensions + โ”œโ”€ ArrayTypeDim + โ”‚ โ””โ”€ Annotation[ @AnnotationName = 'Annotation1' ] + โ””โ”€ ArrayTypeDim + โ””โ”€ Annotation[ @AnnotationName = 'Annotation2' ] +{% endhighlight %} +
+{% highlight java %} +new int[2][] +new @Bar int[3][2] +new Foo[] { f, g } +{% endhighlight %} + +{% highlight js %} +AllocationExpression + + PrimitiveType "int" + + ArrayDimsAndInits + + Expression + + PrimaryExpression + + PrimaryPrefix + + Literal "2" +AllocationExpression + + Annotation + + MarkerAnnotation + + Name "Bar" + + PrimitiveType "int" + + ArrayDimsAndInits + + Expression + + PrimaryExpression + + PrimaryPrefix + + Literal "3" + + Expression + + PrimaryExpression + + PrimaryPrefix + + Literal "2" +AllocationExpression + + ClassOrInterfaceType "Foo" + + ArrayDimsAndInits + + ArrayInitializer + + VariableInitializer + + Expression + + PrimaryExpression + + PrimaryPrefix + + Name "f" + + VariableInitializer + + Expression + + PrimaryExpression + + PrimaryPrefix + + Name "g" +{% endhighlight %} + +{% highlight js %} +ArrayAllocation + + ArrayType + + PrimitiveType "int" + + ArrayDimensions + + ArrayDimExpr + + NumericLiteral "2" + + ArrayTypeDim +ArrayAllocation + + ArrayType + + PrimitiveType "int" + + MarkerAnnotation "Bar" + + ArrayDimensions + + ArrayDimExpr + + NumericLiteral "3" + + ArrayDimExpr + + NumericLiteral "2" +ArrayAllocation + + ArrayType + + ClassOrInterfaceType "Foo" + + ArrayDimensions + + ArrayTypeDim + + ArrayInitializer + + VariableAccess "f" + + VariableAccess "g" +{% endhighlight %} +
+ +##### ClassOrInterfaceType nesting + +* What: ClassOrInterfaceType is now left-recursive, and encloses its qualifying type. +* Why: To preserve the position of annotations and type arguments + * [#1150 ClassOrInterfaceType AST improvements](https://github.com/pmd/pmd/issues/1150) + + + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +Map.Entry +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "Map.Entry" + + TypeArguments + + TypeArgument + + ReferenceType + + ClassOrInterfaceType "K" + + TypeArgument + + ReferenceType + + ClassOrInterfaceType "V" +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "Entry" + + ClassOrInterfaceType "Map" + + TypeArguments + + ClassOrInterfaceType "K" + + ClassOrInterfaceType "V" +{% endhighlight %} +
+{% highlight java %} +First.Second.Third +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "First.Second.Third" + + TypeArguments + + TypeArgument + + ReferenceType + + ClassOrInterfaceType "K" + + TypeArguments + + TypeArgument + + ReferenceType + + ClassOrInterfaceType "V" +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "Third" + - ClassOrInterfaceType "Second" + - ClassOrInterfaceType "First" + - TypeArguments + - ClassOrInterfaceType "K" + - TypeArguments + - ClassOrInterfaceType "V" +{% endhighlight %} +
+ +##### TypeArgument and WildcardType + +* What: + * TypeArgument is removed. Instead, the TypeArguments node contains directly a sequence of Type nodes. To support this, the new node type WildcardType captures the syntax previously parsed as a TypeArgument. + * The WildcardBounds node is removed. Instead, the bound is a direct child of the WildcardType. +* Why: Because wildcard types are types in their own right, and having a node to represent them skims several levels of nesting off. + + + + + + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +Entry +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "Entry" + + TypeArguments + + TypeArgument + + ReferenceType + + ClassOrInterfaceType "String" + + TypeArgument[@UpperBound = true()] + + WildcardBounds + + ReferenceType + + ClassOrInterfaceType "Node" +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "Entry" + + TypeArguments + + ClassOrInterfaceType "String" + + WildcardType[@UpperBound = true()] + + ClassOrInterfaceType "Node" +{% endhighlight %} +
+{% highlight java %} +List +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "List" + + TypeArguments + + TypeArgument +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceType "List" + + TypeArguments + + WildcardType +{% endhighlight %} +
+ +#### Declarations + +##### Import and Package declarations + +* What: Remove the Name node in imports and package declaration nodes. +* Why: Name is a TypeNode, but it's equivalent to AmbiguousName in that it describes nothing about what it represents. The name in an import may represent a method name, a type name, a field name... It's too ambiguous to treat in the parser and could just be the image of the import, or package, or module. +* [#1888 [java] Remove Name nodes in Import- and PackageDeclaration](https://github.com/pmd/pmd/pull/1888) + + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +import java.util.ArrayList; +import static java.util.Comparator.reverseOrder; +import java.util.*; +{% endhighlight %} + +{% highlight js %} +ImportDeclaration + + Name "java.util.ArrayList" +ImportDeclaration[@Static=true()] + + Name "java.util.Comparator.reverseOrder" +ImportDeclaration[@ImportOnDemand=true()] + + Name "java.util" +{% endhighlight %} + +{% highlight js %} +ImportDeclaration "java.util.ArrayList" +ImportDeclaration[@Static=true()] "java.util.Comparator.reverseOrder" +ImportDeclaration[@ImportOnDemand=true()] "java.util" +{% endhighlight %} +
+{% highlight java %} +package com.example.tool; +{% endhighlight %} + +{% highlight js %} +PackageDeclaration + + Name "com.example.tool" +{% endhighlight %} + +{% highlight js %} +PackageDeclaration "com.example.tool" ++ ModifierList +{% endhighlight %} +
+ +##### Modifier lists + +* What: AccessNode is now based on a node: ModifierList. That node represents modifiers occurring before + a declaration. It provides a flexible API to query modifiers, both explicit and implicit. All declaration + nodes now have such a modifier list, even if it's implicit (no explicit modifiers). +* Why: AccessNode gave a lot of irrelevant methods to its subtypes. E.g. `ASTFieldDeclaration::isSynchronized` + makes no sense. Now, these irrelevant methods don't clutter the API. The API of ModifierList is both more + general and flexible +* See [#2259 [java] Rework AccessNode](https://github.com/pmd/pmd/pull/2259) + + + + + + + + + +
CodeOld ASTNew AST
+Method + +{% highlight java %} +@A +public void set(final int x, int y) { } +{% endhighlight %} + +{% highlight js %} +ClassOrInterfaceBodyDeclaration + + Annotation + + MarkerAnnotation + + Name "A" + + MethodDeclaration[@Public = true()] + + ResultType[@Void=true] + + MethodDeclarator + + FormalParameters + + FormalParameter[@Final = true()] + + VariableDeclaratorId "x" + + FormalParameter[@Final = false()] + + VariableDeclaratorId "y" +{% endhighlight %} + +{% highlight js %} +MethodDeclaration + + ModifierList[@Modifiers=("public")] + + Annotation "A" + + VoidType + + FormalParameters + + FormalParameter + + ModifierList[@Modifiers=("final")] + + VariableDeclaratorId "x" + + FormalParameter + + ModifierList[@Modifiers=()] + + VariableDeclaratorId "y" +{% endhighlight %} +
+Top-level type declaration + +{% highlight java %} +public @A class C {} +{% endhighlight %} + +{% highlight js %} +TypeDeclaration + + Annotation + + MarkerAnnotation + + Name "A" + + ClassOrInterfaceDeclaration[@Public=true()] + + ClassOrInterfaceBody +{% endhighlight %} + +{% highlight js %} +TypeDeclaration + + ClassOrInterfaceDeclaration + + ModifierList[@Modifiers=("public")] + + MarkerAnnotation "A" + + ClassOrInterfaceBody +{% endhighlight %} +
+ +##### Flattened body declarations + +* What: Removes ClassOrInterfaceBodyDeclaration, TypeDeclaration, and AnnotationTypeMemberDeclaration. + These were unnecessary since annotations are nested (see above [Annotation nesting](#annotation-nesting)). +* Why: This flattens the tree, makes it less verbose and simpler. +* [#2300 [java] Flatten body declarations](https://github.com/pmd/pmd/pull/2300) + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +public class Flat { + private int f; +} +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CompilationUnit + โ””โ”€ TypeDeclaration + โ””โ”€ ClassOrInterfaceDeclaration[ @SimpleName = 'Flat' ] + โ””โ”€ ClassOrInterfaceBody + โ””โ”€ ClassOrInterfaceBodyDeclaration + โ””โ”€ FieldDeclaration + โ”œโ”€ Type + โ”‚ โ””โ”€ PrimitiveType + โ””โ”€ VariableDeclarator + โ””โ”€ VariableDeclaratorId[ @VariableName = 'f' ] +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CompilationUnit + โ””โ”€ ClassOrInterfaceDeclaration[ @SimpleName = 'Flat' ] + โ”œโ”€ ModifierList + โ””โ”€ ClassOrInterfaceBody + โ””โ”€ FieldDeclaration + โ”œโ”€ ModifierList + โ”œโ”€ PrimitiveType + โ””โ”€ VariableDeclarator + โ””โ”€ VariableDeclaratorId[ @VariableName = 'f' ] +{% endhighlight %} +
+{% highlight java %} +public @interface FlatAnnotation { + String value() default ""; +} +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CompilationUnit + โ””โ”€ TypeDeclaration + โ””โ”€ AnnotationTypeDeclaration + โ””โ”€ AnnotationTypeBody + โ””โ”€ AnnotationTypeMemberDeclaration + โ””โ”€ AnnotationMethodDeclaration + โ”œโ”€ Type + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType + โ””โ”€ DefaultValue + โ””โ”€ MemberValue + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Literal +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CompilationUnit + โ””โ”€ AnnotationTypeDeclaration + โ”œโ”€ ModifierList + โ””โ”€ AnnotationTypeBody + โ””โ”€ MethodDeclaration + โ”œโ”€ ModifierList + โ”œโ”€ ClassOrInterfaceType + โ”œโ”€ FormalParameters + โ””โ”€ DefaultValue + โ””โ”€ StringLiteral +{% endhighlight %} +
+ +##### Module declarations + +* What: Removes the generic Name node and uses instead ClassOrInterfaceType where appropriate. Also + uses specific node types for different directives (requires, exports, uses, provides). +* Why: Simplify queries, support type resolution +* [#3890 [java] Improve module grammar](https://github.com/pmd/pmd/pull/3890) + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +open module com.example.foo { + requires com.example.foo.http; + requires java.logging; + requires transitive com.example.foo.network; + + exports com.example.foo.bar; + exports com.example.foo.internal to com.example.foo.probe; + + uses com.example.foo.spi.Intf; + + provides com.example.foo.spi.Intf with com.example.foo.Impl; +} +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CompilationUnit + โ””โ”€ ModuleDeclaration[ @Image = 'com.example.foo' ][ @Open = true() ] + โ”œโ”€ ModuleDirective[ @Type = 'REQUIRES' ] + โ”‚ โ””โ”€ ModuleName[ @Image = 'com.example.foo.http' ] + โ”œโ”€ ModuleDirective[ @Type = 'REQUIRES' ] + โ”‚ โ””โ”€ ModuleName[ @Image = 'java.logging' ] + โ”œโ”€ ModuleDirective[ @Type = 'REQUIRES' ][ @RequiresModifier = 'TRANSITIVE' ] + โ”‚ โ””โ”€ ModuleName[ @Image = 'com.example.foo.network' ] + โ”œโ”€ ModuleDirective[ @Type = 'EXPORTS' ] + โ”‚ โ””โ”€ Name[ @Image = 'com.example.foo.bar' ] + โ”œโ”€ ModuleDirective[ @Type = 'EXPORTS' ] + โ”‚ โ”œโ”€ Name[ @Image = 'com.example.foo.internal' ] + โ”‚ โ””โ”€ ModuleName[ @Image = 'com.example.foo.probe' ] + โ”œโ”€ ModuleDirective[ @Type = 'USES' ] + โ”‚ โ””โ”€ Name[ @Image = 'com.example.foo.spi.Intf' ] + โ””โ”€ ModuleDirective[ @Type = 'PROVIDES' ] + โ”œโ”€ Name[ @Image = 'com.example.foo.spi.Intf' ] + โ””โ”€ Name[ @Image = 'com.example.foo.Impl' ] +{% endhighlight %} + +{% highlight js %} +โ””โ”€ CompilationUnit + โ””โ”€ ModuleDeclaration[ @Name = 'com.example.foo' ][ @Open = true() ] + โ”œโ”€ ModuleName[ @Name = 'com.example.foo' ] + โ”œโ”€ ModuleRequiresDirective + โ”‚ โ””โ”€ ModuleName[ @Name = 'com.example.foo.http' ] + โ”œโ”€ ModuleRequiresDirective + โ”‚ โ””โ”€ ModuleName[ @Name = 'java.logging' ] + โ”œโ”€ ModuleRequiresDirective[ @Transitive = true ] + โ”‚ โ””โ”€ ModuleName[ @Name = 'com.example.foo.network' ] + โ”œโ”€ ModuleExportsDirective[ @PackageName = 'com.example.foo.bar' ] + โ”œโ”€ ModuleExportsDirective[ @PackageName = 'com.example.foo.internal' ] + โ”‚ โ””โ”€ ModuleName [ @Name = 'com.example.foo.probe' ] + โ”œโ”€ ModuleUsesDirective + โ”‚ โ””โ”€ ClassOrInterfaceType[ pmd-java:typeIs("com.example.foo.spi.Intf") ] + โ””โ”€ ModuleProvidesDirective + โ”œโ”€ ClassOrInterfaceType[ pmd-java:typeIs("com.example.foo.spi.Intf") ] + โ””โ”€ ClassOrInterfaceType[ pmd-java:typeIs("com.example.foo.Impl") ] +{% endhighlight %} +
+ +##### TODO: new node for anonymous class + +#### Method and Constructor declarations + +##### Method grammar simplification + +* What: Simplify and align the grammar used for method and constructor declarations. The methods in an annotation + type are now also method declarations. +* Why: The method declaration had an nested node "MethodDeclarator", which was not available for constructor + declarations. This made it difficult to write rules, that concern both methods and constructors without + explicitly differentiate between these two. +* [#2034 [java] Align method and constructor declaration grammar](https://github.com/pmd/pmd/pull/2034) + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +public class Sample { + public Sample(int arg) throws Exception { + super(); + greet(arg); + } + public void greet(int arg) throws Exception { + System.out.println("Hello"); + } +} +{% endhighlight %} + +{% highlight js %} +ConstructorDeclaration "Sample" + + FormalParameters + + FormalParameter ... + + NameList + + Name "Exception" + + ExplicitConstructorInvocation + + Arguments + + BlockStatement + + Statement ... +MethodDeclaration + + ResultType + + MethodDeclarator "greet" + + FormatParameters + + FormalParameter ... + + NameList + + Name "Exception" + + Block + + BlockStatement + + Statement ... +{% endhighlight %} + +{% highlight js %} +ConstructorDeclaration "Sample" + + ModifierList + + FormalParameters + + FormalParameter ... + + ThrowsList + + ClassOrInterfaceType ... + + Block + + ExplicitConstructorInvocation + + ArgumentList + + ExpressionStatement +MethodDeclaration "greet" + + ModifierList + + VoidType + + FormalParameters + + FormalParameter ... + + ThrowsList + + ClassOrInterfaceType ... + + Block + + ExpressionStatement +{% endhighlight %} +
+{% highlight java %} +public @interface MyAnnotation { + int value() default 1; +} +{% endhighlight %} + +{% highlight js %} +AnnotationTypeDeclaration "MyAnnotation" + + AnnotationTypeBody + + AnnotationTypeMemberDeclaration + + AnnotationMethodDeclaration "value" + + Type ... + + DefaultValue ... +{% endhighlight %} + +{% highlight js %} +AnnotationTypeDeclaration "MyAnnotation" + + AnnotationTypeBody + + AnnotationTypeMemberDeclaration + + MethodDeclaration + + ModifierList + + PrimitiveType + + FormalParameters ... + + DefaultValue ... +{% endhighlight %} +
+ +##### Formal parameters + +* What: Use FormalParameter only for method and constructor declaration. Lambdas use LambdaParameter, catch clauses use CatchParameter +* Why: FormalParameter's API is different from the other ones. + * FormalParameter must mention a type node. + * LambdaParameter can be inferred + * CatchParameter cannot be varargs + * CatchParameter can have multiple exception types (a UnionType now) + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +try { + +} catch (@A IOException | IllegalArgumentException e) { + +} +{% endhighlight %} + +{% highlight js %} +TryStatement + + Block + + CatchStatement + + FormalParameter + + Annotation "A" + + Type + + ReferenceType + + ClassOrInterfaceType "IOException" + + Type + + ReferenceType + + ClassOrInterfaceType "IllegalArgumentException" + + VariableDeclaratorId + + Block +{% endhighlight %} + +{% highlight js %} +TryStatement + + Block + + CatchClause + + CatchParameter + + ModifierList + + Annotation "A" + + UnionType + + ClassOrInterfaceType "IOException" + + ClassOrInterfaceType "IllegalArgumentException" + + VariableDeclaratorId + + Block +{% endhighlight %} +
+{% highlight java %} +(a, b) -> {} +c -> {} +(@A var d) -> {} +(@A int e) -> {} +{% endhighlight %} + +{% highlight js %} +Expression + + PrimaryExpression + + PrimaryPrefix + + LambdaExpression + + VariableDeclaratorId "a" + + VariableDeclaratorId "b" + + Block + +Expression + + PrimaryExpression + + PrimaryPrefix + + LambdaExpression + + VariableDeclaratorId "c" + + Block + +Expression + + PrimaryExpression + + PrimaryPrefix + + LambdaExpression + + FormalParameters + + FormalParameter + + Annotation "A" + + ... + + VariableDeclaratorId "d" + + Block + +Expression + + PrimaryExpression + + PrimaryPrefix + + LambdaExpression + + FormalParameters + + FormalParameter + + Annotation "A" + + ... + + Type + + PrimitiveType + + VariableDeclaratorId "e" + + Block +{% endhighlight %} + +{% highlight js %} +LambdaExpression + + LambdaParameters + + LambdaParameter + + ModifierList + + VariableDeclaratorId "a" + + LambdaParameter + + ModifierList + + VariableDeclaratorId "b" + + Block + ++ LambdaExpression + + LambdaParameters + + LambdaParameter + + ModifierList + + VariableDeclaratorId "c" + + Block + + +LambdaExpression + + LambdaParameters + + LambdaParameter + + ModifierList + + Annotation "A" + + VariableDeclaratorId "d" + + Block + +LambdaExpression + + LambdaParameters + + LambdaParameter + + ModifierList + + Annotation "A" + + PrimitiveType "int" + + VariableDeclaratorId "e" + + Block +{% endhighlight %} +
+ + + +##### New node for explicit receiver parameter + +* What: A separate node type `ReceiverParameter` is introduced to differentiate it from formal parameters. +* Why: A receiver parameter is not a formal parameter, even though it looks like one: it doesn't declare a variable, + and doesn't affect the arity of the method or constructor. It's so rarely used that giving it its own node avoids + matching it by mistake and simplifies the API and grammar of the ubiquitous FormalParameter and VariableDeclaratorId. +* [#1980 [java] Separate receiver parameter from formal parameter](https://github.com/pmd/pmd/pull/1980) + + + + +
CodeOld ASTNew AST
+{% highlight java %} +(@A Foo this, Foo other) +{% endhighlight %} + +{% highlight js %} +FormalParameters[@ParameterCount = 1] + + FormalParameter[@ReceiverParameter=true()] + + ClassOrInterfaceType + + Annotation "A" + + VariableDeclaratorId[@Image="this", @ReceiverParameter=true()] + + FormalParameter + + ClassOrInterfaceType + + VariableDeclaratorId "other" +{% endhighlight %} + +{% highlight js %} +FormalParameters[@ParameterCount = 1] + + ReceiverParameter + + ClassOrInterfaceType + + Annotation "A" + + FormalParameter + + ModifierList + + ClassOrInterfaceType + + VariableDeclaratorId "other" +{% endhighlight %} +
+ +##### Varargs + +* What: parse the varargs ellipsis as an ArrayType +* Why: this improves regularity of the grammar, and allows type annotations to be added to the ellipsis + + + + + + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +(int... is) +{% endhighlight %} + +{% highlight js %} ++ FormalParameter[ @Varargs = true() ] + + Type + + PrimitiveType "int" + + VariableDeclaratorId "is" +{% endhighlight %} + +{% highlight js %} ++ FormalParameter[ @Varargs = true() ] + + ArrayType + + PrimitiveType "int" + + ArrayDimensions + + ArrayTypeDim[ @Varargs = true() ] + + VariableDeclaratorId "is" +{% endhighlight %} +
+{% highlight java %} +(int @A ... is) +{% endhighlight %} + +n/a (parse error) + +{% highlight js %} ++ FormalParameter[ @Varargs = true() ] + + ModifierList + + ArrayType + + PrimitiveType "int" + + ArrayDimensions + + ArrayTypeDim[ @Varargs = true() ] + + Annotation "A" + + VariableDeclaratorId "is" +{% endhighlight %} +
+{% highlight java %} +(int[]... is) +{% endhighlight %} + +{% highlight js %} ++ FormalParameter[ @Varargs = true() ] + + ModifierList + + Type + + ReferenceType + + PrimitiveType "int" + + VariableDeclaratorId "is" +{% endhighlight %} + +{% highlight js %} ++ FormalParameter[ @Varargs = true() ] + + ModifierList + + ArrayType + + PrimitiveType "int" + + ArrayDimensions + + ArrayTypeDim + + ArrayTypeDim[ @Varargs = true() ] + + Annotation "A" + + VariableDeclaratorId "is" +{% endhighlight %} +
+ + +##### Add void type node to replace ResultType + +* What: Add a VoidType node to replace ResultType. +* Why: This means we don't need the ResultType wrapper when the method is not void, and the result type node is never null. +* [[java] Add void type node to replace ResultType #2715](https://github.com/pmd/pmd/pull/2715) + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +void foo(); +{% endhighlight %} + +{% highlight js %} +โ””โ”€ MethodDeclaration + โ””โ”€ ResultType[@Void=true()] + โ””โ”€ MethodDeclarator + โ””โ”€ FormalParameters +{% endhighlight %} + +{% highlight js %} +โ””โ”€ MethodDeclaration + โ””โ”€ ModifierList + โ””โ”€ VoidType + โ””โ”€ FormalParameters +{% endhighlight %} +
+{% highlight java %} +int foo(); +{% endhighlight %} + +{% highlight js %} +โ””โ”€ MethodDeclaration + โ””โ”€ ResultType[@Void=false()] + โ””โ”€ Type + โ””โ”€ PrimitiveType + โ””โ”€ MethodDeclarator + โ””โ”€ FormalParameters +{% endhighlight %} + +{% highlight js %} +โ””โ”€ MethodDeclaration + โ””โ”€ ModifierList + โ””โ”€ PrimitiveType + โ””โ”€ FormalParameters +{% endhighlight %} +
+ +#### Statements + +##### TODO: statements are flattened (no BlockStatement, Statement nodes) +##### TODO: new node for ForeachStatement +##### TODO: New nodes for ExpressionStatement, LocalClassStatement + +##### Improve try-with-resources grammar +* What: The AST representation of a try-with-resources statement has been simplified. + It uses now LocalVariableDeclaration unless it is a concise try-with-resources grammar. +* Why: Simpler integration try-with-resources into symboltable and type resolution. +* [#1897 [java] Improve try-with-resources grammar](https://github.com/pmd/pmd/pull/1897) + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +try (InputStream in = new FileInputStream(); OutputStream out = new FileOutputStream();) { } +{% endhighlight %} + +{% highlight js %} +TryStatement + + ResourceSpecification + + Resources + + Resource + + Type + + ReferenceType + + ClassOrInterfaceType "InputStream" + + VariableDeclaratorId "in" + + Expression + + ... + + Resource + + Type + + ReferenceType + + ClassOrInterfaceType "OutputStream" + + VariableDeclaratorId "in" + + Expression + + ... +{% endhighlight %} + +{% highlight js %} +TryStatement + + ResourceList[@TrailingSemiColon=true()] + + Resource[@ConciseResource=false()] + + LocalVariableDeclaration + + ModifierList + + Type + + VariableDeclarator + + VariableDeclaratorId "in" + + ConstructorCall + + ClassOrInterfaceType + + ArgumentList + + Resource[@ConciseResource=false()] + + LocalVariableDeclaration + + ModifierList + + Type + + VariableDeclarator + + VariableDeclaratorId "in" + + ConstructorCall + + ClassOrInterfaceType + + ArgumentList +{% endhighlight %} +
+{% highlight java %} +InputStream in = new FileInputStream(); +try (in) {} +{% endhighlight %} + +{% highlight js %} +TryStatement + + ResourceSpecification + + Resources + + Resource + + Name "in" +{% endhighlight %} + +{% highlight js %} +TryStatement + + ResourceList[@TrailingSemiColon=false()] + + Resource[@ConciseResource=true()] + + VariableAccess[@VariableName='in'] +{% endhighlight %} +
+ +#### Expressions + +##### TODO: Literals +##### TODO: Method calls, constructor call, array allocation +##### TODO: Field access, array access, variable access +##### TODO: this/super expression +##### TODO: TypeExpression + +##### Merge unary expressions + +* What: Merge AST nodes for postfix and prefix expressions into the single UnaryExpression node. The merged nodes are: + * PreIncrementExpression + * PreDecrementExpression + * UnaryExpression + * UnaryExpressionNotPlusMinus +* Why: Those nodes were asymmetric, and inconsistently nested within UnaryExpression. By definition they're all unary, so that using a single node is appropriate. +* [#1890 [java] Merge different increment/decrement expressions](https://github.com/pmd/pmd/pull/1890) +* [#2155 [java] Merge prefix/postfix expressions into one node](https://github.com/pmd/pmd/pull/2155) + + + + + + +
CodeOld ASTNew AST
+{% highlight java %} +++a; +--b; +c++; +d--; +{% endhighlight %} + +{% highlight js %} +StatementExpression + + PreIncrementExpression + + PrimaryExpression + + PrimaryPrefix + + Name "a" +StatementExpression + + PreDecrementExpression + + PrimaryExpression + + PrimaryPrefix + + Name "b" +StatementExpression + + PostfixExpression "++" + + PrimaryExpression + + PrimaryPrefix + + Name "c" +StatementExpression + + PostfixExpression "--" + + PrimaryExpression + + PrimaryPrefix + + Name "d" +{% endhighlight %} + +{% highlight js %} +StatementExpression + + UnaryExpression[@Prefix=true()][@Operator="++"] + + VariableAccess "a" +StatementExpression + + UnaryExpression[@Prefix=true()][@Operator="--"] + + VariableAccess "b" +StatementExpression + + UnaryExpression[@Prefix=false()][@Operator="++"] + + VariableAccess "c" +StatementExpression + + UnaryExpression[@Prefix=false()][@Operator="--"] + + VariableAccess "d" +{% endhighlight %} +
+{% highlight java %} +~a ++a +{% endhighlight %} + +{% highlight js %} +UnaryExpression[@Image=null] + + UnaryExpressionNotPlusMinus[@Image="~"] + + PrimaryExpression + + PrimaryPrefix + + Name "a" + +UnaryExpression[@Image="+"] + + PrimaryExpression + + PrimaryPrefix + + Name "a" +{% endhighlight %} + +{% highlight js %} ++ UnaryExpression[@Operator="~"] + + VariableAccess "a" + ++ UnaryExpression[@Operator="+"] + + VariableAccess "a" +{% endhighlight %} +
+ +##### Binary operators are left-recursive + +* What: For each operator, there were separate AST nodes (like AdditiveExpression, AndExpression, ...). + These are now unified into a `InfixExpression`, which gives access to the operator via `getOperator()` + and to the operands (`getLhs()`, `getRhs()`). Additionally, the resulting AST is not flat anymore, + but a more structured tree. +* Why: Having different AST node types doesn't add information, that the operator doesn't already provide. + The new structure as a result, that the expressions are now parsed left recursive, makes the AST more JLS-like. + This makes it easier for the type mapping algorithms. It also provides the information, which operands are + used with which operator. This information was lost if more than 2 operands where used and the tree was + flattened with PMD 6. +* [#1979 [java] Make binary operators left-recursive](https://github.com/pmd/pmd/pull/1979) + + + + +
CodeOld ASTNew AST
+{% highlight java %} +int i = 1 * 2 * 3 % 4; +{% endhighlight %} + +{% highlight js %} +Expression + + MultiplicativeExpression "%" + + PrimaryExpression + + PrimaryPrefix + + Literal "1" + + PrimaryExpression + + PrimaryPrefix + + Literal "2" + + PrimaryExpression + + PrimaryPrefix + + Literal "3" + + PrimaryExpression + + PrimaryPrefix + + Literal "4" +{% endhighlight %} + +{% highlight js %} +InfixExpression[@Operator='%'] + + InfixExpression[@Operator='*'] + + InfixExpression[@Operator='*'] + + NumericLiteral[@ValueAsInt=1] + + NumericLiteral[@ValueAsInt=2] + + NumericLiteral[@ValueAsInt=3] + + NumericLiteral[@ValueAsInt=4] +{% endhighlight %} +
+ ### Language versions From efecee4b7b17c6a69fd90829b2215a888622e66d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 10 Jun 2023 18:45:02 +0200 Subject: [PATCH 172/347] Add deprecated to Tokens ctor --- .../src/main/java/net/sourceforge/pmd/cpd/Tokens.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 30291d3213..e92e6a24c1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -31,6 +31,15 @@ public class Tokens { // the first ID is 1, 0 is the ID of the EOF token. private int curImageId = 1; + /** + * Create a new instance, is internal. + */ + @InternalApi + @Deprecated // just to get a warning + public Tokens() { + + } + private void add(TokenEntry tokenEntry) { this.tokens.add(tokenEntry); } From 86f06ae268d12c9fc64d19d9dbf97f538616e0e6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 10 Jun 2023 19:48:52 +0200 Subject: [PATCH 173/347] Update migration guide (Java AST) --- docs/pages/pmd/userdocs/migrating_to_pmd7.md | 933 +++++++++---------- 1 file changed, 466 insertions(+), 467 deletions(-) diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md index cf129bcfaf..1fd805382a 100644 --- a/docs/pages/pmd/userdocs/migrating_to_pmd7.md +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -255,13 +255,6 @@ XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: ### Java AST -{% comment %} -"โ””โ”€ ", -"โ”œโ”€ ", -"โ”‚ ", -" " -{% endcomment %} - #### Annotations * What: Annotations are consolidated into a single node. SingleMemberAnnotation, NormalAnnotation and MarkerAnnotation @@ -618,7 +611,7 @@ Type parameters {% highlight js %} โ””โ”€ TypeParameters - โ””โ”€ TypeParameter "T" + โ”œโ”€ TypeParameter "T" โ”‚ โ””โ”€ Annotation "A" โ””โ”€ TypeParameter "S" โ”œโ”€ Annotation "B" @@ -774,68 +767,68 @@ new Foo[] { f, g } {% endhighlight %} {% highlight js %} -AllocationExpression - + PrimitiveType "int" - + ArrayDimsAndInits - + Expression - + PrimaryExpression - + PrimaryPrefix - + Literal "2" -AllocationExpression - + Annotation - + MarkerAnnotation - + Name "Bar" - + PrimitiveType "int" - + ArrayDimsAndInits - + Expression - + PrimaryExpression - + PrimaryPrefix - + Literal "3" - + Expression - + PrimaryExpression - + PrimaryPrefix - + Literal "2" -AllocationExpression - + ClassOrInterfaceType "Foo" - + ArrayDimsAndInits - + ArrayInitializer - + VariableInitializer - + Expression - + PrimaryExpression - + PrimaryPrefix - + Name "f" - + VariableInitializer - + Expression - + PrimaryExpression - + PrimaryPrefix - + Name "g" +โ”œโ”€ AllocationExpression +โ”‚ โ”œโ”€ PrimitiveType "int" +โ”‚ โ””โ”€ ArrayDimsAndInits +โ”‚ โ””โ”€ Expression +โ”‚ โ””โ”€ PrimaryExpression +โ”‚ โ””โ”€ PrimaryPrefix +โ”‚ โ””โ”€ Literal "2" +โ”œโ”€ AllocationExpression +โ”‚ โ”œโ”€ Annotation +โ”‚ โ”‚ โ””โ”€ MarkerAnnotation +โ”‚ โ”‚ โ””โ”€ Name "Bar" +โ”‚ โ”œโ”€ PrimitiveType "int" +โ”‚ โ””โ”€ ArrayDimsAndInits +โ”‚ โ”œโ”€ Expression +โ”‚ โ”‚ โ””โ”€ PrimaryExpression +โ”‚ โ”‚ โ””โ”€ PrimaryPrefix +โ”‚ โ”‚ โ””โ”€ Literal "3" +โ”‚ โ””โ”€ Expression +โ”‚ โ””โ”€ PrimaryExpression +โ”‚ โ””โ”€ PrimaryPrefix +โ”‚ โ””โ”€ Literal "2" +โ””โ”€ AllocationExpression + โ”œโ”€ ClassOrInterfaceType "Foo" + โ””โ”€ ArrayDimsAndInits + โ””โ”€ ArrayInitializer + โ”œโ”€ VariableInitializer + โ”‚ โ””โ”€ Expression + โ”‚ โ””โ”€ PrimaryExpression + โ”‚ โ””โ”€ PrimaryPrefix + โ”‚ โ””โ”€ Name "f" + โ””โ”€ VariableInitializer + โ””โ”€ Expression + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "g" {% endhighlight %} {% highlight js %} -ArrayAllocation - + ArrayType - + PrimitiveType "int" - + ArrayDimensions - + ArrayDimExpr - + NumericLiteral "2" - + ArrayTypeDim -ArrayAllocation - + ArrayType - + PrimitiveType "int" - + MarkerAnnotation "Bar" - + ArrayDimensions - + ArrayDimExpr - + NumericLiteral "3" - + ArrayDimExpr - + NumericLiteral "2" -ArrayAllocation - + ArrayType - + ClassOrInterfaceType "Foo" - + ArrayDimensions - + ArrayTypeDim - + ArrayInitializer - + VariableAccess "f" - + VariableAccess "g" +โ”œโ”€ ArrayAllocation +โ”‚ โ””โ”€ ArrayType +โ”‚ โ”œโ”€ PrimitiveType "int" +โ”‚ โ””โ”€ ArrayDimensions +โ”‚ โ”œโ”€ ArrayDimExpr +โ”‚ โ”‚ โ””โ”€ NumericLiteral "2" +โ”‚ โ””โ”€ ArrayTypeDim +โ”œโ”€ ArrayAllocation +โ”‚ โ””โ”€ ArrayType +โ”‚ โ”œโ”€ PrimitiveType "int" +โ”‚ โ”‚ โ””โ”€ MarkerAnnotation "Bar" +โ”‚ โ””โ”€ ArrayDimensions +โ”‚ โ”œโ”€ ArrayDimExpr +โ”‚ โ”‚ โ””โ”€ NumericLiteral "3" +โ”‚ โ””โ”€ ArrayDimExpr +โ”‚ โ””โ”€ NumericLiteral "2" +โ””โ”€ ArrayAllocation + โ””โ”€ ArrayType + โ”‚ โ”œโ”€ ClassOrInterfaceType "Foo" + โ”‚ โ””โ”€ ArrayDimensions + โ”‚ โ””โ”€ ArrayTypeDim + โ””โ”€ ArrayInitializer + โ”œโ”€ VariableAccess "f" + โ””โ”€ VariableAccess "g" {% endhighlight %} @@ -855,23 +848,23 @@ Map.Entry {% highlight js %} -ClassOrInterfaceType "Map.Entry" - + TypeArguments - + TypeArgument - + ReferenceType - + ClassOrInterfaceType "K" - + TypeArgument - + ReferenceType - + ClassOrInterfaceType "V" +โ””โ”€ ClassOrInterfaceType "Map.Entry" + โ””โ”€ TypeArguments + โ”œโ”€ TypeArgument + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "K" + โ””โ”€ TypeArgument + โ””โ”€ ReferenceType + โ””โ”€ ClassOrInterfaceType "V" {% endhighlight %} {% highlight js %} -ClassOrInterfaceType "Entry" - + ClassOrInterfaceType "Map" - + TypeArguments - + ClassOrInterfaceType "K" - + ClassOrInterfaceType "V" +โ””โ”€ ClassOrInterfaceType "Entry" + โ”œโ”€ ClassOrInterfaceType "Map" + โ””โ”€ TypeArguments + โ”œโ”€ ClassOrInterfaceType "K" + โ””โ”€ ClassOrInterfaceType "V" {% endhighlight %} @@ -882,25 +875,25 @@ First.Second.Third {% endhighlight %} {% highlight js %} -ClassOrInterfaceType "First.Second.Third" - + TypeArguments - + TypeArgument - + ReferenceType - + ClassOrInterfaceType "K" - + TypeArguments - + TypeArgument - + ReferenceType - + ClassOrInterfaceType "V" +โ””โ”€ ClassOrInterfaceType "First.Second.Third" + โ”œโ”€ TypeArguments + โ”‚ โ””โ”€ TypeArgument + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "K" + โ””โ”€ TypeArguments + โ””โ”€ TypeArgument + โ””โ”€ ReferenceType + โ””โ”€ ClassOrInterfaceType "V" {% endhighlight %} {% highlight js %} -ClassOrInterfaceType "Third" - - ClassOrInterfaceType "Second" - - ClassOrInterfaceType "First" - - TypeArguments - - ClassOrInterfaceType "K" - - TypeArguments - - ClassOrInterfaceType "V" +โ””โ”€ ClassOrInterfaceType "Third" + โ”œโ”€ ClassOrInterfaceType "Second" + โ”‚ โ””โ”€ ClassOrInterfaceType "First" + โ”‚ โ””โ”€ TypeArguments + โ”‚ โ””โ”€ ClassOrInterfaceType "K" + โ””โ”€ TypeArguments + โ””โ”€ ClassOrInterfaceType "V" {% endhighlight %} @@ -921,24 +914,24 @@ Entry {% highlight js %} -ClassOrInterfaceType "Entry" - + TypeArguments - + TypeArgument - + ReferenceType - + ClassOrInterfaceType "String" - + TypeArgument[@UpperBound = true()] - + WildcardBounds - + ReferenceType - + ClassOrInterfaceType "Node" +โ””โ”€ ClassOrInterfaceType "Entry" + โ””โ”€ TypeArguments + โ”œโ”€ TypeArgument + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "String" + โ””โ”€ TypeArgument[ @UpperBound = true() ] + โ””โ”€ WildcardBounds + โ””โ”€ ReferenceType + โ””โ”€ ClassOrInterfaceType "Node" {% endhighlight %} {% highlight js %} -ClassOrInterfaceType "Entry" - + TypeArguments - + ClassOrInterfaceType "String" - + WildcardType[@UpperBound = true()] - + ClassOrInterfaceType "Node" +โ””โ”€ ClassOrInterfaceType "Entry" + โ””โ”€ TypeArguments + โ”œโ”€ ClassOrInterfaceType "String" + โ””โ”€ WildcardType[ @UpperBound = true() ] + โ””โ”€ ClassOrInterfaceType "Node" {% endhighlight %} @@ -950,16 +943,16 @@ List {% highlight js %} -ClassOrInterfaceType "List" - + TypeArguments - + TypeArgument +โ””โ”€ ClassOrInterfaceType "List" + โ””โ”€ TypeArguments + โ””โ”€ TypeArgument {% endhighlight %} {% highlight js %} -ClassOrInterfaceType "List" - + TypeArguments - + WildcardType +โ””โ”€ ClassOrInterfaceType "List" + โ””โ”€ TypeArguments + โ””โ”€ WildcardType {% endhighlight %} @@ -983,18 +976,18 @@ import java.util.*; {% endhighlight %} {% highlight js %} -ImportDeclaration - + Name "java.util.ArrayList" -ImportDeclaration[@Static=true()] - + Name "java.util.Comparator.reverseOrder" -ImportDeclaration[@ImportOnDemand=true()] - + Name "java.util" +โ”œโ”€ ImportDeclaration +โ”‚ โ””โ”€ Name "java.util.ArrayList" +โ”œโ”€ ImportDeclaration[ @Static=true() ] +โ”‚ โ””โ”€ Name "java.util.Comparator.reverseOrder" +โ””โ”€ ImportDeclaration[ @ImportOnDemand = true() ] + โ””โ”€ Name "java.util" {% endhighlight %} {% highlight js %} -ImportDeclaration "java.util.ArrayList" -ImportDeclaration[@Static=true()] "java.util.Comparator.reverseOrder" -ImportDeclaration[@ImportOnDemand=true()] "java.util" +โ”œโ”€ ImportDeclaration "java.util.ArrayList" +โ”œโ”€ ImportDeclaration[ @Static = true() ] "java.util.Comparator.reverseOrder" +โ””โ”€ ImportDeclaration[ @ImportOnDemand = true() ] "java.util" {% endhighlight %} @@ -1005,14 +998,14 @@ package com.example.tool; {% highlight js %} -PackageDeclaration - + Name "com.example.tool" +โ””โ”€ PackageDeclaration + โ””โ”€ Name "com.example.tool" {% endhighlight %} {% highlight js %} -PackageDeclaration "com.example.tool" -+ ModifierList +โ””โ”€ PackageDeclaration "com.example.tool" + โ””โ”€ ModifierList {% endhighlight %} @@ -1038,32 +1031,32 @@ public void set(final int x, int y) { } {% endhighlight %} {% highlight js %} -ClassOrInterfaceBodyDeclaration - + Annotation - + MarkerAnnotation - + Name "A" - + MethodDeclaration[@Public = true()] - + ResultType[@Void=true] - + MethodDeclarator - + FormalParameters - + FormalParameter[@Final = true()] - + VariableDeclaratorId "x" - + FormalParameter[@Final = false()] - + VariableDeclaratorId "y" +โ””โ”€ ClassOrInterfaceBodyDeclaration + โ”œโ”€ Annotation + โ”‚ โ””โ”€ MarkerAnnotation + โ”‚ โ””โ”€ Name "A" + โ””โ”€ MethodDeclaration[ @Public = true() ] + โ”œโ”€ ResultType[@Void=true] + โ””โ”€ MethodDeclarator + โ””โ”€ FormalParameters + โ”œโ”€ FormalParameter[ @Final = true() ] + โ”‚ โ””โ”€ VariableDeclaratorId "x" + โ””โ”€ FormalParameter[ @Final = false() ] + โ””โ”€ VariableDeclaratorId "y" {% endhighlight %} {% highlight js %} -MethodDeclaration - + ModifierList[@Modifiers=("public")] - + Annotation "A" - + VoidType - + FormalParameters - + FormalParameter - + ModifierList[@Modifiers=("final")] - + VariableDeclaratorId "x" - + FormalParameter - + ModifierList[@Modifiers=()] - + VariableDeclaratorId "y" +โ””โ”€ MethodDeclaration + โ”œโ”€ ModifierList[ @Modifiers = ( "public" ) ] + โ”‚ โ””โ”€ Annotation "A" + โ”œโ”€ VoidType + โ””โ”€ FormalParameters + โ”œโ”€ FormalParameter + โ”‚ โ”œโ”€ ModifierList[ @Modifiers = ("final") ] + โ”‚ โ””โ”€ VariableDeclaratorId "x" + โ””โ”€ FormalParameter + โ”œโ”€ ModifierList[ @Modifiers = () ] + โ””โ”€ VariableDeclaratorId "y" {% endhighlight %} @@ -1076,21 +1069,21 @@ public @A class C {} {% highlight js %} -TypeDeclaration - + Annotation - + MarkerAnnotation - + Name "A" - + ClassOrInterfaceDeclaration[@Public=true()] - + ClassOrInterfaceBody +โ””โ”€ TypeDeclaration + โ”œโ”€ Annotation + โ”‚ โ””โ”€ MarkerAnnotation + โ”‚ โ””โ”€ Name "A" + โ””โ”€ ClassOrInterfaceDeclaration[ @Public = true() ] + โ””โ”€ ClassOrInterfaceBody {% endhighlight %} {% highlight js %} -TypeDeclaration - + ClassOrInterfaceDeclaration - + ModifierList[@Modifiers=("public")] - + MarkerAnnotation "A" - + ClassOrInterfaceBody +โ””โ”€ TypeDeclaration + โ””โ”€ ClassOrInterfaceDeclaration + โ”œโ”€ ModifierList[ @Modifiers = ( "public" ) ] + โ”‚ โ””โ”€ MarkerAnnotation "A" + โ””โ”€ ClassOrInterfaceBody {% endhighlight %} @@ -1275,48 +1268,48 @@ public class Sample { {% endhighlight %} {% highlight js %} -ConstructorDeclaration "Sample" - + FormalParameters - + FormalParameter ... - + NameList - + Name "Exception" - + ExplicitConstructorInvocation - + Arguments - + BlockStatement - + Statement ... -MethodDeclaration - + ResultType - + MethodDeclarator "greet" - + FormatParameters - + FormalParameter ... - + NameList - + Name "Exception" - + Block - + BlockStatement - + Statement ... +โ”œโ”€ ConstructorDeclaration "Sample" +โ”‚ โ”œโ”€ FormalParameters +โ”‚ โ”‚ โ””โ”€ FormalParameter ... +โ”‚ โ”œโ”€ NameList +โ”‚ โ”‚ โ””โ”€ Name "Exception" +โ”‚ โ”œโ”€ ExplicitConstructorInvocation +โ”‚ โ”‚ โ””โ”€ Arguments +โ”‚ โ””โ”€ BlockStatement +โ”‚ โ””โ”€ Statement ... +โ””โ”€ MethodDeclaration + โ”œโ”€ ResultType + โ”œโ”€ MethodDeclarator "greet" + โ”‚ โ””โ”€ FormatParameters + โ”‚ โ””โ”€ FormalParameter ... + โ”œโ”€ NameList + โ”‚ โ””โ”€ Name "Exception" + โ””โ”€ Block + โ””โ”€ BlockStatement + โ””โ”€ Statement ... {% endhighlight %} {% highlight js %} -ConstructorDeclaration "Sample" - + ModifierList - + FormalParameters - + FormalParameter ... - + ThrowsList - + ClassOrInterfaceType ... - + Block - + ExplicitConstructorInvocation - + ArgumentList - + ExpressionStatement -MethodDeclaration "greet" - + ModifierList - + VoidType - + FormalParameters - + FormalParameter ... - + ThrowsList - + ClassOrInterfaceType ... - + Block - + ExpressionStatement +โ”œโ”€ ConstructorDeclaration "Sample" +โ”‚ โ”œโ”€ ModifierList +โ”‚ โ”œโ”€ FormalParameters +โ”‚ โ”‚ โ””โ”€ FormalParameter ... +โ”‚ โ”œโ”€ ThrowsList +โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType ... +โ”‚ โ””โ”€ Block +โ”‚ โ”œโ”€ ExplicitConstructorInvocation +โ”‚ โ”‚ โ””โ”€ ArgumentList +โ”‚ โ””โ”€ ExpressionStatement +โ””โ”€ MethodDeclaration "greet" + โ”œโ”€ ModifierList + โ”œโ”€ VoidType + โ”œโ”€ FormalParameters + โ”‚ โ””โ”€ FormalParameter ... + โ”œโ”€ ThrowsList + โ”‚ โ””โ”€ ClassOrInterfaceType ... + โ””โ”€ Block + โ””โ”€ ExpressionStatement {% endhighlight %} @@ -1328,23 +1321,23 @@ public @interface MyAnnotation { {% endhighlight %} {% highlight js %} -AnnotationTypeDeclaration "MyAnnotation" - + AnnotationTypeBody - + AnnotationTypeMemberDeclaration - + AnnotationMethodDeclaration "value" - + Type ... - + DefaultValue ... +โ””โ”€ AnnotationTypeDeclaration "MyAnnotation" + โ””โ”€ AnnotationTypeBody + โ””โ”€ AnnotationTypeMemberDeclaration + โ””โ”€ AnnotationMethodDeclaration "value" + โ”œโ”€ Type ... + โ””โ”€ DefaultValue ... {% endhighlight %} {% highlight js %} -AnnotationTypeDeclaration "MyAnnotation" - + AnnotationTypeBody - + AnnotationTypeMemberDeclaration - + MethodDeclaration - + ModifierList - + PrimitiveType - + FormalParameters ... - + DefaultValue ... +โ””โ”€ AnnotationTypeDeclaration "MyAnnotation" + โ””โ”€ AnnotationTypeBody + โ””โ”€ AnnotationTypeMemberDeclaration + โ””โ”€ MethodDeclaration + โ”œโ”€ ModifierList + โ”œโ”€ PrimitiveType + โ”œโ”€ FormalParameters ... + โ””โ”€ DefaultValue ... {% endhighlight %} @@ -1370,34 +1363,34 @@ try { {% endhighlight %} {% highlight js %} -TryStatement - + Block - + CatchStatement - + FormalParameter - + Annotation "A" - + Type - + ReferenceType - + ClassOrInterfaceType "IOException" - + Type - + ReferenceType - + ClassOrInterfaceType "IllegalArgumentException" - + VariableDeclaratorId - + Block +โ””โ”€ TryStatement + โ”œโ”€ Block + โ””โ”€ CatchStatement + โ”œโ”€ FormalParameter + โ”‚ โ”œโ”€ Annotation "A" + โ”‚ โ”œโ”€ Type + โ”‚ โ”‚ โ””โ”€ ReferenceType + โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType "IOException" + โ”‚ โ”œโ”€ Type + โ”‚ โ”‚ โ””โ”€ ReferenceType + โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType "IllegalArgumentException" + โ”‚ โ””โ”€ VariableDeclaratorId + โ””โ”€ Block {% endhighlight %} {% highlight js %} -TryStatement - + Block - + CatchClause - + CatchParameter - + ModifierList - + Annotation "A" - + UnionType - + ClassOrInterfaceType "IOException" - + ClassOrInterfaceType "IllegalArgumentException" - + VariableDeclaratorId - + Block +โ””โ”€ TryStatement + โ”œโ”€ Block + โ””โ”€ CatchClause + โ”œโ”€ CatchParameter + โ”‚ โ”œโ”€ ModifierList + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ”œโ”€ UnionType + โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType "IOException" + โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType "IllegalArgumentException" + โ”‚ โ””โ”€ VariableDeclaratorId + โ””โ”€ Block {% endhighlight %} @@ -1410,81 +1403,81 @@ c -> {} {% endhighlight %} {% highlight js %} -Expression - + PrimaryExpression - + PrimaryPrefix - + LambdaExpression - + VariableDeclaratorId "a" - + VariableDeclaratorId "b" - + Block +โ””โ”€ Expression + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ LambdaExpression + โ”œโ”€ VariableDeclaratorId "a" + โ”œโ”€ VariableDeclaratorId "b" + โ””โ”€ Block -Expression - + PrimaryExpression - + PrimaryPrefix - + LambdaExpression - + VariableDeclaratorId "c" - + Block +โ””โ”€ Expression + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ LambdaExpression + โ”œโ”€ VariableDeclaratorId "c" + โ””โ”€ Block -Expression - + PrimaryExpression - + PrimaryPrefix - + LambdaExpression - + FormalParameters - + FormalParameter - + Annotation "A" - + ... - + VariableDeclaratorId "d" - + Block +โ””โ”€ Expression + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ LambdaExpression + โ”œโ”€ FormalParameters + โ”‚ โ””โ”€ FormalParameter + โ”‚ โ”œโ”€ Annotation "A" + โ”‚ โ”‚ โ””โ”€ ... + โ”‚ โ””โ”€ VariableDeclaratorId "d" + โ””โ”€ Block -Expression - + PrimaryExpression - + PrimaryPrefix - + LambdaExpression - + FormalParameters - + FormalParameter - + Annotation "A" - + ... - + Type - + PrimitiveType - + VariableDeclaratorId "e" - + Block +โ””โ”€ Expression + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ LambdaExpression + โ”œโ”€ FormalParameters + โ”‚ โ””โ”€ FormalParameter + โ”‚ โ”œโ”€ Annotation "A" + โ”‚ โ”‚ โ””โ”€ ... + โ”‚ โ”œโ”€ Type + โ”‚ โ”‚ โ””โ”€ PrimitiveType + โ”‚ โ””โ”€ VariableDeclaratorId "e" + โ””โ”€ Block {% endhighlight %} {% highlight js %} -LambdaExpression - + LambdaParameters - + LambdaParameter - + ModifierList - + VariableDeclaratorId "a" - + LambdaParameter - + ModifierList - + VariableDeclaratorId "b" - + Block +โ””โ”€ LambdaExpression + โ”œโ”€ LambdaParameters + โ”‚ โ”œโ”€ LambdaParameter + โ”‚ โ”‚ โ”œโ”€ ModifierList + โ”‚ โ”‚ โ””โ”€ VariableDeclaratorId "a" + โ”‚ โ””โ”€ LambdaParameter + โ”‚ โ”œโ”€ ModifierList + โ”‚ โ””โ”€ VariableDeclaratorId "b" + โ””โ”€ Block -+ LambdaExpression - + LambdaParameters - + LambdaParameter - + ModifierList - + VariableDeclaratorId "c" - + Block +โ””โ”€ LambdaExpression + โ”œโ”€ LambdaParameters + โ”‚ โ””โ”€ LambdaParameter + โ”‚ โ”œโ”€ ModifierList + โ”‚ โ””โ”€ VariableDeclaratorId "c" + โ””โ”€ Block -LambdaExpression - + LambdaParameters - + LambdaParameter - + ModifierList - + Annotation "A" - + VariableDeclaratorId "d" - + Block +โ””โ”€ LambdaExpression + โ”œโ”€ LambdaParameters + โ”‚ โ””โ”€ LambdaParameter + โ”‚ โ”œโ”€ ModifierList + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ VariableDeclaratorId "d" + โ””โ”€ Block -LambdaExpression - + LambdaParameters - + LambdaParameter - + ModifierList - + Annotation "A" - + PrimitiveType "int" - + VariableDeclaratorId "e" - + Block +โ””โ”€ LambdaExpression + โ”œโ”€ LambdaParameters + โ”‚ โ””โ”€ LambdaParameter + โ”‚ โ”œโ”€ ModifierList + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ VariableDeclaratorId "e" + โ””โ”€ Block {% endhighlight %} @@ -1507,25 +1500,25 @@ LambdaExpression {% endhighlight %} {% highlight js %} -FormalParameters[@ParameterCount = 1] - + FormalParameter[@ReceiverParameter=true()] - + ClassOrInterfaceType - + Annotation "A" - + VariableDeclaratorId[@Image="this", @ReceiverParameter=true()] - + FormalParameter - + ClassOrInterfaceType - + VariableDeclaratorId "other" +โ””โ”€ FormalParameters[ @ParameterCount = 1 ] + โ”œโ”€ FormalParameter[ @ReceiverParameter = true() ] + โ”‚ โ”œโ”€ ClassOrInterfaceType + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ VariableDeclaratorId[ @Image = "this" ][ @ReceiverParameter = true() ] + โ””โ”€ FormalParameter + โ”œโ”€ ClassOrInterfaceType + โ””โ”€ VariableDeclaratorId "other" {% endhighlight %} {% highlight js %} -FormalParameters[@ParameterCount = 1] - + ReceiverParameter - + ClassOrInterfaceType - + Annotation "A" - + FormalParameter - + ModifierList - + ClassOrInterfaceType - + VariableDeclaratorId "other" +โ””โ”€ FormalParameters[ @ParameterCount = 1 ] + โ”œโ”€ ReceiverParameter + โ”‚ โ””โ”€ ClassOrInterfaceType + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ FormalParameter + โ”œโ”€ ModifierList + โ”œโ”€ ClassOrInterfaceType + โ””โ”€ VariableDeclaratorId "other" {% endhighlight %} @@ -1543,20 +1536,20 @@ FormalParameters[@ParameterCount = 1] {% endhighlight %} {% highlight js %} -+ FormalParameter[ @Varargs = true() ] - + Type - + PrimitiveType "int" - + VariableDeclaratorId "is" +โ””โ”€ FormalParameter[ @Varargs = true() ] + โ”œโ”€ Type + โ”‚ โ””โ”€ PrimitiveType "int" + โ””โ”€ VariableDeclaratorId "is" {% endhighlight %} {% highlight js %} -+ FormalParameter[ @Varargs = true() ] - + ArrayType - + PrimitiveType "int" - + ArrayDimensions - + ArrayTypeDim[ @Varargs = true() ] - + VariableDeclaratorId "is" +โ””โ”€ FormalParameter[ @Varargs = true() ] + โ”œโ”€ ArrayType + โ”‚ โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ ArrayDimensions + โ”‚ โ””โ”€ ArrayTypeDim[ @Varargs = true() ] + โ””โ”€ VariableDeclaratorId "is" {% endhighlight %} @@ -1569,14 +1562,14 @@ n/a (parse error) {% highlight js %} -+ FormalParameter[ @Varargs = true() ] - + ModifierList - + ArrayType - + PrimitiveType "int" - + ArrayDimensions - + ArrayTypeDim[ @Varargs = true() ] - + Annotation "A" - + VariableDeclaratorId "is" +โ””โ”€ FormalParameter[ @Varargs = true() ] + โ”œโ”€ ModifierList + โ”œโ”€ ArrayType + โ”‚ โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ ArrayDimensions + โ”‚ โ””โ”€ ArrayTypeDim[ @Varargs = true() ] + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ VariableDeclaratorId "is" {% endhighlight %} @@ -1586,25 +1579,25 @@ n/a (parse error) {% endhighlight %} {% highlight js %} -+ FormalParameter[ @Varargs = true() ] - + ModifierList - + Type - + ReferenceType - + PrimitiveType "int" - + VariableDeclaratorId "is" +โ””โ”€ FormalParameter[ @Varargs = true() ] + โ”œโ”€ ModifierList + โ”œโ”€ Type + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ PrimitiveType "int" + โ””โ”€ VariableDeclaratorId "is" {% endhighlight %} {% highlight js %} -+ FormalParameter[ @Varargs = true() ] - + ModifierList - + ArrayType - + PrimitiveType "int" - + ArrayDimensions - + ArrayTypeDim - + ArrayTypeDim[ @Varargs = true() ] - + Annotation "A" - + VariableDeclaratorId "is" +โ””โ”€ FormalParameter[ @Varargs = true() ] + โ”œโ”€ ModifierList + โ”œโ”€ ArrayType + โ”‚ โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ ArrayDimensions + โ”‚ โ”œโ”€ ArrayTypeDim + โ”‚ โ””โ”€ ArrayTypeDim[ @Varargs = true() ] + โ”‚ โ””โ”€ Annotation "A" + โ””โ”€ VariableDeclaratorId "is" {% endhighlight %} @@ -1625,7 +1618,7 @@ void foo(); {% highlight js %} โ””โ”€ MethodDeclaration - โ””โ”€ ResultType[@Void=true()] + โ””โ”€ ResultType[ @Void = true() ] โ””โ”€ MethodDeclarator โ””โ”€ FormalParameters {% endhighlight %} @@ -1645,7 +1638,7 @@ int foo(); {% highlight js %} โ””โ”€ MethodDeclaration - โ””โ”€ ResultType[@Void=false()] + โ””โ”€ ResultType[ @Void = false() ] โ””โ”€ Type โ””โ”€ PrimitiveType โ””โ”€ MethodDeclarator @@ -1681,46 +1674,46 @@ try (InputStream in = new FileInputStream(); OutputStream out = new FileOutputSt {% endhighlight %} {% highlight js %} -TryStatement - + ResourceSpecification - + Resources - + Resource - + Type - + ReferenceType - + ClassOrInterfaceType "InputStream" - + VariableDeclaratorId "in" - + Expression - + ... - + Resource - + Type - + ReferenceType - + ClassOrInterfaceType "OutputStream" - + VariableDeclaratorId "in" - + Expression - + ... +โ””โ”€ TryStatement + โ””โ”€ ResourceSpecification + โ””โ”€ Resources + โ”œโ”€ Resource + โ”‚ โ”œโ”€ Type + โ”‚ โ”‚ โ””โ”€ ReferenceType + โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType "InputStream" + โ”‚ โ”œโ”€ VariableDeclaratorId "in" + โ”‚ โ””โ”€ Expression + โ”‚ โ””โ”€ ... + โ””โ”€ Resource + โ”œโ”€ Type + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "OutputStream" + โ”œโ”€ VariableDeclaratorId "in" + โ””โ”€ Expression + โ””โ”€ ... {% endhighlight %} {% highlight js %} -TryStatement - + ResourceList[@TrailingSemiColon=true()] - + Resource[@ConciseResource=false()] - + LocalVariableDeclaration - + ModifierList - + Type - + VariableDeclarator - + VariableDeclaratorId "in" - + ConstructorCall - + ClassOrInterfaceType - + ArgumentList - + Resource[@ConciseResource=false()] - + LocalVariableDeclaration - + ModifierList - + Type - + VariableDeclarator - + VariableDeclaratorId "in" - + ConstructorCall - + ClassOrInterfaceType - + ArgumentList +โ””โ”€ TryStatement + โ””โ”€ ResourceList[ @TrailingSemiColon = true() ] + โ”œโ”€ Resource[ @ConciseResource = false() ] + โ”‚ โ””โ”€ LocalVariableDeclaration + โ”‚ โ”œโ”€ ModifierList + โ”‚ โ”œโ”€ Type + โ”‚ โ””โ”€ VariableDeclarator + โ”‚ โ”œโ”€ VariableDeclaratorId "in" + โ”‚ โ””โ”€ ConstructorCall + โ”‚ โ”œโ”€ ClassOrInterfaceType + โ”‚ โ””โ”€ ArgumentList + โ””โ”€ Resource[ @ConciseResource = false() ] + โ””โ”€ LocalVariableDeclaration + โ”œโ”€ ModifierList + โ”œโ”€ Type + โ””โ”€ VariableDeclarator + โ”œโ”€ VariableDeclaratorId "in" + โ””โ”€ ConstructorCall + โ”œโ”€ ClassOrInterfaceType + โ””โ”€ ArgumentList {% endhighlight %} @@ -1731,18 +1724,18 @@ try (in) {} {% endhighlight %} {% highlight js %} -TryStatement - + ResourceSpecification - + Resources - + Resource - + Name "in" +โ””โ”€ TryStatement + โ””โ”€ ResourceSpecification + โ””โ”€ Resources + โ””โ”€ Resource + โ””โ”€ Name "in" {% endhighlight %} {% highlight js %} -TryStatement - + ResourceList[@TrailingSemiColon=false()] - + Resource[@ConciseResource=true()] - + VariableAccess[@VariableName='in'] +โ””โ”€ TryStatement + โ””โ”€ ResourceList[ @TrailingSemiColon = false() ] + โ””โ”€ Resource[ @ConciseResource = true() ] + โ””โ”€ VariableAccess[ @VariableName = 'in' ] {% endhighlight %} @@ -1777,41 +1770,47 @@ d--; {% endhighlight %} {% highlight js %} -StatementExpression - + PreIncrementExpression - + PrimaryExpression - + PrimaryPrefix - + Name "a" -StatementExpression - + PreDecrementExpression - + PrimaryExpression - + PrimaryPrefix - + Name "b" -StatementExpression - + PostfixExpression "++" - + PrimaryExpression - + PrimaryPrefix - + Name "c" -StatementExpression - + PostfixExpression "--" - + PrimaryExpression - + PrimaryPrefix - + Name "d" +โ””โ”€ StatementExpression + โ””โ”€ PreIncrementExpression + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "a" + +โ””โ”€ StatementExpression + โ””โ”€ PreDecrementExpression + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "b" + +โ””โ”€ StatementExpression + โ””โ”€ PostfixExpression "++" + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "c" + +โ””โ”€ StatementExpression + โ””โ”€ PostfixExpression "--" + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "d" {% endhighlight %} {% highlight js %} -StatementExpression - + UnaryExpression[@Prefix=true()][@Operator="++"] - + VariableAccess "a" -StatementExpression - + UnaryExpression[@Prefix=true()][@Operator="--"] - + VariableAccess "b" -StatementExpression - + UnaryExpression[@Prefix=false()][@Operator="++"] - + VariableAccess "c" -StatementExpression - + UnaryExpression[@Prefix=false()][@Operator="--"] - + VariableAccess "d" +โ””โ”€ StatementExpression + โ””โ”€ UnaryExpression[ @Prefix = true() ][ @Operator = '++' ] + โ””โ”€ VariableAccess "a" + +โ””โ”€ StatementExpression + โ””โ”€ UnaryExpression[ @Prefix = true() ][ @Operator = '--' ] + โ””โ”€ VariableAccess "b" + +โ””โ”€ StatementExpression + โ””โ”€ UnaryExpression[ @Prefix = false() ][ @Operator = '++' ] + โ””โ”€ VariableAccess "c" + +โ””โ”€ StatementExpression + โ””โ”€ UnaryExpression[ @Prefix = false() ][ @Operator = '--' ] + โ””โ”€ VariableAccess "d" {% endhighlight %} @@ -1822,24 +1821,24 @@ StatementExpression {% endhighlight %} {% highlight js %} -UnaryExpression[@Image=null] - + UnaryExpressionNotPlusMinus[@Image="~"] - + PrimaryExpression - + PrimaryPrefix - + Name "a" +โ””โ”€ UnaryExpression[ @Image = null ] + โ””โ”€ UnaryExpressionNotPlusMinus[ @Image = '~' ] + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "a" -UnaryExpression[@Image="+"] - + PrimaryExpression - + PrimaryPrefix - + Name "a" +โ””โ”€ UnaryExpression[ @Image = '+' ] + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "a" {% endhighlight %} {% highlight js %} -+ UnaryExpression[@Operator="~"] - + VariableAccess "a" +โ””โ”€ UnaryExpression[ @Operator = '~' ] + โ””โ”€ VariableAccess "a" -+ UnaryExpression[@Operator="+"] - + VariableAccess "a" +โ””โ”€ UnaryExpression[ @Operator = '+' ] + โ””โ”€ VariableAccess "a" {% endhighlight %} @@ -1865,30 +1864,30 @@ int i = 1 * 2 * 3 % 4; {% endhighlight %} {% highlight js %} -Expression - + MultiplicativeExpression "%" - + PrimaryExpression - + PrimaryPrefix - + Literal "1" - + PrimaryExpression - + PrimaryPrefix - + Literal "2" - + PrimaryExpression - + PrimaryPrefix - + Literal "3" - + PrimaryExpression - + PrimaryPrefix - + Literal "4" +โ””โ”€ Expression + โ””โ”€ MultiplicativeExpression "%" + โ”œโ”€ PrimaryExpression + โ”‚ โ””โ”€ PrimaryPrefix + โ”‚ โ””โ”€ Literal "1" + โ”œโ”€ PrimaryExpression + โ”‚ โ””โ”€ PrimaryPrefix + โ”‚ โ””โ”€ Literal "2" + โ”œโ”€ PrimaryExpression + โ”‚ โ””โ”€ PrimaryPrefix + โ”‚ โ””โ”€ Literal "3" + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Literal "4" {% endhighlight %} {% highlight js %} -InfixExpression[@Operator='%'] - + InfixExpression[@Operator='*'] - + InfixExpression[@Operator='*'] - + NumericLiteral[@ValueAsInt=1] - + NumericLiteral[@ValueAsInt=2] - + NumericLiteral[@ValueAsInt=3] - + NumericLiteral[@ValueAsInt=4] +โ””โ”€ InfixExpression[ @Operator = '%' ] + โ”œโ”€ InfixExpression[@Operator='*'] + โ”‚ โ”œโ”€ InfixExpression[@Operator='*'] + โ”‚ โ”‚ โ”œโ”€ NumericLiteral[@ValueAsInt=1] + โ”‚ โ”‚ โ””โ”€ NumericLiteral[@ValueAsInt=2] + โ”‚ โ””โ”€ NumericLiteral[@ValueAsInt=3] + โ””โ”€ NumericLiteral[@ValueAsInt=4] {% endhighlight %} From 08f3776a2aa2f9e3c5ec43f59b66fe0a3df0e75d Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 10 Jun 2023 20:33:42 +0200 Subject: [PATCH 174/347] Update migration guide (Java AST, Annotations) --- docs/pages/pmd/userdocs/migrating_to_pmd7.md | 209 ++++++++++++------- 1 file changed, 135 insertions(+), 74 deletions(-) diff --git a/docs/pages/pmd/userdocs/migrating_to_pmd7.md b/docs/pages/pmd/userdocs/migrating_to_pmd7.md index 1fd805382a..2e0496f54e 100644 --- a/docs/pages/pmd/userdocs/migrating_to_pmd7.md +++ b/docs/pages/pmd/userdocs/migrating_to_pmd7.md @@ -274,13 +274,13 @@ XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: {% endhighlight %} {% highlight js %} -โ””โ”€ Annotation - โ””โ”€ MarkerAnnotation +โ””โ”€ Annotation "A" + โ””โ”€ MarkerAnnotation "A" โ””โ”€ Name "A" {% endhighlight %} {% highlight js %} -โ””โ”€ Annotation +โ””โ”€ Annotation "A" โ””โ”€ ClassOrInterfaceType "A" {% endhighlight %} @@ -292,8 +292,8 @@ XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: {% highlight js %} -โ””โ”€ Annotation - โ””โ”€ NormalAnnotation +โ””โ”€ Annotation "A" + โ””โ”€ NormalAnnotation "A" โ””โ”€ Name "A" {% endhighlight %} @@ -313,8 +313,8 @@ XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: {% highlight js %} -โ””โ”€ Annotation - โ””โ”€ NormalAnnotation +โ””โ”€ Annotation "A" + โ””โ”€ NormalAnnotation "A" โ”œโ”€ Name "A" โ””โ”€ MemberValuePairs โ””โ”€ MemberValuePair "value" @@ -342,8 +342,8 @@ XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: {% highlight js %} -โ””โ”€ Annotation - โ””โ”€ SingleMemberAnnotation +โ””โ”€ Annotation "A" + โ””โ”€ SingleMemberAnnotation "A" โ”œโ”€ Name "A" โ””โ”€ MemberValue โ””โ”€ PrimaryExpression @@ -369,8 +369,8 @@ XPath 1.0 and 2.0 queries. Here's a list of known incompatibilities: {% highlight js %} -โ””โ”€ Annotation - โ””โ”€ NormalAnnotation +โ””โ”€ Annotation "A" + โ””โ”€ NormalAnnotation "A" โ”œโ”€ Name "A" โ””โ”€ MemberValuePairs โ”œโ”€ MemberValuePair "value" @@ -418,11 +418,11 @@ public void set(int x) { } {% highlight js %} โ””โ”€ ClassOrInterfaceBodyDeclaration - โ”œโ”€ Annotation - โ”‚ โ””โ”€ MarkerAnnotation + โ”œโ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" โ”‚ โ””โ”€ Name "A" โ””โ”€ MethodDeclaration - โ”œโ”€ ResultType[@Void=true] + โ”œโ”€ ResultType[ @Void = true ] โ”œโ”€ ... {% endhighlight %} @@ -430,6 +430,7 @@ public void set(int x) { } โ””โ”€ MethodDeclaration โ”œโ”€ ModifierList โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ ClassOrInterfaceType "A" โ”œโ”€ VoidType โ”œโ”€ ... {% endhighlight %} @@ -445,20 +446,20 @@ Top-level type declaration {% highlight js %} โ””โ”€ TypeDeclaration - โ”œโ”€ Annotation - โ”‚ โ””โ”€ MarkerAnnotation + โ”œโ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" โ”‚ โ””โ”€ Name "A" - โ””โ”€ ClassOrInterfaceDeclaration + โ””โ”€ ClassOrInterfaceDeclaration "C" โ””โ”€ ClassOrInterfaceBody {% endhighlight %} {% highlight js %} -โ””โ”€ TypeDeclaration - โ””โ”€ ClassOrInterfaceDeclaration - โ”œโ”€ ModifierList - โ”‚ โ””โ”€ Annotation "A" - โ””โ”€ ClassOrInterfaceBody +โ””โ”€ ClassOrInterfaceDeclaration + โ”œโ”€ ModifierList + โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ ClassOrInterfaceType "A" + โ””โ”€ ClassOrInterfaceBody {% endhighlight %} @@ -467,19 +468,35 @@ Top-level type declaration Cast expression {% highlight java %} -(@A T.@B S) expr +var x = (@A T.@B S) expr; {% endhighlight %} -N/A (Parse error) +{% highlight js %} +โ””โ”€ CastExpression + โ”œโ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" + โ”‚ โ””โ”€ Name "A" + โ”œโ”€ Type + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "T.S" + โ”‚ โ””โ”€ Annotation "B" + โ”‚ โ””โ”€ MarkerAnnotation "B" + โ”‚ โ””โ”€ Name "B" + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "expr" +{% endhighlight %} {% highlight js %} โ””โ”€ CastExpression โ”œโ”€ ClassOrInterfaceType "S" + โ”‚ โ”œโ”€ ClassOrInterfaceType "T" + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType "A" โ”‚ โ””โ”€ Annotation "B" - โ”‚ โ””โ”€ ClassOrInterfaceType "T" - โ”‚ โ””โ”€ Annotation "A" - โ””โ”€ (Expression `expr`) + โ”‚ โ””โ”€ ClassOrInterfaceType "B" + โ””โ”€ VariableAccess "expr" {% endhighlight %} @@ -487,25 +504,33 @@ N/A (Parse error) Cast expression with intersection {% highlight java %} -(@A T & S) expr +var x = (@A T & S) expr; {% endhighlight %} {% highlight js %} โ””โ”€ CastExpression - โ”œโ”€ MarkerAnnotation "A" - โ”œโ”€ ClassOrInterfaceType "T" - โ”œโ”€ ClassOrInterfaceType "S" - โ””โ”€ (Expression `expr`) + โ”œโ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" + โ”‚ โ””โ”€ Name "A" + โ”œโ”€ Type + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "T" + โ”œโ”€ ReferenceType + โ”‚ โ””โ”€ ClassOrInterfaceType "S" + โ””โ”€ PrimaryExpression + โ””โ”€ PrimaryPrefix + โ””โ”€ Name "expr" {% endhighlight %} {% highlight js %} โ””โ”€ CastExpression - โ”œโ”€ IntersectionType - โ”‚ โ”œโ”€ ClassOrInterfaceType "T" - โ”‚ โ”‚ โ””โ”€ Annotation "A" - โ”‚ โ””โ”€ ClassOrInterfaceType "S" - โ””โ”€ (Expression `expr`) + โ”œโ”€ IntersectionType + โ”‚ โ”œโ”€ ClassOrInterfaceType "T" + โ”‚ โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ”‚ โ””โ”€ ClassOrInterfaceType "A" + โ”‚ โ””โ”€ ClassOrInterfaceType "S" + โ””โ”€ VariableAccess "expr" {% endhighlight %} Notice @A binds to T, not T & S @@ -521,10 +546,10 @@ new @A T() {% highlight js %} โ””โ”€ AllocationExpression - โ”œโ”€ MarkerAnnotation "A" - โ”œโ”€ Type - โ”‚ โ””โ”€ ReferenceType - โ”‚ โ””โ”€ ClassOrInterfaceType "T" + โ”œโ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" + โ”‚ โ””โ”€ Name "A" + โ”œโ”€ ClassOrInterfaceType "T" โ””โ”€ Arguments {% endhighlight %} @@ -533,7 +558,8 @@ new @A T() โ””โ”€ ConstructorCall โ”œโ”€ ClassOrInterfaceType "T" โ”‚ โ””โ”€ Annotation "A" - โ””โ”€ ArgumentsList + โ”‚ โ””โ”€ ClassOrInterfaceType "A" + โ””โ”€ ArgumentList {% endhighlight %} @@ -546,23 +572,27 @@ new @A int[0] {% highlight js %} โ””โ”€ AllocationExpression - โ”œโ”€ MarkerAnnotation "A" - โ”œโ”€ Type - โ”‚ โ””โ”€ PrimitiveType "int" + โ”œโ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" + โ”‚ โ””โ”€ Name "A" + โ”œโ”€ PrimitiveType "int" โ””โ”€ ArrayDimsAndInits โ””โ”€ Expression โ””โ”€ PrimaryExpression - โ””โ”€ Literal "0" + โ””โ”€ PrimaryPrefix + โ””โ”€ Literal "0" {% endhighlight %} {% highlight js %} โ””โ”€ ArrayAllocation - โ”œโ”€ PrimitiveType "int" - โ”‚ โ””โ”€ Annotation "A" - โ””โ”€ ArrayAllocationDims - โ””โ”€ ArrayDimExpr - โ””โ”€ NumericLiteral "0" + โ””โ”€ ArrayType + โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ ClassOrInterfaceType "A" + โ””โ”€ ArrayDimensions + โ””โ”€ ArrayDimExpr + โ””โ”€ NumericLiteral "0" {% endhighlight %} @@ -570,23 +600,40 @@ new @A int[0] Array type {% highlight java %} -@A int @B[] +@A int @B[] x; {% endhighlight %} -N/A (parse error) +{% highlight js %} +โ””โ”€ LocalVariableDeclaration + โ”œโ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" + โ”‚ โ””โ”€ Name "A" + โ”œโ”€ Type[ @ArrayType = true() ] + โ”‚ โ””โ”€ ReferenceType + โ”‚ โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ Annotation "B" + โ”‚ โ””โ”€ MarkerAnnotation "B" + โ”‚ โ””โ”€ Name "B" + โ””โ”€ VariableDeclarator + โ””โ”€ VariableDeclaratorId "x" +{% endhighlight %} {% highlight js %} -โ””โ”€ ArrayType - โ”œโ”€ PrimitiveType "int" - โ”‚ โ””โ”€ Annotation "A" - โ””โ”€ ArrayTypeDims - โ””โ”€ ArrayTypeDim - โ””โ”€ Annotation "B" +โ””โ”€ LocalVariableDeclaration + โ”œโ”€ ModifierList + โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ ClassOrInterfaceType "A" + โ”œโ”€ ArrayType + โ”‚ โ”œโ”€ PrimitiveType "int" + โ”‚ โ””โ”€ ArrayDimensions + โ”‚ โ””โ”€ ArrayTypeDim + โ”‚ โ””โ”€ Annotation "B" + โ”‚ โ””โ”€ ClassOrInterfaceType "B" + โ””โ”€ VariableDeclarator + โ””โ”€ VariableDeclaratorId "x" {% endhighlight %} -Notice @A binds to int, not int[] - @@ -598,14 +645,19 @@ Type parameters {% highlight js %} โ””โ”€ TypeParameters - โ”œโ”€ MarkerAnnotation "A" โ”œโ”€ TypeParameter "T" - โ”œโ”€ MarkerAnnotation "B" + โ”‚ โ””โ”€ Annotation "A" + โ”‚ โ””โ”€ MarkerAnnotation "A" + โ”‚ โ””โ”€ Name "A" โ””โ”€ TypeParameter "S" - โ”œโ”€ MarkerAnnotation "C" + โ”œโ”€ Annotation "B" + โ”‚ โ””โ”€ MarkerAnnotation "B" + โ”‚ โ””โ”€ Name "B" โ””โ”€ TypeBound - โ””โ”€ ReferenceType - โ””โ”€ ClassOrInterfaceType "Object" + โ”œโ”€ Annotation "C" + โ”‚ โ””โ”€ MarkerAnnotation "C" + โ”‚ โ””โ”€ Name "C" + โ””โ”€ ClassOrInterfaceType "Object" {% endhighlight %} @@ -613,10 +665,13 @@ Type parameters โ””โ”€ TypeParameters โ”œโ”€ TypeParameter "T" โ”‚ โ””โ”€ Annotation "A" - โ””โ”€ TypeParameter "S" + โ”‚ โ””โ”€ ClassOrInterfaceType "A" + โ””โ”€ TypeParameter "S" [ @TypeBound = true() ] โ”œโ”€ Annotation "B" + โ”‚ โ””โ”€ ClassOrInterfaceType "B" โ””โ”€ ClassOrInterfaceType "Object" โ””โ”€ Annotation "C" + โ””โ”€ ClassOrInterfaceType "C" {% endhighlight %}