From abdb96238dd8f25bff31af3ec71cae1479dbd629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 28 Nov 2017 18:49:23 +0100 Subject: [PATCH 001/413] Improve builders interface --- .../builders/MultiValuePropertyBuilder.java | 6 +++-- .../SinglePackagedPropertyBuilder.java | 26 ++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) 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 index e3be94eac2..6405dd5770 100644 --- 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 @@ -4,7 +4,9 @@ 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; @@ -36,8 +38,8 @@ public abstract class MultiValuePropertyBuilder val) { - this.defaultValues = val; + public T defaultValues(Collection val) { + this.defaultValues = new ArrayList<>(val); return (T) this; } 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 index 3fb0b755bf..2f7219d14c 100644 --- 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 @@ -5,6 +5,7 @@ package net.sourceforge.pmd.properties.builders; import java.util.Arrays; +import java.util.Collection; /** @@ -22,12 +23,35 @@ public abstract class SinglePackagedPropertyBuilder packs) { + if (packs != null) { + this.legalPackageNames = packs.toArray(new String[0]); + } + return (T) this; + } + } From 539d541ffdde810fbc1c0911a7001593013f5694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 28 Nov 2017 19:00:59 +0100 Subject: [PATCH 002/413] Hide factories of MethodProperty, MethodMultiProperty, FileProperty from XPath rules refs #762 --- .../properties/PropertyDescriptorUtil.java | 12 ++-- .../pmd/properties/MethodPropertyTest.java | 57 +++++++++++++++---- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java index 43db194d09..c55f80765e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java @@ -15,13 +15,14 @@ import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder * @author Clément Fournier * @since 6.0.0 */ +// TODO make an enum public class PropertyDescriptorUtil { private static final Map> DESCRIPTOR_FACTORIES_BY_TYPE; static { - Map> temp = new HashMap<>(18); + Map> temp = new HashMap<>(16); temp.put("Boolean", BooleanProperty.extractor()); temp.put("List[Boolean]", BooleanMultiProperty.extractor()); @@ -30,7 +31,6 @@ public class PropertyDescriptorUtil { temp.put("Character", CharacterProperty.extractor()); temp.put("List[Character]", CharacterMultiProperty.extractor()); - temp.put("Integer", IntegerProperty.extractor()); temp.put("List[Integer]", IntegerMultiProperty.extractor()); temp.put("Long", LongProperty.extractor()); @@ -44,10 +44,10 @@ public class PropertyDescriptorUtil { temp.put("Class", TypeProperty.extractor()); temp.put("List[Class]", TypeMultiProperty.extractor()); - temp.put("Method", MethodProperty.extractor()); - temp.put("List[Method]", MethodMultiProperty.extractor()); - temp.put("File", FileProperty.extractor()); + // temp.put("Method", MethodProperty.extractor()); // hidden, see #762 + // temp.put("List[Method]", MethodMultiProperty.extractor()); // ditto + // temp.put("File", FileProperty.extractor()); // ditto DESCRIPTOR_FACTORIES_BY_TYPE = Collections.unmodifiableMap(temp); } @@ -56,7 +56,7 @@ public class PropertyDescriptorUtil { private PropertyDescriptorUtil() { } - + /** * Returns the full mappings from type ids to extractors. * diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java index 5d675607b4..392e99f12b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java @@ -10,12 +10,15 @@ import static org.junit.Assert.assertTrue; import java.lang.reflect.Method; 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.modules.MethodPropertyModule; import net.sourceforge.pmd.util.ClassUtil; + /** * Evaluates the functionality of the MethodProperty descriptor by testing its * ability to catch creation errors (illegal args), flag invalid methods per the @@ -33,7 +36,7 @@ public class MethodPropertyTest extends AbstractPackagedPropertyDescriptorTester private static final String[] METHOD_SIGNATURES = {"String#indexOf(int)", "String#substring(int,int)", "java.lang.String#substring(int,int)", "Integer#parseInt(String)", "java.util.HashMap#put(Object,Object)", - "HashMap#containsKey(Object)", }; + "HashMap#containsKey(Object)",}; public MethodPropertyTest() { @@ -41,14 +44,24 @@ public class MethodPropertyTest extends AbstractPackagedPropertyDescriptorTester } + @Override + @Test + public void testMissingPackageNames() { + Map attributes = getPropertyDescriptorValues(); + attributes.remove(PropertyDescriptorField.LEGAL_PACKAGES); + new MethodProperty("p", "d", ALL_METHODS[1], null, 1.0f); // no exception, null is ok + new MethodMultiProperty("p", "d", new Method[]{ALL_METHODS[2], ALL_METHODS[3]}, null, 1.0f); // no exception, null is ok + } + + @Test public void testAsStringOn() { Method method; - for (int i = 0; i < METHOD_SIGNATURES.length; i++) { - method = ValueParserConstants.METHOD_PARSER.valueOf(METHOD_SIGNATURES[i]); - assertNotNull("Unable to identify method: " + METHOD_SIGNATURES[i], method); + for (String methodSignature : METHOD_SIGNATURES) { + method = ValueParserConstants.METHOD_PARSER.valueOf(methodSignature); + assertNotNull("Unable to identify method: " + methodSignature, method); } } @@ -86,28 +99,50 @@ public class MethodPropertyTest extends AbstractPackagedPropertyDescriptorTester @Override protected PropertyDescriptor createProperty() { - return new MethodProperty("methodProperty", "asdf", ALL_METHODS[1], new String[] {"java.lang", "org.apache"}, - 1.0f); + return new MethodProperty("methodProperty", "asdf", ALL_METHODS[1], new String[]{"java.lang", "org.apache"}, + 1.0f); } @Override protected PropertyDescriptor> createMultiProperty() { - return new MethodMultiProperty("methodProperty", "asdf", new Method[] {ALL_METHODS[2], ALL_METHODS[3]}, - new String[] {"java.lang"}, 1.0f); + return new MethodMultiProperty("methodProperty", "asdf", new Method[]{ALL_METHODS[2], ALL_METHODS[3]}, + new String[]{"java.lang"}, 1.0f); } @Override protected PropertyDescriptor createBadProperty() { - return new MethodProperty("methodProperty", "asdf", ALL_METHODS[1], new String[] {"java.util"}, 1.0f); + return new MethodProperty("methodProperty", "asdf", ALL_METHODS[1], new String[]{"java.util"}, 1.0f); } @Override protected PropertyDescriptor> createBadMultiProperty() { - return new MethodMultiProperty("methodProperty", "asdf", new Method[] {ALL_METHODS[2], ALL_METHODS[3]}, - new String[] {"java.util"}, 1.0f); + return new MethodMultiProperty("methodProperty", "asdf", new Method[]{ALL_METHODS[2], ALL_METHODS[3]}, + new String[]{"java.util"}, 1.0f); } + + + @Override + @Test + public void testFactorySingleValue() { + Assume.assumeTrue("MethodProperty cannot be built from XPath (#762)", false); + } + + + @Override + @Test + public void testFactoryMultiValueCustomDelimiter() { + Assume.assumeTrue("MethodProperty cannot be built from XPath (#762)", false); + } + + + @Override + @Test + public void testFactoryMultiValueDefaultDelimiter() { + Assume.assumeTrue("MethodProperty cannot be built from XPath (#762)", false); + } + } From b81158ecf066f9aac4a51e7f4b7d093d77a46322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 28 Nov 2017 19:39:54 +0100 Subject: [PATCH 003/413] Convert PropertyDescriptorUtil to an enum and enrich its interface refs #763 --- .../net/sourceforge/pmd/RuleSetWriter.java | 4 +- .../properties/PropertyDescriptorField.java | 2 +- .../properties/PropertyDescriptorUtil.java | 101 ---------- .../pmd/properties/PropertyTypeId.java | 174 ++++++++++++++++++ .../sourceforge/pmd/rules/RuleFactory.java | 4 +- .../AbstractPropertyDescriptorTester.java | 4 +- .../pmd/properties/MethodPropertyTest.java | 2 +- 7 files changed, 182 insertions(+), 109 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.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 856ce4549f..0081100627 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -34,7 +34,7 @@ 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.PropertyDescriptorUtil; +import net.sourceforge.pmd.properties.PropertyTypeId; /** * This class represents a way to serialize a RuleSet to an XML configuration @@ -348,7 +348,7 @@ public class RuleSetWriter { final Element propertyElement = createPropertyValueElement(propertyDescriptor, propertyDescriptor.defaultValue()); propertyElement.setAttribute(PropertyDescriptorField.TYPE.attributeName(), - PropertyDescriptorUtil.typeIdFor(propertyDescriptor.type(), + PropertyTypeId.typeIdFor(propertyDescriptor.type(), propertyDescriptor.isMultiValue())); Map propertyValuesById = propertyDescriptor.attributeValuesById(); 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 e6ea6468d2..e1d3f1dd09 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 @@ -15,7 +15,7 @@ import net.sourceforge.pmd.RuleSetFactory; * * @author Brian Remedios * @see RuleSetFactory - * @see PropertyDescriptorUtil + * @see PropertyTypeId */ public enum PropertyDescriptorField { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java deleted file mode 100644 index c55f80765e..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorUtil.java +++ /dev/null @@ -1,101 +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.HashMap; -import java.util.Map; - -import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; - - -/** - * @author Clément Fournier - * @since 6.0.0 - */ -// TODO make an enum -public class PropertyDescriptorUtil { - - private static final Map> DESCRIPTOR_FACTORIES_BY_TYPE; - - - static { - Map> temp = new HashMap<>(16); - temp.put("Boolean", BooleanProperty.extractor()); - temp.put("List[Boolean]", BooleanMultiProperty.extractor()); - - temp.put("String", StringProperty.extractor()); - temp.put("List[String]", StringMultiProperty.extractor()); - temp.put("Character", CharacterProperty.extractor()); - temp.put("List[Character]", CharacterMultiProperty.extractor()); - - temp.put("Integer", IntegerProperty.extractor()); - temp.put("List[Integer]", IntegerMultiProperty.extractor()); - temp.put("Long", LongProperty.extractor()); - temp.put("List[Long]", LongMultiProperty.extractor()); - temp.put("Float", FloatProperty.extractor()); - temp.put("List[Float]", FloatMultiProperty.extractor()); - temp.put("Double", DoubleProperty.extractor()); - temp.put("List[Double]", DoubleMultiProperty.extractor()); - // temp.put("Enum", EnumeratedProperty.FACTORY); // TODO:cf we need new syntax in the xml to support that - // temp.put("List[Enum]", EnumeratedMultiProperty.FACTORY); - - temp.put("Class", TypeProperty.extractor()); - temp.put("List[Class]", TypeMultiProperty.extractor()); - - // temp.put("Method", MethodProperty.extractor()); // hidden, see #762 - // temp.put("List[Method]", MethodMultiProperty.extractor()); // ditto - // temp.put("File", FileProperty.extractor()); // ditto - - DESCRIPTOR_FACTORIES_BY_TYPE = Collections.unmodifiableMap(temp); - } - - - private PropertyDescriptorUtil() { - } - - - /** - * Returns the full mappings from type ids to extractors. - * - * @return The full mapping. - */ - public static Map> typeIdsToExtractors() { - return DESCRIPTOR_FACTORIES_BY_TYPE; - } - - - /** - * Gets the factory for the descriptor identified by the string id. - * - * @param typeId The identifier of the type - * - * @return The factory used to build new instances of a descriptor - */ - public static PropertyDescriptorExternalBuilder factoryFor(String typeId) { - return DESCRIPTOR_FACTORIES_BY_TYPE.get(typeId); - } - - - /** - * Gets the string representation of this type, as it should be given when defining a descriptor in the xml. - * - * @param valueType The type to look for - * @param multiValue Whether the descriptor is multivalued or not - * - * @return The type id - */ - public static String typeIdFor(Class valueType, boolean multiValue) { - - for (Map.Entry> entry : DESCRIPTOR_FACTORIES_BY_TYPE.entrySet()) { - if (entry.getValue().valueType() == valueType && entry.getValue().isMultiValue() == multiValue) { - return entry.getKey(); - } - } - return null; - } - - -} 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 new file mode 100644 index 0000000000..2668c7fed7 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyTypeId.java @@ -0,0 +1,174 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.properties; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import net.sourceforge.pmd.properties.builders.PropertyDescriptorBuilderConversionWrapper; +import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; + + +/** + * Enumerates the properties that can be built from the XML. Defining a property in + * the XML requires the {@code type} attribute, and the mapping between the values of + * this attribute and the concrete property that is built is encoded in the constants + * of this enum. + * + * @author Clément Fournier + * @see PropertyDescriptorExternalBuilder + * @since 6.0.0 + */ +public enum PropertyTypeId { + BOOLEAN("Boolean", BooleanProperty.extractor()), + BOOLEAN_LIST("List[Boolean]", BooleanMultiProperty.extractor()), + + STRING("String", StringProperty.extractor()), + STRING_LIST("List[String]", StringMultiProperty.extractor()), + CHARACTER("Character", CharacterProperty.extractor()), + CHARACTER_LIST("List[Character]", CharacterMultiProperty.extractor()), + + INTEGER("Integer", IntegerProperty.extractor()), + INTEGER_LIST("List[Integer]", IntegerMultiProperty.extractor()), + LONG("Long", LongProperty.extractor()), + LONG_LIST("List[Long]", LongMultiProperty.extractor()), + FLOAT("Float", FloatProperty.extractor()), + FLOAT_LIST("List[Float]", FloatMultiProperty.extractor()), + DOUBLE("Double", DoubleProperty.extractor()), + DOUBLE_LIST("List[Double]", DoubleMultiProperty.extractor()), + // ENUM("Enum", EnumeratedProperty.FACTORY), // TODO:cf we need new syntax in the xml to support that + // ENUM_LIST("List[Enum]", EnumeratedMultiProperty.FACTORY), + + CLASS("Class", TypeProperty.extractor()), + CLASS_LIST("List[Class]", TypeMultiProperty.extractor()); + + + private static final Map> DESCRIPTOR_FACTORIES_BY_TYPE; + private final String typeId; + private final PropertyDescriptorExternalBuilder factory; + + static { + Map> temp = new HashMap<>(); + for (PropertyTypeId id : values()) { + temp.put(id.typeId, id.factory); + } + DESCRIPTOR_FACTORIES_BY_TYPE = Collections.unmodifiableMap(temp); + } + + + PropertyTypeId(String id, PropertyDescriptorExternalBuilder factory) { + this.typeId = id; + this.factory = factory; + } + + + /** + * Gets the value of the type attribute represented by this constant. + * + * @return The type id + */ + public String getTypeId() { + return typeId; + } + + + /** + * Gets the factory associated to the type id, that can build the + * property from strings extracted from the XML. + * + * @return The factory + */ + public PropertyDescriptorExternalBuilder getFactory() { + return factory; + } + + + /** + * 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 + */ + public boolean isPropertyNumeric() { + return factory instanceof PropertyDescriptorBuilderConversionWrapper.SingleValue.Numeric + || factory instanceof PropertyDescriptorBuilderConversionWrapper.MultiValue.Numeric; + } + + + /** + * Returns true if the property corresponding to this factory is packaged, + * which means it can be safely cast to a {@link PackagedPropertyDescriptor}. + * + * @return whether the property is packaged + */ + public boolean isPropertyPackaged() { + return factory instanceof PropertyDescriptorBuilderConversionWrapper.SingleValue.Packaged + || factory instanceof PropertyDescriptorBuilderConversionWrapper.MultiValue.Packaged; + } + + + /** + * Returns true if the property corresponding to this factory takes + * lists of values as its value. + * + * @return whether the property is multivalue + */ + 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 + */ + public Class propertyValueType() { + return factory.valueType(); + } + + + /** + * Returns the full mappings from type ids to factory. + * + * @return The full mapping. + */ + public static Map> typeIdsToExtractors() { + return DESCRIPTOR_FACTORIES_BY_TYPE; + } + + + /** + * Gets the factory for the descriptor identified by the string id. + * + * @param typeId The identifier of the type + * + * @return The factory used to build new instances of a descriptor + */ + public static PropertyDescriptorExternalBuilder factoryFor(String typeId) { + return DESCRIPTOR_FACTORIES_BY_TYPE.get(typeId); + } + + + /** + * Gets the string representation of this type, as it should be given + * when defining a descriptor in the xml. + * + * @param valueType The type to look for + * @param multiValue Whether the descriptor is multivalued or not + * + * @return The type id + */ + public static String typeIdFor(Class valueType, boolean multiValue) { + for (Map.Entry> entry : DESCRIPTOR_FACTORIES_BY_TYPE.entrySet()) { + if (entry.getValue().valueType() == valueType && entry.getValue().isMultiValue() == multiValue) { + return entry.getKey(); + } + } + return null; + } +} 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 84ce6b95d4..bdca4f45a2 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 @@ -28,7 +28,7 @@ import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; -import net.sourceforge.pmd.properties.PropertyDescriptorUtil; +import net.sourceforge.pmd.properties.PropertyTypeId; import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder; @@ -304,7 +304,7 @@ public class RuleFactory { private static PropertyDescriptor parsePropertyDefinition(Element propertyElement) { String typeId = propertyElement.getAttribute(PropertyDescriptorField.TYPE.attributeName()); - PropertyDescriptorExternalBuilder pdFactory = PropertyDescriptorUtil.factoryFor(typeId); + PropertyDescriptorExternalBuilder pdFactory = PropertyTypeId.factoryFor(typeId); if (pdFactory == null) { throw new IllegalArgumentException("No property descriptor factory for type: " + typeId); } 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 index b0c56663ae..cbf11c0743 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java @@ -61,7 +61,7 @@ public abstract class AbstractPropertyDescriptorTester { @SuppressWarnings("unchecked") protected final PropertyDescriptorExternalBuilder getSingleFactory() { - return (PropertyDescriptorExternalBuilder) PropertyDescriptorUtil.factoryFor(typeName); + return (PropertyDescriptorExternalBuilder) PropertyTypeId.factoryFor(typeName); } @@ -95,7 +95,7 @@ public abstract class AbstractPropertyDescriptorTester { @SuppressWarnings("unchecked") protected final PropertyDescriptorExternalBuilder> getMultiFactory() { - return (PropertyDescriptorExternalBuilder>) PropertyDescriptorUtil.factoryFor("List[" + typeName + "]"); + return (PropertyDescriptorExternalBuilder>) PropertyTypeId.factoryFor("List[" + typeName + "]"); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java index 392e99f12b..3ed12aa0fa 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java @@ -36,7 +36,7 @@ public class MethodPropertyTest extends AbstractPackagedPropertyDescriptorTester private static final String[] METHOD_SIGNATURES = {"String#indexOf(int)", "String#substring(int,int)", "java.lang.String#substring(int,int)", "Integer#parseInt(String)", "java.util.HashMap#put(Object,Object)", - "HashMap#containsKey(Object)",}; + "HashMap#containsKey(Object)", }; public MethodPropertyTest() { From 53f6c39f65143d5c6ec122a495c9583c188e30b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 29 Nov 2017 00:18:40 +0100 Subject: [PATCH 004/413] Make the constants available from the string type id --- .../pmd/properties/PropertyTypeId.java | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 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 2668c7fed7..a3236133f9 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 @@ -14,9 +14,9 @@ import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder /** * Enumerates the properties that can be built from the XML. Defining a property in - * the XML requires the {@code type} attribute, and the mapping between the values of - * this attribute and the concrete property that is built is encoded in the constants - * of this enum. + * the XML requires the {@code type} attribute, which acts as a mnemonic for the type + * of the property that should be built. The mapping between the values of this attribute + * and the concrete property that is built is encoded in the constants of this enum. * * @author Clément Fournier * @see PropertyDescriptorExternalBuilder @@ -46,21 +46,21 @@ public enum PropertyTypeId { CLASS_LIST("List[Class]", TypeMultiProperty.extractor()); - private static final Map> DESCRIPTOR_FACTORIES_BY_TYPE; - private final String typeId; + private static final Map CONSTANTS_BY_MNEMONIC; + private final String stringId; private final PropertyDescriptorExternalBuilder factory; static { - Map> temp = new HashMap<>(); + Map temp = new HashMap<>(); for (PropertyTypeId id : values()) { - temp.put(id.typeId, id.factory); + temp.put(id.stringId, id); } - DESCRIPTOR_FACTORIES_BY_TYPE = Collections.unmodifiableMap(temp); + CONSTANTS_BY_MNEMONIC = Collections.unmodifiableMap(temp); } - + PropertyTypeId(String id, PropertyDescriptorExternalBuilder factory) { - this.typeId = id; + this.stringId = id; this.factory = factory; } @@ -68,10 +68,10 @@ public enum PropertyTypeId { /** * Gets the value of the type attribute represented by this constant. * - * @return The type id + * @return The string id */ - public String getTypeId() { - return typeId; + public String getStringId() { + return stringId; } @@ -133,39 +133,53 @@ public enum PropertyTypeId { /** - * Returns the full mappings from type ids to factory. + * Returns the full mappings from type ids to enum constants. * * @return The full mapping. */ - public static Map> typeIdsToExtractors() { - return DESCRIPTOR_FACTORIES_BY_TYPE; + public static Map typeIdsToConstants() { + return CONSTANTS_BY_MNEMONIC; } /** * Gets the factory for the descriptor identified by the string id. * - * @param typeId The identifier of the type + * @param stringId The identifier of the type * * @return The factory used to build new instances of a descriptor */ - public static PropertyDescriptorExternalBuilder factoryFor(String typeId) { - return DESCRIPTOR_FACTORIES_BY_TYPE.get(typeId); + public static PropertyDescriptorExternalBuilder factoryFor(String stringId) { + PropertyTypeId cons = CONSTANTS_BY_MNEMONIC.get(stringId); + return cons == null ? null : cons.factory; } /** - * Gets the string representation of this type, as it should be given + * Gets the enum constant corresponding to the given mnemonic. + * + * @param stringId A mnemonic for the property type + * + * @return A PropertyTypeId + */ + public static PropertyTypeId lookupMnemonic(String stringId) { + return CONSTANTS_BY_MNEMONIC.get(stringId); + } + + + /** + * Gets the string representation of this type, as it should be given * when defining a descriptor in the xml. * * @param valueType The type to look for * @param multiValue Whether the descriptor is multivalued or not * - * @return The type id + * @return The string id */ public static String typeIdFor(Class valueType, boolean multiValue) { - for (Map.Entry> entry : DESCRIPTOR_FACTORIES_BY_TYPE.entrySet()) { - if (entry.getValue().valueType() == valueType && entry.getValue().isMultiValue() == multiValue) { + for (Map.Entry entry : CONSTANTS_BY_MNEMONIC.entrySet()) { + if (entry.getValue().propertyValueType() == valueType + && entry.getValue().isPropertyMultivalue() == multiValue) { return entry.getKey(); } } From 36ba0e95814a84ead5d784f7643b1fb01825f76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 30 Nov 2017 11:34:44 +0100 Subject: [PATCH 005/413] Add a handle on the value parser of the property --- .../pmd/properties/PropertyTypeId.java | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 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 a3236133f9..9d8fec5d6d 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 @@ -23,32 +23,33 @@ import net.sourceforge.pmd.properties.builders.PropertyDescriptorExternalBuilder * @since 6.0.0 */ public enum PropertyTypeId { - BOOLEAN("Boolean", BooleanProperty.extractor()), - BOOLEAN_LIST("List[Boolean]", BooleanMultiProperty.extractor()), + BOOLEAN("Boolean", BooleanProperty.extractor(), ValueParserConstants.BOOLEAN_PARSER), + BOOLEAN_LIST("List[Boolean]", BooleanMultiProperty.extractor(), ValueParserConstants.BOOLEAN_PARSER), - STRING("String", StringProperty.extractor()), - STRING_LIST("List[String]", StringMultiProperty.extractor()), - CHARACTER("Character", CharacterProperty.extractor()), - CHARACTER_LIST("List[Character]", CharacterMultiProperty.extractor()), + 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), - INTEGER("Integer", IntegerProperty.extractor()), - INTEGER_LIST("List[Integer]", IntegerMultiProperty.extractor()), - LONG("Long", LongProperty.extractor()), - LONG_LIST("List[Long]", LongMultiProperty.extractor()), - FLOAT("Float", FloatProperty.extractor()), - FLOAT_LIST("List[Float]", FloatMultiProperty.extractor()), - DOUBLE("Double", DoubleProperty.extractor()), - DOUBLE_LIST("List[Double]", DoubleMultiProperty.extractor()), + 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), + FLOAT("Float", FloatProperty.extractor(), ValueParserConstants.FLOAT_PARSER), + 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), // ENUM("Enum", EnumeratedProperty.FACTORY), // TODO:cf we need new syntax in the xml to support that // ENUM_LIST("List[Enum]", EnumeratedMultiProperty.FACTORY), - CLASS("Class", TypeProperty.extractor()), - CLASS_LIST("List[Class]", TypeMultiProperty.extractor()); + CLASS("Class", TypeProperty.extractor(), ValueParserConstants.CLASS_PARSER), + CLASS_LIST("List[Class]", TypeMultiProperty.extractor(), ValueParserConstants.CLASS_PARSER); private static final Map CONSTANTS_BY_MNEMONIC; private final String stringId; private final PropertyDescriptorExternalBuilder factory; + private final ValueParser valueParser; static { Map temp = new HashMap<>(); @@ -59,9 +60,10 @@ public enum PropertyTypeId { } - PropertyTypeId(String id, PropertyDescriptorExternalBuilder factory) { + PropertyTypeId(String id, PropertyDescriptorExternalBuilder factory, ValueParser valueParser) { this.stringId = id; this.factory = factory; + this.valueParser = valueParser; } @@ -132,6 +134,18 @@ 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)}. + * + * @return The value parser + */ + public ValueParser getValueParser() { + return valueParser; + } + + /** * Returns the full mappings from type ids to enum constants. * From 2f4e1ba20d73a50dd010ebd4cfdc92821fe0700f Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 9 Dec 2017 09:44:29 +0100 Subject: [PATCH 006/413] Update release notes, closes #766 --- docs/pages/release_notes.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c27d6c75f4..ab65e9527c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -298,7 +298,7 @@ rule, but other rules may now produce improved results as consequence of this fi #### Apex Parser Update -The Apex parser version was bumped, from `1.0-sfdc-187` to `1.0-sfdc-224`. This update let us take full advantage +The Apex parser version was bumped, from `1.0-sfdc-187` to `210-SNAPSHOT`. This update let us take full advantage of the latest improvements from Salesforce, but introduces some breaking changes: * `BlockStatements` are now created for all control structures, even if no brace is used. We have therefore added @@ -311,6 +311,9 @@ of the latest improvements from Salesforce, but introduces some breaking changes All existing rules have been updated to reflect these changes. If you have custom rules, be sure to update them. +For more info about the included Apex parser, see the new pmd module "pmd-apex-jorje", which packages and provides +the parser as a binary. + #### Incremental Analysis The incremental analysis feature first introduced in PMD 5.6.0 has been enhanced. A few minor issues have been fixed, @@ -349,6 +352,7 @@ a warning will now be produced suggesting users to adopt it for better performan * [#605](https://github.com/pmd/pmd/issues/605): \[apex] java.lang.NoClassDefFoundError in the latest build * [#637](https://github.com/pmd/pmd/issues/637): \[apex] Avoid SOSL in loops * [#760](https://github.com/pmd/pmd/issues/760): \[apex] EmptyStatementBlock complains about missing rather than empty block + * [#766](https://github.com/pmd/pmd/issues/766): \[apex] Replace old Jorje parser with new one * [#768](https://github.com/pmd/pmd/issues/768): \[apex] java.lang.NullPointerException from PMD * cpp * [#448](https://github.com/pmd/pmd/issues/448): \[cpp] Write custom CharStream to handle continuation characters From 060848a4553a308647936024b5a2c9868edf88a4 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 9 Dec 2017 10:11:10 +0100 Subject: [PATCH 007/413] Update release notes, refs #764 closes #762 closes #763 --- docs/pages/release_notes.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index eb1a123a63..4dc043141d 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -316,6 +316,16 @@ under debug / verbose builds you can even see individual hits / misses to the ca Finally, as this feature keeps maturing, we are gently pushing this forward. If not using incremental analysis, a warning will now be produced suggesting users to adopt it for better performance. +#### Rule and Report Properties + +The implementation around the properties support for rule properties and report properties has been revamped +to be fully typesafe. Along with that change, the support classes have been moved into an own +package `net.sourceforge.pmd.properties`. While there is no change necessary in the ruleset XML files, +when using/setting values for rules, there are adjustments necessary when declaring properties in Java-implemented +rules. + +The properties are now very well documented: [Working with properties](pmd_devdocs_working_with_properties.html). + ### Fixed Issues * all @@ -328,6 +338,8 @@ a warning will now be produced suggesting users to adopt it for better performan * [#618](https://github.com/pmd/pmd/issues/618): \[core] Incremental Analysis doesn't close file correctly on Windows upon a cache hit * [#643](https://github.com/pmd/pmd/issues/643): \[core] PMD Properties (dev-properties) breaks markup on CodeClimateRenderer * [#680](https://github.com/pmd/pmd/pull/680): \[core] Isolate classloaders for runtime and auxclasspath + * [#762](https://github.com/pmd/pmd/issues/762): \[core] Remove method and file property from available property descriptors for XPath rules + * [#763](https://github.com/pmd/pmd/issues/763): \[core] Turn property descriptor util into an enum and enrich its interface * apex * [#265](https://github.com/pmd/pmd/issues/265): \[apex] Make Rule suppression work * [#488](https://github.com/pmd/pmd/pull/488): \[apex] Use Apex lexer for CPD @@ -377,13 +389,16 @@ a warning will now be produced suggesting users to adopt it for better performan * The class `net.sourceforge.pmd.lang.dfa.NodeType` has been converted to an enum. All node types are enum members now instead of int constants. The names for node types are retained. -* The properties API (rule and report properties) have been revamped to be fully typesafe. This is everything - around `net.sourceforge.pmd.PropertyDescriptor`. +* The *Properties API* (rule and report properties) has been revamped to be fully typesafe. This is everything + around `net.sourceforge.pmd.properties.PropertyDescriptor`. + + Note: All classes related to properties have been moved into the package `net.sourceforge.pmd.properties`. * The rule classes `net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestClassShouldHaveAsserts` and `net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestShouldNotUseSeeAllDataTrue` have been renamed to `ApexUnitTestClassShouldHaveAssertsRule` and `ApexUnitTestShouldNotUseSeeAllDataTrueRule`, respectively. This is to comply with the naming convention, that each rule class should be suffixed with "Rule". + This change has no impact on custom rulesets, since the rule names themselves didn't change. * The never implemented method `PMD.processFiles(PMDConfiguration, RuleSetFactory, Collection, RuleContext, ProgressMonitor)` along with the interface `ProgressMonitor` has been removed. @@ -554,4 +569,5 @@ a warning will now be produced suggesting users to adopt it for better performan * [#746](https://github.com/pmd/pmd/pull/746): \[doc] Fix typo in incremental analysis log message - [Clément Fournier](https://github.com/oowekyala) * [#749](https://github.com/pmd/pmd/pull/749): \[doc] Update the documentation for properties - [Clément Fournier](https://github.com/oowekyala) * [#758](https://github.com/pmd/pmd/pull/758): \[core] Expose the full mapping from property type id to property extractor - [Clément Fournier](https://github.com/oowekyala) +* [#764](https://github.com/pmd/pmd/pull/764): \[core] Prevent method and file property use in XPath rules - [Clément Fournier](https://github.com/oowekyala)# From c8645440f2d6d1547d86c988afc414573a010ce9 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 9 Dec 2017 10:24:30 +0100 Subject: [PATCH 008/413] Update release notes with new rules --- docs/pages/release_notes.md | 7 +++++++ pmd-core/src/main/resources/rulesets/releases/600.xml | 1 + .../src/main/resources/category/java/bestpractices.xml | 2 +- pmd-java/src/main/resources/category/java/design.xml | 4 ++-- pmd-java/src/main/resources/category/java/errorprone.xml | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 30c38e8bee..ff50b0ccfe 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -181,6 +181,10 @@ The rule reference documentation has been updated to reflect these changes. which, by using a finalizer, produces extra / unnecessary overhead to garbage collection, and should be replaced with `Files.newInputStream` / `Files.newOutputStream` available since java 1.7. +* The new Java rule `DataClass` (category `design`) detects simple data-holders without behaviour. This might indicate + that the behaviour is scattered elsewhere and the data class exposes the internal data structure, + which breaks encapsulation. + * The new Apex rule `AvoidDirectAccessTriggerMap` (category `errorprone`) helps to identify direct array access to triggers, which can produce bugs by either accessing non-existing indexes, or leaving them out. You should use for-each-loops instead. @@ -189,6 +193,9 @@ The rule reference documentation has been updated to reflect these changes. and flags them. Record IDs change between environments, meaning hardcoded ids are bound to fail under a different setup. +* The new Apex rule `CyclomaticComplexity` (category `design`) detects overly complex classes and methods. The + report threshold can be configured separately for classes and methods. + * A whole bunch of new rules has been added to Apex. They all fit into the category `errorprone`. The 5 rules are migrated for Apex from the equivalent Java rules and include: * `EmptyCatchBlock` to detect catch blocks completely ignoring exceptions. diff --git a/pmd-core/src/main/resources/rulesets/releases/600.xml b/pmd-core/src/main/resources/rulesets/releases/600.xml index f2c145af90..4ffa27dd8d 100644 --- a/pmd-core/src/main/resources/rulesets/releases/600.xml +++ b/pmd-core/src/main/resources/rulesets/releases/600.xml @@ -12,6 +12,7 @@ This ruleset contains links to rules that are new in PMD v6.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 236dd78dcc..a649c9aa47 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -353,7 +353,7 @@ public class Foo { @@ -919,7 +919,7 @@ public class Foo extends Bar { diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 1cc2ef6ad6..2afdc6a818 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1209,7 +1209,7 @@ public void foo() { From 5ae9607cfcfa7db2f298974d0749508c2c37f8b0 Mon Sep 17 00:00:00 2001 From: "Travis CI (pmd-bot)" Date: Sat, 9 Dec 2017 09:42:47 +0000 Subject: [PATCH 009/413] Update documentation --- docs/pages/pmd/rules/java/bestpractices.md | 2 +- docs/pages/pmd/rules/java/design.md | 4 ++-- docs/pages/pmd/rules/java/errorprone.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/pages/pmd/rules/java/bestpractices.md b/docs/pages/pmd/rules/java/bestpractices.md index 99f865f6b8..38634d53b0 100644 --- a/docs/pages/pmd/rules/java/bestpractices.md +++ b/docs/pages/pmd/rules/java/bestpractices.md @@ -373,7 +373,7 @@ public class Foo { ## ForLoopCanBeForeach -**Since:** PMD 6.0 +**Since:** PMD 6.0.0 **Priority:** Medium (3) diff --git a/docs/pages/pmd/rules/java/design.md b/docs/pages/pmd/rules/java/design.md index 7678f6ce5e..ec483d3f4c 100644 --- a/docs/pages/pmd/rules/java/design.md +++ b/docs/pages/pmd/rules/java/design.md @@ -435,7 +435,7 @@ class Foo { ## DataClass -**Since:** PMD 6.0 +**Since:** PMD 6.0.0 **Priority:** Medium (3) @@ -1046,7 +1046,7 @@ public class Foo extends Bar { ## NcssCount -**Since:** PMD 6.0 +**Since:** PMD 6.0.0 **Priority:** Medium (3) diff --git a/docs/pages/pmd/rules/java/errorprone.md b/docs/pages/pmd/rules/java/errorprone.md index cd1b49c80f..06832944e7 100644 --- a/docs/pages/pmd/rules/java/errorprone.md +++ b/docs/pages/pmd/rules/java/errorprone.md @@ -1310,7 +1310,7 @@ public void foo() { ## DoNotExtendJavaLangThrowable -**Since:** PMD 6.0 +**Since:** PMD 6.0.0 **Priority:** Medium (3) From 7e5c272f17ca3265624b085e3863c8a6f1e44362 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 9 Dec 2017 10:35:14 +0100 Subject: [PATCH 010/413] release notes: add missing toc entry --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index ff50b0ccfe..1931de3e36 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -28,6 +28,7 @@ This is a major release. * [Java Symbol Table](#java-symbol-table) * [Apex Parser Update](#apex-parser-update) * [Incremental Analysis](#incremental-analysis) + * [Rule and Report Properties](#rule-and-report-properties) * [Fixed Issues](#fixed-issues) * [API Changes](#api-changes) * [External Contributions](#external-contributions) From 99d1e96b1b930223141f2dbbe3773495f4736faa Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 11 Dec 2017 19:50:33 +0100 Subject: [PATCH 011/413] [doc] Update doc regarding properties refs #504, refs #762 --- docs/pages/pmd/devdocs/working_with_properties.md | 4 +--- docs/pages/release_notes.md | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/pages/pmd/devdocs/working_with_properties.md b/docs/pages/pmd/devdocs/working_with_properties.md index 9aeba8cc44..0510f1da72 100644 --- a/docs/pages/pmd/devdocs/working_with_properties.md +++ b/docs/pages/pmd/devdocs/working_with_properties.md @@ -151,9 +151,7 @@ XPath rules can also define their own properties. To do so, you must add a `prop |String|StringProperty |Character|CharacterProperty |Boolean|BooleanProperty -|File|FileProperty |Class|TypeProperty -|Method|MethodProperty Note that enumerated properties are not available in XPath rules (yet?). @@ -181,7 +179,7 @@ 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 (except `File`). These properties **require XPath 2.0** to work properly, and make use of the **sequence datatype** provided by that language. You thus need to set the `version` property to `2.0` to use them. Properties can also declare the `delimiter` attribute. +Multivalued properties are also allowed and their `type` attribute has the form `List[Boolean]` or `List[Character]`, with every above type allowed. These properties **require XPath 2.0** to work properly, and make use of the **sequence datatype** provided by that language. You thus need to set the `version` property to `2.0` to use them. Properties can also declare the `delimiter` attribute. diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 1931de3e36..77947d5265 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -348,7 +348,10 @@ package `net.sourceforge.pmd.properties`. While there is no change necessary in when using/setting values for rules, there are adjustments necessary when declaring properties in Java-implemented rules. -The properties are now very well documented: [Working with properties](pmd_devdocs_working_with_properties.html). +Rule properties can be declared both for Java based rules and XPath rules. +This is now very well documented in [Working with properties](pmd_devdocs_working_with_properties.html). + +With PMD 6.0.0, multivalued properties are now also possible with XPath rules. ### Fixed Issues From 02fadc1689a66184c3fb5241859f7519907a4f65 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 10:32:06 +0100 Subject: [PATCH 012/413] [core] Fix classloader closed issue when executing Ant task Note: the problem (ClassNotFoundExceptions) only shows up with java8 - with java9 the classloader seems to be still usable, even after we closed it... --- .../java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java index 477232d6ee..734fbf2d51 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -42,6 +42,7 @@ import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.renderers.AbstractRenderer; import net.sourceforge.pmd.renderers.Renderer; +import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.datasource.DataSource; @@ -275,7 +276,11 @@ public class PMDTaskImpl { doTask(); } finally { logManager.close(); - IOUtil.tryCloseClassLoader(configuration.getClassLoader()); + // only close the classloader, if it is ours. Otherwise we end up with class not found + // exceptions + if (configuration.getClassLoader() instanceof ClasspathClassLoader) { + IOUtil.tryCloseClassLoader(configuration.getClassLoader()); + } } } From a61b2e16c157f8e6209db3f0ded8d7ed15d00d52 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 10:58:09 +0100 Subject: [PATCH 013/413] [scala] Properly activate/decativate scala depending on java version --- pmd-dist/pom.xml | 2 +- .../net/sourceforge/pmd/it/BinaryDistributionIT.java | 10 +++++++++- pom.xml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index cd98712317..82d35835a9 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -217,7 +217,7 @@ jdk9-disabled - !1.9 + !9 diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java index dbdd89e2eb..addf48577f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java @@ -57,6 +57,7 @@ public class PMDCoverageTest { PMD.run(args); assertFalse("There was at least one exception mentioned in the output", output.getLog().contains("Exception applying rule")); + assertFalse("Wrong configuration? Ruleset not found", output.getLog().contains("Ruleset not found")); String report = FileUtils.readFileToString(f); assertEquals("No processing errors expected", 0, StringUtils.countMatches(report, "Error while processing")); From d6a9be55a877c5f5f3c7c802890e050d60777922 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 11:22:33 +0100 Subject: [PATCH 016/413] Unify rule category names --- pmd-apex/src/main/resources/category/apex/codestyle.xml | 2 +- pmd-jsp/src/main/resources/category/jsp/codestyle.xml | 2 +- pmd-plsql/src/main/resources/category/plsql/codestyle.xml | 2 +- pmd-visualforce/src/main/resources/category/vf/codestyle.xml | 2 +- pmd-visualforce/src/main/resources/category/vf/errorprone.xml | 2 +- pmd-vm/src/main/resources/category/vm/codestyle.xml | 2 +- pmd-vm/src/main/resources/category/vm/errorprone.xml | 2 +- pmd-xml/src/main/resources/category/pom/codestyle.xml | 2 +- pmd-xml/src/main/resources/category/pom/errorprone.xml | 2 +- pmd-xml/src/main/resources/category/wsdl/codestyle.xml | 2 +- pmd-xml/src/main/resources/category/wsdl/errorprone.xml | 2 +- pmd-xml/src/main/resources/category/xml/codestyle.xml | 2 +- pmd-xml/src/main/resources/category/xml/errorprone.xml | 2 +- pmd-xml/src/main/resources/category/xsl/codestyle.xml | 2 +- pmd-xml/src/main/resources/category/xsl/errorprone.xml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pmd-apex/src/main/resources/category/apex/codestyle.xml b/pmd-apex/src/main/resources/category/apex/codestyle.xml index 53c7578548..6254e17d13 100644 --- a/pmd-apex/src/main/resources/category/apex/codestyle.xml +++ b/pmd-apex/src/main/resources/category/apex/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-jsp/src/main/resources/category/jsp/codestyle.xml b/pmd-jsp/src/main/resources/category/jsp/codestyle.xml index 2a1f887f09..cfafcc6da5 100644 --- a/pmd-jsp/src/main/resources/category/jsp/codestyle.xml +++ b/pmd-jsp/src/main/resources/category/jsp/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-plsql/src/main/resources/category/plsql/codestyle.xml b/pmd-plsql/src/main/resources/category/plsql/codestyle.xml index 8f5468389a..db3b49a7c6 100644 --- a/pmd-plsql/src/main/resources/category/plsql/codestyle.xml +++ b/pmd-plsql/src/main/resources/category/plsql/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-visualforce/src/main/resources/category/vf/codestyle.xml b/pmd-visualforce/src/main/resources/category/vf/codestyle.xml index a79f8b8f44..2301d1964c 100644 --- a/pmd-visualforce/src/main/resources/category/vf/codestyle.xml +++ b/pmd-visualforce/src/main/resources/category/vf/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-visualforce/src/main/resources/category/vf/errorprone.xml b/pmd-visualforce/src/main/resources/category/vf/errorprone.xml index df38a2adcd..2539956892 100644 --- a/pmd-visualforce/src/main/resources/category/vf/errorprone.xml +++ b/pmd-visualforce/src/main/resources/category/vf/errorprone.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-vm/src/main/resources/category/vm/codestyle.xml b/pmd-vm/src/main/resources/category/vm/codestyle.xml index a79f8b8f44..2301d1964c 100644 --- a/pmd-vm/src/main/resources/category/vm/codestyle.xml +++ b/pmd-vm/src/main/resources/category/vm/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-vm/src/main/resources/category/vm/errorprone.xml b/pmd-vm/src/main/resources/category/vm/errorprone.xml index f6c47d2871..603760da24 100644 --- a/pmd-vm/src/main/resources/category/vm/errorprone.xml +++ b/pmd-vm/src/main/resources/category/vm/errorprone.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/pom/codestyle.xml b/pmd-xml/src/main/resources/category/pom/codestyle.xml index d425f59ea7..aa887dbbf7 100644 --- a/pmd-xml/src/main/resources/category/pom/codestyle.xml +++ b/pmd-xml/src/main/resources/category/pom/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/pom/errorprone.xml b/pmd-xml/src/main/resources/category/pom/errorprone.xml index a5c9e1ddf2..c05f70b96b 100644 --- a/pmd-xml/src/main/resources/category/pom/errorprone.xml +++ b/pmd-xml/src/main/resources/category/pom/errorprone.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/wsdl/codestyle.xml b/pmd-xml/src/main/resources/category/wsdl/codestyle.xml index a79f8b8f44..2301d1964c 100644 --- a/pmd-xml/src/main/resources/category/wsdl/codestyle.xml +++ b/pmd-xml/src/main/resources/category/wsdl/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/wsdl/errorprone.xml b/pmd-xml/src/main/resources/category/wsdl/errorprone.xml index df38a2adcd..2539956892 100644 --- a/pmd-xml/src/main/resources/category/wsdl/errorprone.xml +++ b/pmd-xml/src/main/resources/category/wsdl/errorprone.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/xml/codestyle.xml b/pmd-xml/src/main/resources/category/xml/codestyle.xml index a79f8b8f44..2301d1964c 100644 --- a/pmd-xml/src/main/resources/category/xml/codestyle.xml +++ b/pmd-xml/src/main/resources/category/xml/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/xml/errorprone.xml b/pmd-xml/src/main/resources/category/xml/errorprone.xml index b03d03ae7c..55608d18c4 100644 --- a/pmd-xml/src/main/resources/category/xml/errorprone.xml +++ b/pmd-xml/src/main/resources/category/xml/errorprone.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/xsl/codestyle.xml b/pmd-xml/src/main/resources/category/xsl/codestyle.xml index ede8b0b91f..548004aecb 100644 --- a/pmd-xml/src/main/resources/category/xsl/codestyle.xml +++ b/pmd-xml/src/main/resources/category/xsl/codestyle.xml @@ -1,6 +1,6 @@ - diff --git a/pmd-xml/src/main/resources/category/xsl/errorprone.xml b/pmd-xml/src/main/resources/category/xsl/errorprone.xml index df38a2adcd..2539956892 100644 --- a/pmd-xml/src/main/resources/category/xsl/errorprone.xml +++ b/pmd-xml/src/main/resources/category/xsl/errorprone.xml @@ -1,6 +1,6 @@ - From 5eb3ecd5ad5d21c9f455ef3d91f5de5f460726c8 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 12:02:04 +0100 Subject: [PATCH 017/413] [core] Make sure, a language with empty version selects the default version --- .../net/sourceforge/pmd/lang/LanguageRegistry.java | 4 +++- .../sourceforge/pmd/lang/LanguageRegistryTest.java | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) 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 9fc1f2d48b..4de50951d1 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 @@ -14,6 +14,8 @@ import java.util.List; import java.util.Map; import java.util.ServiceLoader; +import org.apache.commons.lang3.StringUtils; + /** * Created by christoferdutz on 20.09.14. */ @@ -90,7 +92,7 @@ public final class LanguageRegistry { String version; String terseName; if (terseNameAndVersion.contains(" ")) { - version = terseNameAndVersion.substring(terseNameAndVersion.lastIndexOf(' ') + 1); + version = StringUtils.trimToNull(terseNameAndVersion.substring(terseNameAndVersion.lastIndexOf(' ') + 1)); terseName = terseNameAndVersion.substring(0, terseNameAndVersion.lastIndexOf(' ')); } else { version = null; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java index 7c67d740f6..8feaaa7f28 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/LanguageRegistryTest.java @@ -17,4 +17,17 @@ public class LanguageRegistryTest { // available language now -> DummyLanguage Assert.assertSame(DummyLanguageModule.class, defaultLanguage.getClass()); } + + @Test + public void getDefaultVersionLanguageTest() { + LanguageVersion dummy12 = LanguageRegistry.findLanguageVersionByTerseName("dummy 1.2"); + Assert.assertNotNull(dummy12); + + Language dummy = LanguageRegistry.findLanguageByTerseName("dummy"); + LanguageVersion dummyDefault = dummy.getDefaultVersion(); + + LanguageVersion dummyDefault2 = LanguageRegistry.findLanguageVersionByTerseName("dummy "); + Assert.assertNotNull(dummyDefault2); + Assert.assertSame(dummyDefault, dummyDefault2); + } } From ca07aa29647d91ef094e411027cbb68db65d47bc Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 12:02:24 +0100 Subject: [PATCH 018/413] [doc] Update release notes, docs --- docs/index.md | 1 + docs/pages/pmd/devdocs/releasing.md | 2 +- docs/pages/pmd/userdocs/getting_started.md | 13 +++++++------ docs/pages/pmd/userdocs/tools/ant.md | 8 ++++---- docs/pages/release_notes.md | 16 +++++++++------- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/docs/index.md b/docs/index.md index 2f63d0f86d..9462c92d68 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,6 +47,7 @@ Features: Supported Languages: * [Java](pmd_rules_java.html) +* [JSP](pmd_rules_jsp.html) * [JavaScript](pmd_rules_ecmascript.html) * [Salesforce.com Apex](pmd_rules_apex.html) and [Visualforce](pmd_rules_vf.html) * [PLSQL](pmd_rules_plsql.html) diff --git a/docs/pages/pmd/devdocs/releasing.md b/docs/pages/pmd/devdocs/releasing.md index cf911cc79d..7f4f2f928e 100644 --- a/docs/pages/pmd/devdocs/releasing.md +++ b/docs/pages/pmd/devdocs/releasing.md @@ -17,7 +17,7 @@ through the release process. Make sure code is up to date and everything is committed and pushed with git: - $ mvn clean + $ ./mvnw clean $ git pull $ git status diff --git a/docs/pages/pmd/userdocs/getting_started.md b/docs/pages/pmd/userdocs/getting_started.md index a3b40dc58b..5b4ab0a96c 100644 --- a/docs/pages/pmd/userdocs/getting_started.md +++ b/docs/pages/pmd/userdocs/getting_started.md @@ -261,12 +261,13 @@ This behavior has been introduced to ease PMD integration into scripts or hooks, ### Supported Languages -* [apex](/pmd_rules_apex.html) -* [java](/pmd_rules_java.html) -* [ecmascript](/pmd_rules_javascript.html) (JavaScript) -* [jsp](/pmd_rules_jsp.html) -* [plsql](/pmd_rules_plsql.html) -* [vm](/pmd_rules_vm.html) (Apache Velocity) +* [apex](pmd_rules_apex.html) (Salesforce Apex) +* [java](pmd_rules_java.html) +* [ecmascript](pmd_rules_javascript.html) (JavaScript) +* [jsp](pmd_rules_jsp.html) +* [plsql](pmd_rules_plsql.html) +* [vf](pmd_rules_vf.html) (Salesforce VisualForce) +* [vm](pmd_rules_vm.html) (Apache Velocity) * [xml and xsl](/pmd_rules_xml.html) diff --git a/docs/pages/pmd/userdocs/tools/ant.md b/docs/pages/pmd/userdocs/tools/ant.md index eb2ff5f42e..bc61e402e3 100644 --- a/docs/pages/pmd/userdocs/tools/ant.md +++ b/docs/pages/pmd/userdocs/tools/ant.md @@ -151,8 +151,7 @@ and the associated version (1.5, 1.6,...) The specific version of a language to be used for parsing is selected via the `sourceLanguage` nested element. Possible values are: - - + @@ -160,12 +159,13 @@ nested element. Possible values are: + - - + + ### Postprocessing the report file with XSLT diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 77947d5265..be6cba6b18 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -41,7 +41,7 @@ Thanks to [Clément Fournier](https://github.com/oowekyala), we now have a new r is based on JavaFX. It replaces the old designer and can be started via * `bin/run.sh designer` (on Unix-like platform such as Linux and Mac OS X) -* `bin\\designer.bat` (on Windows) +* `bin\designer.bat` (on Windows) Note: At least Java8 is required for the designer. The old designer is still available as `designerold` but will be removed with the next major release. @@ -70,21 +70,23 @@ CPD will therefore have less false positives and false negatives. #### Java Type Resolution -As part of Google Summer of Code 2017, [Bendegúz Nagy](https://github.com/WinterGrascph)'s work on type resolution for Java continues. -For this release he has extended support for method calls for both instance and static methods. +As part of Google Summer of Code 2017, [Bendegúz Nagy](https://github.com/WinterGrascph) worked on type resolution +for Java. For this release he has extended support for method calls for both instance and static methods. -Method shadowing and overloading are supported, as are varargs. However, the selection of the target method upon the presence -of generics and type inference is still work in progress. Expect it in forecoming releases. +Method shadowing and overloading are supported, as are varargs. However, the selection of the target method upon +the presence of generics and type inference is still work in progress. Expect it in forecoming releases. As for fields, the basic support was in place for release 5.8.0, but has now been expanded to support static fields. #### Metrics Framework -As part of Google Summer of Code 2017, [Clément Fournier](https://github.com/oowekyala) is continuing his work +As part of Google Summer of Code 2017, [Clément Fournier](https://github.com/oowekyala) is worked on the new metrics framework for object-oriented metrics. There are already a couple of metrics (e.g. ATFD, WMC, Cyclo, LoC) implemented. More metrics are planned. -Based on those metrics, rules like "GodClass" detection can be implemented more easily. +Based on those metrics, rules like "GodClass" detection could be implemented more easily. +The following rules benefit from the metrics framework: NcssCount (java), NPathComplexity (java), +CyclomaticComplexity (both java and apex). The Metrics framework has been abstracted and is available in `pmd-core` for other languages. With this PMD release, the metrics framework is supported for both Java and Apex. From bc66441da66f4f60817db990fd32b29b39e88365 Mon Sep 17 00:00:00 2001 From: "Travis CI (pmd-bot)" Date: Fri, 15 Dec 2017 11:12:04 +0000 Subject: [PATCH 019/413] Update documentation --- docs/_data/sidebars/pmd_sidebar.yml | 14 +++++++------- docs/pages/pmd/rules/apex.md | 2 +- docs/pages/pmd/rules/apex/codestyle.md | 4 ++-- docs/pages/pmd/rules/jsp.md | 2 +- docs/pages/pmd/rules/jsp/codestyle.md | 4 ++-- docs/pages/pmd/rules/plsql.md | 2 +- docs/pages/pmd/rules/plsql/codestyle.md | 4 ++-- docs/pages/pmd/rules/pom.md | 2 +- docs/pages/pmd/rules/pom/errorprone.md | 4 ++-- docs/pages/pmd/rules/vm.md | 2 +- docs/pages/pmd/rules/vm/errorprone.md | 4 ++-- docs/pages/pmd/rules/xml.md | 2 +- docs/pages/pmd/rules/xml/errorprone.md | 4 ++-- docs/pages/pmd/rules/xsl.md | 2 +- docs/pages/pmd/rules/xsl/codestyle.md | 4 ++-- 15 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index 504c4927b3..a66bbe2cc8 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -97,7 +97,7 @@ entries: - title: Best Practices output: web, pdf url: /pmd_rules_apex_bestpractices.html - - title: Codestyle + - title: Code Style output: web, pdf url: /pmd_rules_apex_codestyle.html - title: Design @@ -172,7 +172,7 @@ entries: - title: Best Practices output: web, pdf url: /pmd_rules_jsp_bestpractices.html - - title: Codestyle + - title: Code Style output: web, pdf url: /pmd_rules_jsp_codestyle.html - title: Design @@ -193,7 +193,7 @@ entries: - title: Index output: web, pdf url: /pmd_rules_pom.html - - title: Errorprone + - title: Error Prone output: web, pdf url: /pmd_rules_pom_errorprone.html - title: null @@ -208,7 +208,7 @@ entries: - title: Best Practices output: web, pdf url: /pmd_rules_plsql_bestpractices.html - - title: Codestyle + - title: Code Style output: web, pdf url: /pmd_rules_plsql_codestyle.html - title: Design @@ -244,7 +244,7 @@ entries: - title: Design output: web, pdf url: /pmd_rules_vm_design.html - - title: Errorprone + - title: Error Prone output: web, pdf url: /pmd_rules_vm_errorprone.html - title: null @@ -256,7 +256,7 @@ entries: - title: Index output: web, pdf url: /pmd_rules_xml.html - - title: Errorprone + - title: Error Prone output: web, pdf url: /pmd_rules_xml_errorprone.html - title: null @@ -268,7 +268,7 @@ entries: - title: Index output: web, pdf url: /pmd_rules_xsl.html - - title: Codestyle + - title: Code Style output: web, pdf url: /pmd_rules_xsl_codestyle.html - title: Performance diff --git a/docs/pages/pmd/rules/apex.md b/docs/pages/pmd/rules/apex.md index a12a1e28bb..5e18176286 100644 --- a/docs/pages/pmd/rules/apex.md +++ b/docs/pages/pmd/rules/apex.md @@ -12,7 +12,7 @@ folder: pmd/rules * [AvoidGlobalModifier](pmd_rules_apex_bestpractices.html#avoidglobalmodifier): Global classes should be avoided (especially in managed packages) as they can never be deleted or... * [AvoidLogicInTrigger](pmd_rules_apex_bestpractices.html#avoidlogicintrigger): As triggers do not allow methods like regular classes they are less flexible and suited to apply ... -## Codestyle +## Code Style {% include callout.html content="Rules which enforce a specific coding style." %} diff --git a/docs/pages/pmd/rules/apex/codestyle.md b/docs/pages/pmd/rules/apex/codestyle.md index 3ca4788439..ed429a8f43 100644 --- a/docs/pages/pmd/rules/apex/codestyle.md +++ b/docs/pages/pmd/rules/apex/codestyle.md @@ -1,11 +1,11 @@ --- -title: Codestyle +title: Code Style summary: Rules which enforce a specific coding style. permalink: pmd_rules_apex_codestyle.html folder: pmd/rules/apex sidebaractiveurl: /pmd_rules_apex.html editmepath: ../pmd-apex/src/main/resources/category/apex/codestyle.xml -keywords: Codestyle, ClassNamingConventions, IfElseStmtsMustUseBraces, IfStmtsMustUseBraces, ForLoopsMustUseBraces, MethodNamingConventions, VariableNamingConventions, WhileLoopsMustUseBraces +keywords: Code Style, ClassNamingConventions, IfElseStmtsMustUseBraces, IfStmtsMustUseBraces, ForLoopsMustUseBraces, MethodNamingConventions, VariableNamingConventions, WhileLoopsMustUseBraces --- ## ClassNamingConventions diff --git a/docs/pages/pmd/rules/jsp.md b/docs/pages/pmd/rules/jsp.md index 25a84a9c3e..47ee66cc1b 100644 --- a/docs/pages/pmd/rules/jsp.md +++ b/docs/pages/pmd/rules/jsp.md @@ -12,7 +12,7 @@ folder: pmd/rules * [NoHtmlComments](pmd_rules_jsp_bestpractices.html#nohtmlcomments): In a production system, HTML comments increase the payloadbetween the application server to the c... * [NoJspForward](pmd_rules_jsp_bestpractices.html#nojspforward): Do not do a forward from within a JSP file. -## Codestyle +## Code Style {% include callout.html content="Rules which enforce a specific coding style." %} diff --git a/docs/pages/pmd/rules/jsp/codestyle.md b/docs/pages/pmd/rules/jsp/codestyle.md index 19cb449604..ab33a56a6e 100644 --- a/docs/pages/pmd/rules/jsp/codestyle.md +++ b/docs/pages/pmd/rules/jsp/codestyle.md @@ -1,11 +1,11 @@ --- -title: Codestyle +title: Code Style summary: Rules which enforce a specific coding style. permalink: pmd_rules_jsp_codestyle.html folder: pmd/rules/jsp sidebaractiveurl: /pmd_rules_jsp.html editmepath: ../pmd-jsp/src/main/resources/category/jsp/codestyle.xml -keywords: Codestyle, DuplicateJspImports +keywords: Code Style, DuplicateJspImports --- ## DuplicateJspImports diff --git a/docs/pages/pmd/rules/plsql.md b/docs/pages/pmd/rules/plsql.md index a8ca9f461f..aa86a94fd1 100644 --- a/docs/pages/pmd/rules/plsql.md +++ b/docs/pages/pmd/rules/plsql.md @@ -9,7 +9,7 @@ folder: pmd/rules * [TomKytesDespair](pmd_rules_plsql_bestpractices.html#tomkytesdespair): "WHEN OTHERS THEN NULL" hides all errors - (Re)RAISE an exception or call RAISE_APPLICATION_ERROR -## Codestyle +## Code Style {% include callout.html content="Rules which enforce a specific coding style." %} diff --git a/docs/pages/pmd/rules/plsql/codestyle.md b/docs/pages/pmd/rules/plsql/codestyle.md index e984c3de60..6949322f58 100644 --- a/docs/pages/pmd/rules/plsql/codestyle.md +++ b/docs/pages/pmd/rules/plsql/codestyle.md @@ -1,11 +1,11 @@ --- -title: Codestyle +title: Code Style summary: Rules which enforce a specific coding style. permalink: pmd_rules_plsql_codestyle.html folder: pmd/rules/plsql sidebaractiveurl: /pmd_rules_plsql.html editmepath: ../pmd-plsql/src/main/resources/category/plsql/codestyle.xml -keywords: Codestyle, MisplacedPragma +keywords: Code Style, MisplacedPragma --- ## MisplacedPragma diff --git a/docs/pages/pmd/rules/pom.md b/docs/pages/pmd/rules/pom.md index 693b26f48a..0810b5b46e 100644 --- a/docs/pages/pmd/rules/pom.md +++ b/docs/pages/pmd/rules/pom.md @@ -3,7 +3,7 @@ title: Maven POM Rules permalink: pmd_rules_pom.html folder: pmd/rules --- -## Errorprone +## Error Prone {% include callout.html content="Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors." %} diff --git a/docs/pages/pmd/rules/pom/errorprone.md b/docs/pages/pmd/rules/pom/errorprone.md index ba2744f25b..28a718d9d2 100644 --- a/docs/pages/pmd/rules/pom/errorprone.md +++ b/docs/pages/pmd/rules/pom/errorprone.md @@ -1,11 +1,11 @@ --- -title: Errorprone +title: Error Prone summary: Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. permalink: pmd_rules_pom_errorprone.html folder: pmd/rules/pom sidebaractiveurl: /pmd_rules_pom.html editmepath: ../pmd-xml/src/main/resources/category/pom/errorprone.xml -keywords: Errorprone, InvalidDependencyTypes, ProjectVersionAsDependencyVersion +keywords: Error Prone, InvalidDependencyTypes, ProjectVersionAsDependencyVersion --- ## InvalidDependencyTypes diff --git a/docs/pages/pmd/rules/vm.md b/docs/pages/pmd/rules/vm.md index 435a49ba10..88c2848866 100644 --- a/docs/pages/pmd/rules/vm.md +++ b/docs/pages/pmd/rules/vm.md @@ -20,7 +20,7 @@ folder: pmd/rules * [NoInlineJavaScript](pmd_rules_vm_design.html#noinlinejavascript): Avoid inline JavaScript. Import .js files instead. * [NoInlineStyles](pmd_rules_vm_design.html#noinlinestyles): Avoid inline styles. Use css classes instead. -## Errorprone +## Error Prone {% include callout.html content="Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors." %} diff --git a/docs/pages/pmd/rules/vm/errorprone.md b/docs/pages/pmd/rules/vm/errorprone.md index 40ceb54ff0..382dc0d948 100644 --- a/docs/pages/pmd/rules/vm/errorprone.md +++ b/docs/pages/pmd/rules/vm/errorprone.md @@ -1,11 +1,11 @@ --- -title: Errorprone +title: Error Prone summary: Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. permalink: pmd_rules_vm_errorprone.html folder: pmd/rules/vm sidebaractiveurl: /pmd_rules_vm.html editmepath: ../pmd-vm/src/main/resources/category/vm/errorprone.xml -keywords: Errorprone, EmptyForeachStmt, EmptyIfStmt +keywords: Error Prone, EmptyForeachStmt, EmptyIfStmt --- ## EmptyForeachStmt diff --git a/docs/pages/pmd/rules/xml.md b/docs/pages/pmd/rules/xml.md index c6025818cb..8b8c2b85a6 100644 --- a/docs/pages/pmd/rules/xml.md +++ b/docs/pages/pmd/rules/xml.md @@ -3,7 +3,7 @@ title: XML Rules permalink: pmd_rules_xml.html folder: pmd/rules --- -## Errorprone +## Error Prone {% include callout.html content="Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors." %} diff --git a/docs/pages/pmd/rules/xml/errorprone.md b/docs/pages/pmd/rules/xml/errorprone.md index ff8299a9bd..c0f11931e8 100644 --- a/docs/pages/pmd/rules/xml/errorprone.md +++ b/docs/pages/pmd/rules/xml/errorprone.md @@ -1,11 +1,11 @@ --- -title: Errorprone +title: Error Prone summary: Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. permalink: pmd_rules_xml_errorprone.html folder: pmd/rules/xml sidebaractiveurl: /pmd_rules_xml.html editmepath: ../pmd-xml/src/main/resources/category/xml/errorprone.xml -keywords: Errorprone, MistypedCDATASection +keywords: Error Prone, MistypedCDATASection --- ## MistypedCDATASection diff --git a/docs/pages/pmd/rules/xsl.md b/docs/pages/pmd/rules/xsl.md index 7397ce82eb..befc91c3bc 100644 --- a/docs/pages/pmd/rules/xsl.md +++ b/docs/pages/pmd/rules/xsl.md @@ -3,7 +3,7 @@ title: XSL Rules permalink: pmd_rules_xsl.html folder: pmd/rules --- -## Codestyle +## Code Style {% include callout.html content="Rules which enforce a specific coding style." %} diff --git a/docs/pages/pmd/rules/xsl/codestyle.md b/docs/pages/pmd/rules/xsl/codestyle.md index 8b87d43ed2..96428f01a5 100644 --- a/docs/pages/pmd/rules/xsl/codestyle.md +++ b/docs/pages/pmd/rules/xsl/codestyle.md @@ -1,11 +1,11 @@ --- -title: Codestyle +title: Code Style summary: Rules which enforce a specific coding style. permalink: pmd_rules_xsl_codestyle.html folder: pmd/rules/xsl sidebaractiveurl: /pmd_rules_xsl.html editmepath: ../pmd-xml/src/main/resources/category/xsl/codestyle.xml -keywords: Codestyle, UseConcatOnce +keywords: Code Style, UseConcatOnce --- ## UseConcatOnce From bab09de827002d444105b193f451736dd6e03110 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 15:05:21 +0100 Subject: [PATCH 020/413] Prepare pmd release 6.0.0 --- do-release.sh | 2 +- docs/_config.yml | 2 +- docs/pages/release_notes.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/do-release.sh b/do-release.sh index 5023077a28..ba27fba433 100755 --- a/do-release.sh +++ b/do-release.sh @@ -24,7 +24,7 @@ echo "Releasing PMD" echo "-------------------------------------------" # see also https://gist.github.com/pdunnavant/4743895 -CURRENT_VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec|tail -1) +CURRENT_VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec) RELEASE_VERSION=${CURRENT_VERSION%-SNAPSHOT} MAJOR=$(echo $RELEASE_VERSION | cut -d . -f 1) MINOR=$(echo $RELEASE_VERSION | cut -d . -f 2) diff --git a/docs/_config.yml b/docs/_config.yml index 2064c7c9d4..a30a3f2dc6 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -2,7 +2,7 @@ repository: pmd/pmd pmd: version: 6.0.0 - date: to-be-defined + date: 2017-12-15 output: web # this property is useful for conditional filtering of content that is separate from the PDF. diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index be6cba6b18..23334ae3f3 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -4,7 +4,7 @@ permalink: pmd_release_notes.html keywords: changelog, release notes --- -## ????? - 6.0.0-SNAPSHOT +## 15-December-2017 - 6.0.0-SNAPSHOT The PMD team is pleased to announce PMD 6.0.0. From e88db416b21b08b122c06323a971e4da9bc1cd56 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 15:27:57 +0100 Subject: [PATCH 021/413] [doc] Update release docu --- do-release.sh | 7 ++++++- docs/pages/pmd/devdocs/releasing.md | 32 +++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/do-release.sh b/do-release.sh index ba27fba433..8bf9f53b16 100755 --- a/do-release.sh +++ b/do-release.sh @@ -64,10 +64,14 @@ echo "* Update version/release info in **docs/pages/release_notes.md**." echo echo " ## $(date -u +%d-%B-%Y) - ${RELEASE_VERSION}" echo +echo "* Update date info in **docs/_config.yml**. +echo echo "* Ensure all the new rules are listed in a the proper file:" echo " pmd-core/src/main/resources/rulesets/releases/${RELEASE_VERSION}.xml file." echo -echo "* Update **../pmd.github.io/index.html** to mention the new release" +echo "* Update **../pmd.github.io/_config.yml** to mention the new release" +echo +echo "* Add **../pmd.github.io/_posts/$(date -u +%d-%m-%Y)-PMD-${RELEASE_VERSION}.md" echo echo "Press enter to continue..." read @@ -76,6 +80,7 @@ git commit -a -m "Prepare pmd release ${RELEASE_VERSION}" ( echo "Committing current changes (pmd.github.io)" cd ../pmd.github.io + git add _posts/$(date -u +%d-%m-%Y)-PMD-${RELEASE_VERSION}.md git commit -a -m "Prepare pmd release ${RELEASE_VERSION}" git push ) diff --git a/docs/pages/pmd/devdocs/releasing.md b/docs/pages/pmd/devdocs/releasing.md index 7f4f2f928e..f823bb50f4 100644 --- a/docs/pages/pmd/devdocs/releasing.md +++ b/docs/pages/pmd/devdocs/releasing.md @@ -23,13 +23,22 @@ Make sure code is up to date and everything is committed and pushed with git: -### The Release Notes +### The Release Notes and docs -At a very minimum, the current date must be noted in the release notes. Also, the version +At a very minimum, the current date must be noted in the release notes and the download section. Also, the version must be adjusted. E.g. by removing "-SNAPSHOT". You can find the release notes here: `docs/pages/release_notes.md`. +The date for the download section is to be entered in `docs/_config.yml`, e.g. + +``` +pmd: + version: 6.0.0 + date: 2017-12-15 +``` + + The release notes usual mention any new rules that have been added since the last release. Please double check the file `pmd-core/src/main/resources/rulesets/releases/.xml`, so that all new rules are listed. @@ -43,12 +52,19 @@ Check in all (version) changes to branch master or any other branch, from which ### The Homepage The github repo `pmd.github.io` hosts the homepage for [https://pmd.github.io](https://pmd.github.io). -The `index.html` page needes to be updated to display the new release. The new release is mentioned -* on the start page -* in the announcements -* and in the previous releases section +The new version needs to be entered into `_config.yml`, e.g.: +``` +pmd: + latestVersion: 6.0.0 + latestVersionDate: 15th December 2017 +``` + +Also move the previous version down into the "downloads" section. + +Then create a new page for the new release, e.g. `_posts/2017-12-15-PMD-6.0.0.md` and copy +the release notes into this page. This will appear under the news section. Check in all (version) changes to branch master: @@ -191,6 +207,6 @@ At some point, it might be time for a new maintenance branch. Such a branch is u the `master` branch. Here are the steps: * Create a new branch: `git branch pmd/5.6.x master` -* Update the version in both the new branch and master, e.g. `mvn versions:set -Dversion=5.6.0-SNAPSHOT` - and `mvn versions:set -Dversion=5.7.0-SNAPSHOT`. +* Update the version in both the new branch and master, e.g. `mvn versions:set -DnewVersion=5.6.1-SNAPSHOT` + and `mvn versions:set -DnewVersion=5.7.0-SNAPSHOT`. * Update the release notes on both the new branch and master From 63742b98e9c988bb7694e83f6afa7d9611db3359 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 15:50:56 +0100 Subject: [PATCH 022/413] Avoid pmdVersion property which conflicts with releasing See https://issues.apache.org/jira/browse/MRELEASE-932 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 8f08ad1225..e07253d2d8 100644 --- a/pom.xml +++ b/pom.xml @@ -259,7 +259,6 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code 2.20.1 2.17 3.8 - 5.8.1 7 1.10.1 3.0.0-M1 @@ -533,15 +532,16 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code false + net.sourceforge.pmd pmd-core - ${pmdVersion} + 5.8.1 net.sourceforge.pmd pmd-java - ${pmdVersion} + 5.8.1 From 9b6fe9022595187e5128e195d1b84c0989f6eab1 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 16:01:14 +0100 Subject: [PATCH 023/413] [maven-release-plugin] prepare release pmd_releases/6.0.0 --- pmd-apex-jorje/pom.xml | 5 ++--- pmd-apex/pom.xml | 11 ++++------- pmd-core/pom.xml | 2 +- pmd-cpp/pom.xml | 2 +- pmd-cs/pom.xml | 2 +- pmd-dist/pom.xml | 2 +- pmd-doc/pom.xml | 2 +- pmd-fortran/pom.xml | 2 +- pmd-go/pom.xml | 2 +- pmd-groovy/pom.xml | 2 +- pmd-java/pom.xml | 2 +- pmd-java8/pom.xml | 2 +- pmd-javascript/pom.xml | 2 +- pmd-jsp/pom.xml | 2 +- pmd-matlab/pom.xml | 2 +- pmd-objectivec/pom.xml | 2 +- pmd-perl/pom.xml | 2 +- pmd-php/pom.xml | 2 +- pmd-plsql/pom.xml | 2 +- pmd-python/pom.xml | 2 +- pmd-ruby/pom.xml | 2 +- pmd-scala/pom.xml | 2 +- pmd-swift/pom.xml | 2 +- pmd-test/pom.xml | 2 +- pmd-ui/pom.xml | 5 ++--- pmd-visualforce/pom.xml | 2 +- pmd-vm/pom.xml | 2 +- pmd-xml/pom.xml | 2 +- pom.xml | 4 ++-- 29 files changed, 35 insertions(+), 40 deletions(-) diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index 5aa8487889..0c7b60f313 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 pmd-apex-jorje PMD Apex Jorje Parser Library @@ -9,7 +8,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index a938271394..63d5a9acfd 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 pmd-apex PMD Apex @@ -8,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 @@ -45,10 +44,8 @@ PMD specific tasks: cleaning generated markdown - - + + diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index ebf2654132..ce72e8b3f2 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index b22a429191..2efbf34807 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 591b9a8ef1..06372d3619 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 82d35835a9..4cfe44c7df 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index 9fd7a709d4..85abaece50 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index dc58ea6d15..d1ef873609 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index 47a7c9d06b..7982efcd0b 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index 8ae0b45110..f9b710d094 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 9573af4e9f..ea0ebd1f66 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml index 9df9c3e5b2..366741eb3e 100644 --- a/pmd-java8/pom.xml +++ b/pmd-java8/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index f52c992bd0..62168a5675 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index 3fddff63ad..b2e7aba640 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index 1cef8ba4b1..953766b6ab 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index 20c045ab3b..0e24f2f527 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index 90c47547bf..81e0720052 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index 435918dc75..b3e9fd95d6 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-plsql/pom.xml b/pmd-plsql/pom.xml index 3f62ba89a3..f8467b2648 100644 --- a/pmd-plsql/pom.xml +++ b/pmd-plsql/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-python/pom.xml b/pmd-python/pom.xml index 8d04710890..3c5c1e68f9 100644 --- a/pmd-python/pom.xml +++ b/pmd-python/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-ruby/pom.xml b/pmd-ruby/pom.xml index b9fb8b18a6..3c45435495 100644 --- a/pmd-ruby/pom.xml +++ b/pmd-ruby/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-scala/pom.xml b/pmd-scala/pom.xml index 4b572f979e..dc379820dc 100644 --- a/pmd-scala/pom.xml +++ b/pmd-scala/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-swift/pom.xml b/pmd-swift/pom.xml index 20b5b3b522..ef0da8edc4 100644 --- a/pmd-swift/pom.xml +++ b/pmd-swift/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml index 1e5b375301..492d44c7de 100644 --- a/pmd-test/pom.xml +++ b/pmd-test/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-ui/pom.xml b/pmd-ui/pom.xml index 7b71f7fec4..10fe26ac4d 100644 --- a/pmd-ui/pom.xml +++ b/pmd-ui/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 pmd-ui PMD UI Applications @@ -8,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 4cc469c9bb..e9ca03b0ea 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-vm/pom.xml b/pmd-vm/pom.xml index fb0e357f84..f148ceab6b 100644 --- a/pmd-vm/pom.xml +++ b/pmd-vm/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pmd-xml/pom.xml b/pmd-xml/pom.xml index 02a6c6931d..03eb275305 100644 --- a/pmd-xml/pom.xml +++ b/pmd-xml/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 diff --git a/pom.xml b/pom.xml index e07253d2d8..d8600d9d46 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 net.sourceforge.pmd pmd - 6.0.0-SNAPSHOT + 6.0.0 pom PMD @@ -234,7 +234,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code scm:git:git://github.com/pmd/pmd.git scm:git:ssh://git@github.com/pmd/pmd.git https://github.com/pmd/pmd - HEAD + pmd_releases/6.0.0 From a9750486552b2e76f3bcb78fa16f1c2f31a4f444 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 16:01:22 +0100 Subject: [PATCH 024/413] [maven-release-plugin] prepare for next development iteration --- pmd-apex-jorje/pom.xml | 2 +- pmd-apex/pom.xml | 2 +- pmd-core/pom.xml | 2 +- pmd-cpp/pom.xml | 2 +- pmd-cs/pom.xml | 2 +- pmd-dist/pom.xml | 2 +- pmd-doc/pom.xml | 2 +- pmd-fortran/pom.xml | 2 +- pmd-go/pom.xml | 2 +- pmd-groovy/pom.xml | 2 +- pmd-java/pom.xml | 2 +- pmd-java8/pom.xml | 2 +- pmd-javascript/pom.xml | 2 +- pmd-jsp/pom.xml | 2 +- pmd-matlab/pom.xml | 2 +- pmd-objectivec/pom.xml | 2 +- pmd-perl/pom.xml | 2 +- pmd-php/pom.xml | 2 +- pmd-plsql/pom.xml | 2 +- pmd-python/pom.xml | 2 +- pmd-ruby/pom.xml | 2 +- pmd-scala/pom.xml | 2 +- pmd-swift/pom.xml | 2 +- pmd-test/pom.xml | 2 +- pmd-ui/pom.xml | 2 +- pmd-visualforce/pom.xml | 2 +- pmd-vm/pom.xml | 2 +- pmd-xml/pom.xml | 2 +- pom.xml | 4 ++-- 29 files changed, 30 insertions(+), 30 deletions(-) diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index 0c7b60f313..2b2e6689fa 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index 63d5a9acfd..90816300f0 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index ce72e8b3f2..78173d32e9 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index 2efbf34807..af219bee20 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 06372d3619..1df662898b 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 4cfe44c7df..8adfe16154 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index 85abaece50..e3a7cea6ea 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index d1ef873609..790a0064be 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index 7982efcd0b..6cc21b2071 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index f9b710d094..694a867b63 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index ea0ebd1f66..2c0dff6c07 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml index 366741eb3e..62ee202bc5 100644 --- a/pmd-java8/pom.xml +++ b/pmd-java8/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index 62168a5675..969960e89c 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index b2e7aba640..593dede7e4 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index 953766b6ab..a8ec6af82b 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index 0e24f2f527..d6b50033f2 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index 81e0720052..7c86e6a74d 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index b3e9fd95d6..5187f8fc06 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-plsql/pom.xml b/pmd-plsql/pom.xml index f8467b2648..02447bd7f2 100644 --- a/pmd-plsql/pom.xml +++ b/pmd-plsql/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-python/pom.xml b/pmd-python/pom.xml index 3c5c1e68f9..30c622093c 100644 --- a/pmd-python/pom.xml +++ b/pmd-python/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-ruby/pom.xml b/pmd-ruby/pom.xml index 3c45435495..a933a88f7f 100644 --- a/pmd-ruby/pom.xml +++ b/pmd-ruby/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-scala/pom.xml b/pmd-scala/pom.xml index dc379820dc..d280060138 100644 --- a/pmd-scala/pom.xml +++ b/pmd-scala/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-swift/pom.xml b/pmd-swift/pom.xml index ef0da8edc4..079c52de0c 100644 --- a/pmd-swift/pom.xml +++ b/pmd-swift/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml index 492d44c7de..70ab30c47a 100644 --- a/pmd-test/pom.xml +++ b/pmd-test/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-ui/pom.xml b/pmd-ui/pom.xml index 10fe26ac4d..20a75bf895 100644 --- a/pmd-ui/pom.xml +++ b/pmd-ui/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index e9ca03b0ea..b13707dc1f 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-vm/pom.xml b/pmd-vm/pom.xml index f148ceab6b..5334c6c4a6 100644 --- a/pmd-vm/pom.xml +++ b/pmd-vm/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pmd-xml/pom.xml b/pmd-xml/pom.xml index 03eb275305..01895d829c 100644 --- a/pmd-xml/pom.xml +++ b/pmd-xml/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT diff --git a/pom.xml b/pom.xml index d8600d9d46..513d16f474 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 net.sourceforge.pmd pmd - 6.0.0 + 6.1.0-SNAPSHOT pom PMD @@ -234,7 +234,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code scm:git:git://github.com/pmd/pmd.git scm:git:ssh://git@github.com/pmd/pmd.git https://github.com/pmd/pmd - pmd_releases/6.0.0 + HEAD From cdc33280d60a6d48e1870a8c41dfa1828281f7a6 Mon Sep 17 00:00:00 2001 From: "Travis CI (pmd-bot)" Date: Fri, 15 Dec 2017 15:21:42 +0000 Subject: [PATCH 025/413] Update documentation --- docs/pages/pmd/rules/pom/errorprone.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/pmd/rules/pom/errorprone.md b/docs/pages/pmd/rules/pom/errorprone.md index 28a718d9d2..3315e4f3b7 100644 --- a/docs/pages/pmd/rules/pom/errorprone.md +++ b/docs/pages/pmd/rules/pom/errorprone.md @@ -53,7 +53,7 @@ The following types are considered valid: pom, jar, maven-plugin, ejb, war, ear, **Priority:** Medium (3) Using that expression in dependency declarations seems like a shortcut, but it can go wrong. -By far the most common problem is the use of 6.0.0-SNAPSHOT in a BOM or parent POM. +By far the most common problem is the use of 6.1.0-SNAPSHOT in a BOM or parent POM. ``` //dependency/version/text[contains(@Image,'{project.version}')] From cb954e93e5f3910c0c0bc567b4729d07a2b0ed49 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 16:05:14 +0100 Subject: [PATCH 026/413] Prepare next development version --- docs/pages/release_notes.md | 590 +------------------------------ docs/pages/release_notes_old.md | 602 ++++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+), 586 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 23334ae3f3..c9df30afa8 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -4,605 +4,23 @@ permalink: pmd_release_notes.html keywords: changelog, release notes --- -## 15-December-2017 - 6.0.0-SNAPSHOT +## ????? - 6.1.0-SNAPSHOT -The PMD team is pleased to announce PMD 6.0.0. +The PMD team is pleased to announce PMD 6.1.0-SNAPSHOT. -This is a major release. +This is a bug fixing release. ### Table Of Contents * [New and noteworthy](#new-and-noteworthy) - * [New Rule Designer](#new-rule-designer) - * [Java 9 support](#java-9-support) - * [Revamped Apex CPD](#revamped-apex-cpd) - * [Java Type Resolution](#java-type-resolution) - * [Metrics Framework](#metrics-framework) - * [Error Reporting](#error-reporting) - * [Apex Rule Suppression](#apex-rule-suppression) - * [Rule Categories](#rule-categories) - * [New Rules](#new-rules) - * [Modified Rules](#modified-rules) - * [Deprecated Rules](#deprecated-rules) - * [Removed Rules](#removed-rules) - * [Java Symbol Table](#java-symbol-table) - * [Apex Parser Update](#apex-parser-update) - * [Incremental Analysis](#incremental-analysis) - * [Rule and Report Properties](#rule-and-report-properties) * [Fixed Issues](#fixed-issues) * [API Changes](#api-changes) * [External Contributions](#external-contributions) ### New and noteworthy -#### New Rule Designer - -Thanks to [Clément Fournier](https://github.com/oowekyala), we now have a new rule designer GUI, which -is based on JavaFX. It replaces the old designer and can be started via - -* `bin/run.sh designer` (on Unix-like platform such as Linux and Mac OS X) -* `bin\designer.bat` (on Windows) - -Note: At least Java8 is required for the designer. The old designer is still available -as `designerold` but will be removed with the next major release. - -#### Java 9 support - -The Java grammar has been updated to support analyzing Java 9 projects: - -* private methods in interfaces are possible -* The underscore "\_" is considered an invalid identifier -* Diamond operator for anonymous classes -* The module declarations in `module-info.java` can be parsed -* Concise try-with-resources statements are supported - -Java 9 support is enabled by default. You can switch back to an older java version -via the command line, e.g. `-language java -version 1.8`. - -#### Revamped Apex CPD - -We are now using the Apex Jorje Lexer to tokenize Apex code for CPD. This change means: - -* All comments are now ignored for CPD. This is consistent with how other languages such as Java and Groovy work. -* Tokenization honors the language specification, which improves accuracy. - -CPD will therefore have less false positives and false negatives. - -#### Java Type Resolution - -As part of Google Summer of Code 2017, [Bendegúz Nagy](https://github.com/WinterGrascph) worked on type resolution -for Java. For this release he has extended support for method calls for both instance and static methods. - -Method shadowing and overloading are supported, as are varargs. However, the selection of the target method upon -the presence of generics and type inference is still work in progress. Expect it in forecoming releases. - -As for fields, the basic support was in place for release 5.8.0, but has now been expanded to support static fields. - -#### Metrics Framework - -As part of Google Summer of Code 2017, [Clément Fournier](https://github.com/oowekyala) is worked -on the new metrics framework for object-oriented metrics. - -There are already a couple of metrics (e.g. ATFD, WMC, Cyclo, LoC) implemented. More metrics are planned. -Based on those metrics, rules like "GodClass" detection could be implemented more easily. -The following rules benefit from the metrics framework: NcssCount (java), NPathComplexity (java), -CyclomaticComplexity (both java and apex). - -The Metrics framework has been abstracted and is available in `pmd-core` for other languages. With this -PMD release, the metrics framework is supported for both Java and Apex. - -#### Error Reporting - -A number of improvements on error reporting have taken place, meaning changes to some of the report formats. - -Also of note, the xml report now provides a XML Schema definition, allowing easier parsing and validation. - -##### Processing Errors - -Processing errors can now provide not only the message previously included on some reports, but also a full stacktrace. -This will allow better error reports when providing feedback to the PMD team and help in debugging issues. - -The report formats providing full stacktrace of errors are: - -* html -* summaryhtml -* textcolor -* vbhtml -* xml - -##### Configuration Errors - -For a long time reports have been notified of configuration errors on rules, but they have remained hidden. -On a push to make these more evident to users, and help them get the best results out of PMD, we have started -to include them on the reports. - -So far, only reports that include processing errors are showing configuration errors. In other words, the report formats -providing configuration error reporting are: - -* csv -* html -* summaryhtml -* text -* textcolor -* vbhtml -* xml - -As we move forward we will be able to detect and report more configuration errors (ie: incomplete `auxclasspath`) -and include them to such reports. - -#### Apex Rule Suppression - -Apex violations can now be suppressed very similarly to how it's done in Java, by making use of a -`@SuppressWarnings` annotation. - -Supported syntax includes: - -``` -@SupressWarnings('PMD') // to supress all Apex rules -@SupressWarnings('all') // to supress all Apex rules -@SupressWarnings('PMD.ARuleName') // to supress only the rule named ARuleName -@SupressWarnings('PMD.ARuleName, PMD.AnotherRuleName') // to supress only the rule named ARuleName or AnotherRuleName -``` - -Notice this last scenario is slightly different to the Java syntax. This is due to differences in the Apex grammar for annotations. - -#### Rule Categories - -All built-in rules have been sorted into one of eight categories: - -1. **Best Practices**: These are rules which enforce generally accepted best practices. -2. **Code Style**: These rules enforce a specific coding style. -3. **Design**: Rules that help you discover design issues. -4. **Documentation**: These rules are related to code documentation. -5. **Error Prone**: Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. -6. **Multithreading**: These are rules that flag issues when dealing with multiple threads of execution. -7. **Performance**: Rules that flag suboptimal code. -8. **Security**: Rules that flag potential security flaws. - -These categories help you to find rules and figure out the relevance and impact for your project. - -All rules have been moved accordingly, e.g. the rule "JumbledIncrementer", which was previously defined in the -ruleset "java-basic" has now been moved to the "Error Prone" category. The new rule reference to be used is -``. - -The old rulesets like "java-basic" are still kept for backwards-compatibility but will be removed eventually. -The rule reference documentation has been updated to reflect these changes. - -#### New Rules - -* The new Java rule `NcssCount` (category `design`) replaces the three rules "NcssConstructorCount", "NcssMethodCount", - and "NcssTypeCount". The new rule uses the metrics framework to achieve the same. It has two properties, to - define the report level for method and class sizes separately. Constructors and methods are considered the same. - -* The new Java rule `DoNotExtendJavaLangThrowable` (category `errorprone`) is a companion for the - `java-strictexception.xml/DoNotExtendJavaLangError`, detecting direct extensions of `java.lang.Throwable`. - -* The new Java rule `ForLoopCanBeForeach` (category `errorprone`) helps to identify those for-loops that can - be safely refactored into for-each-loops available since java 1.5. - -* The new Java rule `AvoidFileStream` (category `performance`) helps to identify code relying on `FileInputStream` / `FileOutputStream` - which, by using a finalizer, produces extra / unnecessary overhead to garbage collection, and should be replaced with - `Files.newInputStream` / `Files.newOutputStream` available since java 1.7. - -* The new Java rule `DataClass` (category `design`) detects simple data-holders without behaviour. This might indicate - that the behaviour is scattered elsewhere and the data class exposes the internal data structure, - which breaks encapsulation. - -* The new Apex rule `AvoidDirectAccessTriggerMap` (category `errorprone`) helps to identify direct array access to triggers, - which can produce bugs by either accessing non-existing indexes, or leaving them out. You should use for-each-loops - instead. - -* The new Apex rule `AvoidHardcodingId` (category `errorprone`) detects hardcoded strings that look like identifiers - and flags them. Record IDs change between environments, meaning hardcoded ids are bound to fail under a different - setup. - -* The new Apex rule `CyclomaticComplexity` (category `design`) detects overly complex classes and methods. The - report threshold can be configured separately for classes and methods. - -* A whole bunch of new rules has been added to Apex. They all fit into the category `errorprone`. - The 5 rules are migrated for Apex from the equivalent Java rules and include: - * `EmptyCatchBlock` to detect catch blocks completely ignoring exceptions. - * `EmptyIfStmt` for if blocks with no content, that can be safely removed. - * `EmptyTryOrFinallyBlock` for empty try / finally blocks that can be safely removed. - * `EmptyWhileStmt` for empty while loops that can be safely removed. - * `EmptyStatementBlock` for empty code blocks that can be safely removed. - -* The new Apex rule `AvoidSoslInLoops` (category `performance`) is the companion of the old - `AvoidSoqlInLoops` rule, flagging SOSL (Salesforce Object Search Language) queries when within - loops, to avoid governor issues, and hitting the database too often. - -#### Modified Rules - -* The Java rule `UnnecessaryFinalModifier` (category `codestyle`, former ruleset `java-unnecessarycode`) - has been merged into the rule `UnnecessaryModifier`. As part of this, the rule has been revamped to detect more cases. - It will now flag anonymous class' methods marked as final (can't be overridden, so it's pointless), along with - final methods overridden / defined within enum instances. It will also flag `final` modifiers on try-with-resources. - -* The Java rule `UnnecessaryParentheses` (category `codestyle`, former ruleset `java-controversial`) - has been merged into `UselessParentheses` (category `codestyle`, former ruleset `java-unnecessary`). - The rule covers all scenarios previously covered by either rule. - -* The Java rule `UncommentedEmptyConstructor` (category `documentation`, former ruleset `java-design`) - will now ignore empty constructors annotated with `javax.inject.Inject`. - -* The Java rule `AbstractClassWithoutAnyMethod` (category `bestpractices`, former ruleset `java-design`) - will now ignore classes annotated with `com.google.auto.value.AutoValue`. - -* The Java rule `GodClass` (category `design', former ruleset `java-design`) has been revamped to use - the new metrics framework. - -* The Java rule `LooseCoupling` (category `bestpractices`, former ruleset `java-coupling`) has been - replaced by the typeresolution-based implementation. - -* The Java rule `CloneMethodMustImplementCloneable` (category `errorprone`, former ruleset `java-clone`) - has been replaced by the typeresolution-based - implementation and is now able to detect cases if a class implements or extends a Cloneable class/interface. - -* The Java rule `UnusedImports` (category `bestpractices`, former ruleset `java-imports`) has been - replaced by the typeresolution-based - implementation and is now able to detect unused on-demand imports. - -* The Java rule `SignatureDeclareThrowsException` (category `design`, former ruleset 'java-strictexception') - has been replaced by the - typeresolution-based implementation. It has a new property `IgnoreJUnitCompletely`, which allows all - methods in a JUnit testcase to throw exceptions. - -* The Java rule `NPathComplexity` (category `design`, former ruleset `java-codesize`) has been revamped - to use the new metrics framework. - Its report threshold can be configured via the property `reportLevel`, which replaces the now - deprecated property `minimum`. - -* The Java rule `CyclomaticComplexity` (category `design`, former ruleset `java-codesize`) has been - revamped to use the new metrics framework. - Its report threshold can be configured via the properties `classReportLevel` and `methodReportLevel` separately. - The old property `reportLevel`, which configured the level for both total class and method complexity, - is deprecated. - -* The Java rule `CommentRequired` (category `documentation`, former ruleset `java-comments`) - has been revamped to include 2 new properties: - * `accessorCommentRequirement` to specify documentation requirements for getters and setters (default to `ignored`) - * `methodWithOverrideCommentRequirement` to specify documentation requirements for methods annotated with `@Override` (default to `ignored`) - -* The Java rule `EmptyCatchBlock` (category `errorprone`, former ruleset `java-empty`) has been changed to ignore - exceptions named `ignore` or `expected` by default. You can still override this behaviour by setting the `allowExceptionNameRegex` property. - -* The Java rule `OptimizableToArrayCall` (category `performance`, former ruleset `design`) has been - modified to fit for the current JVM implementations: It basically detects now the opposite and suggests to - use `Collection.toArray(new E[0])` with a zero-sized array. - See [Arrays of Wisdom of the Ancients](https://shipilev.net/blog/2016/arrays-wisdom-ancients/). - -#### Deprecated Rules - -* The Java rules `NcssConstructorCount`, `NcssMethodCount`, and `NcssTypeCount` (ruleset `java-codesize`) have been - deprecated. They will be replaced by the new rule `NcssCount` in the category `design`. - -* The Java rule `LooseCoupling` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name - from category `bestpractices` instead. - -* The Java rule `CloneMethodMustImplementCloneable` in ruleset `java-typeresolution` is deprecated. Use the rule with - the same name from category `errorprone` instead. - -* The Java rule `UnusedImports` in ruleset `java-typeresolution` is deprecated. Use the rule with - the same name from category `bestpractices` instead. - -* The Java rule `SignatureDeclareThrowsException` in ruleset `java-typeresolution` is deprecated. Use the rule - with the same name from category `design` instead. - -* The Java rule `EmptyStaticInitializer` in ruleset `java-empty` is deprecated. Use the rule `EmptyInitializer` - from the category `errorprone`, which covers both static and non-static empty initializers.` - -* The Java rules `GuardDebugLogging` (ruleset `java-logging-jakarta-commons`) and `GuardLogStatementJavaUtil` - (ruleset `java-logging-java`) have been deprecated. Use the rule `GuardLogStatement` from the - category `bestpractices`, which covers all cases regardless of the logging framework. - -#### Removed Rules - -* The deprecated Java rule `UseSingleton` has been removed from the ruleset `java-design`. The rule has been renamed - long time ago to `UseUtilityClass` (category `design`). - -#### Java Symbol Table - -A [bug in symbol table](https://github.com/pmd/pmd/pull/549/commits/0958621ca884a8002012fc7738308c8dfc24b97c) prevented -the symbol table analysis to properly match primitive arrays types. The issue [affected the `java-unsedcode/UnusedPrivateMethod`](https://github.com/pmd/pmd/issues/521) -rule, but other rules may now produce improved results as consequence of this fix. - -#### Apex Parser Update - -The Apex parser version was bumped, from `1.0-sfdc-187` to `210-SNAPSHOT`. This update let us take full advantage -of the latest improvements from Salesforce, but introduces some breaking changes: - -* `BlockStatements` are now created for all control structures, even if no brace is used. We have therefore added - a `hasCurlyBrace` method to differentiate between both scenarios. -* New AST node types are available. In particular `CastExpression`, `ConstructorPreamble`, `IllegalStoreExpression`, - `MethodBlockStatement`, `Modifier`, `MultiStatement`, `NestedExpression`, `NestedStoreExpression`, - `NewKeyValueObjectExpression` and `StatementExecuted` -* Some nodes have been removed. Such is the case of `TestNode`, `DottedExpression` and `NewNameValueObjectExpression` - (replaced by `NewKeyValueObjectExpression`) - -All existing rules have been updated to reflect these changes. If you have custom rules, be sure to update them. - -For more info about the included Apex parser, see the new pmd module "pmd-apex-jorje", which packages and provides -the parser as a binary. - -#### Incremental Analysis - -The incremental analysis feature first introduced in PMD 5.6.0 has been enhanced. A few minor issues have been fixed, -and several improvements have been performed to make it more accurate. - -The cache will now detect changes to the JARs referenced in the `auxclasspath` instead of simply looking at their paths -and order. This means that if you are referencing a JAR you are overwriting in some way, the incremental analysis can -now detect it and invalidate it's cache to avoid false reports. - -Similarly, any changes to the execution classpath of PMD will invalidate the cache. This means that if you have custom -rules packaged in a jar, any changes to it will invalidate the cache automatically. - -We have also improved logging on the analysis code, allowing better insight into how the cache is performing, -under debug / verbose builds you can even see individual hits / misses to the cache (and the reason for any miss!) - -Finally, as this feature keeps maturing, we are gently pushing this forward. If not using incremental analysis, -a warning will now be produced suggesting users to adopt it for better performance. - -#### Rule and Report Properties - -The implementation around the properties support for rule properties and report properties has been revamped -to be fully typesafe. Along with that change, the support classes have been moved into an own -package `net.sourceforge.pmd.properties`. While there is no change necessary in the ruleset XML files, -when using/setting values for rules, there are adjustments necessary when declaring properties in Java-implemented -rules. - -Rule properties can be declared both for Java based rules and XPath rules. -This is now very well documented in [Working with properties](pmd_devdocs_working_with_properties.html). - -With PMD 6.0.0, multivalued properties are now also possible with XPath rules. - ### Fixed Issues -* all - * [#394](https://github.com/pmd/pmd/issues/394): \[core] PMD exclude rules are failing with IllegalArgumentException with non-default minimumPriority - * [#532](https://github.com/pmd/pmd/issues/532): \[core] security concerns on URL-based rulesets - * [#538](https://github.com/pmd/pmd/issues/538): \[core] Provide an XML Schema for XML reports - * [#600](https://github.com/pmd/pmd/issues/600): \[core] Nullpointer while creating cache File - * [#604](https://github.com/pmd/pmd/issues/604): \[core] Incremental analysis should detect changes to jars in classpath - * [#608](https://github.com/pmd/pmd/issues/608): \[core] Add DEBUG log when applying incremental analysis - * [#618](https://github.com/pmd/pmd/issues/618): \[core] Incremental Analysis doesn't close file correctly on Windows upon a cache hit - * [#643](https://github.com/pmd/pmd/issues/643): \[core] PMD Properties (dev-properties) breaks markup on CodeClimateRenderer - * [#680](https://github.com/pmd/pmd/pull/680): \[core] Isolate classloaders for runtime and auxclasspath - * [#762](https://github.com/pmd/pmd/issues/762): \[core] Remove method and file property from available property descriptors for XPath rules - * [#763](https://github.com/pmd/pmd/issues/763): \[core] Turn property descriptor util into an enum and enrich its interface -* apex - * [#265](https://github.com/pmd/pmd/issues/265): \[apex] Make Rule suppression work - * [#488](https://github.com/pmd/pmd/pull/488): \[apex] Use Apex lexer for CPD - * [#489](https://github.com/pmd/pmd/pull/489): \[apex] Update Apex compiler - * [#500](https://github.com/pmd/pmd/issues/500): \[apex] Running through CLI shows jorje optimization messages - * [#605](https://github.com/pmd/pmd/issues/605): \[apex] java.lang.NoClassDefFoundError in the latest build - * [#637](https://github.com/pmd/pmd/issues/637): \[apex] Avoid SOSL in loops - * [#760](https://github.com/pmd/pmd/issues/760): \[apex] EmptyStatementBlock complains about missing rather than empty block - * [#766](https://github.com/pmd/pmd/issues/766): \[apex] Replace old Jorje parser with new one - * [#768](https://github.com/pmd/pmd/issues/768): \[apex] java.lang.NullPointerException from PMD -* cpp - * [#448](https://github.com/pmd/pmd/issues/448): \[cpp] Write custom CharStream to handle continuation characters -* java - * [#1454](https://sourceforge.net/p/pmd/bugs/1454/): \[java] OptimizableToArrayCall is outdated and invalid in current JVMs - * [#1513](https://sourceforge.net/p/pmd/bugs/1513/): \[java] Remove deprecated rule UseSingleton - * [#328](https://github.com/pmd/pmd/issues/328): \[java] java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/jsp/PageContext - * [#487](https://github.com/pmd/pmd/pull/487): \[java] Fix typeresolution for anonymous extending object - * [#496](https://github.com/pmd/pmd/issues/496): \[java] processing error on generics inherited from enclosing class - * [#510](https://github.com/pmd/pmd/issues/510): \[java] Typeresolution fails on a simple primary when the source is loaded from a class literal - * [#527](https://github.com/pmd/pmd/issues/527): \[java] Lombok getter annotation on enum is not recognized correctly - * [#534](https://github.com/pmd/pmd/issues/534): \[java] NPE in MethodTypeResolution for static methods - * [#603](https://github.com/pmd/pmd/issues/603): \[core] incremental analysis should invalidate upon Java rule plugin changes - * [#650](https://github.com/pmd/pmd/issues/650): \[java] ProcesingError analyzing code under 5.8.1 - * [#732](https://github.com/pmd/pmd/issues/732): \[java] LinkageError with aux classpath -* java-basic - * [#565](https://github.com/pmd/pmd/pull/565): \[java] False negative on DontCallThreadRun when extending Thread -* java-comments - * [#396](https://github.com/pmd/pmd/issues/396): \[java] CommentRequired: add properties to ignore @Override method and getters / setters - * [#536](https://github.com/pmd/pmd/issues/536): \[java] CommentDefaultAccessModifierRule ignores constructors -* java-controversial - * [#388](https://github.com/pmd/pmd/issues/388): \[java] controversial.AvoidLiteralsInIfCondition 0.0 false positive - * [#408](https://github.com/pmd/pmd/issues/408): \[java] DFA not analyzing asserts - * [#537](https://github.com/pmd/pmd/issues/537): \[java] UnnecessaryParentheses fails to detect obvious scenario -* java-design - * [#357](https://github.com/pmd/pmd/issues/357): \[java] UncommentedEmptyConstructor consider annotations on Constructor - * [#438](https://github.com/pmd/pmd/issues/438): \[java] Relax AbstractClassWithoutAnyMethod when class is annotated by @AutoValue - * [#590](https://github.com/pmd/pmd/issues/590): \[java] False positive on MissingStaticMethodInNonInstantiatableClass -* java-logging - * [#457](https://github.com/pmd/pmd/issues/457): \[java] Merge all log guarding rules - * [#721](https://github.com/pmd/pmd/issues/721): \[java] NPE in PMD 5.8.1 InvalidSlf4jMessageFormat -* java-sunsecure - * [#468](https://github.com/pmd/pmd/issues/468): \[java] ArrayIsStoredDirectly false positive -* java-unusedcode - * [#521](https://github.com/pmd/pmd/issues/521): \[java] UnusedPrivateMethod returns false positives with primitive data type in map argument -* java-unnecessarycode - * [#412](https://github.com/pmd/pmd/issues/412): \[java] java-unnecessarycode/UnnecessaryFinalModifier missing cases - * [#676](https://github.com/pmd/pmd/issues/676): \[java] java-unnecessarycode/UnnecessaryFinalModifier on try-with-resources - ### API Changes -* The class `net.sourceforge.pmd.lang.dfa.NodeType` has been converted to an enum. - All node types are enum members now instead of int constants. The names for node types are retained. - -* The *Properties API* (rule and report properties) has been revamped to be fully typesafe. This is everything - around `net.sourceforge.pmd.properties.PropertyDescriptor`. - - Note: All classes related to properties have been moved into the package `net.sourceforge.pmd.properties`. - -* The rule classes `net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestClassShouldHaveAsserts` - and `net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestShouldNotUseSeeAllDataTrue` have been - renamed to `ApexUnitTestClassShouldHaveAssertsRule` and `ApexUnitTestShouldNotUseSeeAllDataTrueRule`, - respectively. This is to comply with the naming convention, that each rule class should be suffixed with "Rule". - - This change has no impact on custom rulesets, since the rule names themselves didn't change. - -* The never implemented method `PMD.processFiles(PMDConfiguration, RuleSetFactory, Collection, RuleContext, ProgressMonitor)` along with the interface `ProgressMonitor` has been removed. - -* The method `PMD.setupReport(RuleSets, RuleContext, String)` is gone. It was used to report dysfunctional - rules. But PMD does this now automatically before processing the files, so there is no need for this - method anymore. - -* All APIs deprecated in older versions are now removed. This includes: - * `Renderer.getPropertyDefinitions` - * `AbstractRenderer.defineProperty(String, String)` - * `AbstractRenderer.propertyDefinitions` - * `ReportListener` - * `Report.addListener(ReportListener)` - * `SynchronizedReportListener` - * `CPDConfiguration.CPDConfiguration(int, Language, String)` - * `CPDConfiguration.getRendererFromString(String)` - * `StreamUtil` - * `StringUtil.appendXmlEscaped(StringBuilder, String)` - * `StringUtil.htmlEncode(String)` - - -* Several methods in `net.sourceforge.pmd.util.CollectionUtil` have been deprecated and will be removed in PMD 7.0.0. In particular: - * `CollectionUtil.addWithoutDuplicates(T[], T)` - * `CollectionUtil.addWithoutDuplicates(T[], T[])` - * `CollectionUtil.areSemanticEquals(T[], T[])` - * `CollectionUtil.areEqual(Object, Object)` - * `CollectionUtil.arraysAreEqual(Object, Object)` - * `CollectionUtil.valuesAreTransitivelyEqual(Object[], Object[])` - - -* Several methods in `net.sourceforge.pmd.util.StringUtil` have been deprecated and will be removed in PMD 7.0.0. In particular: - * `StringUtil.startsWithAny(String, String[])` - * `StringUtil.isNotEmpty(String)` - * `StringUtil.isEmpty(String)` - * `StringUtil.isMissing(String)` - * `StringUtil.areSemanticEquals(String, String)` - * `StringUtil.replaceString(String, String, String)` - * `StringUtil.replaceString(String, char, String)` - * `StringUtil.substringsOf(String, char)` - * `StringUtil.substringsOf(String, String)` - * `StringUtil.asStringOn(StringBuffer, Iterator, String)` - * `StringUtil.asStringOn(StringBuilder, Object[], String)` - * `StringUtil.lpad(String, int)` - -* The class `net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition` is now abstract, and has been enhanced - to provide several new methods. - -* The constructor of `net.sourceforge.pmd.RuleSetFactory`, which took a `ClassLoader` is deprecated. - Please use the alternative constructor with the `net.sourceforge.pmd.util.ResourceLoader` instead. - -* The following GUI related classes have been deprecated and will be removed in PMD 7.0.0. - The tool "bgastviewer", that could be started via the script `bgastviewer.bat` or `run.sh bgastviewer` is - deprecated, too, and will be removed in PMD 7.0.0. - Both the "old designer" and "bgastviewer" are replaced by the [New Rule Designer](#new-rule-designer). - * `net.sourceforge.pmd.util.designer.CodeEditorTextPane` - * `net.sourceforge.pmd.util.designer.CreateXMLRulePanel` - * `net.sourceforge.pmd.util.designer.Designer` - * `net.sourceforge.pmd.util.designer.DFAPanel` - * `net.sourceforge.pmd.util.designer.LineGetter` - * `net.sourceforge.pmd.util.viewer.Viewer` - * `net.sourceforge.pmd.util.viewer.gui.ActionCommands` - * `net.sourceforge.pmd.util.viewer.gui.ASTPanel` - * `net.sourceforge.pmd.util.viewer.gui.EvaluationResultsPanel` - * `net.sourceforge.pmd.util.viewer.gui.MainFrame` - * `net.sourceforge.pmd.util.viewer.gui.ParseExceptionHandler` - * `net.sourceforge.pmd.util.viewer.gui.SourceCodePanel` - * `net.sourceforge.pmd.util.viewer.gui.XPathPanel` - * `net.sourceforge.pmd.util.viewer.gui.menu.ASTNodePopupMenu` - * `net.sourceforge.pmd.util.viewer.gui.menu.AttributesSubMenu` - * `net.sourceforge.pmd.util.viewer.gui.menu.SimpleNodeSubMenu` - * `net.sourceforge.pmd.util.viewer.gui.menu.XPathFragmentAddingItem` - * `net.sourceforge.pmd.util.viewer.model.ASTModel` - * `net.sourceforge.pmd.util.viewer.model.AttributeToolkit` - * `net.sourceforge.pmd.util.viewer.model.SimpleNodeTreeNodeAdapter` - * `net.sourceforge.pmd.util.viewer.model.ViewerModel` - * `net.sourceforge.pmd.util.viewer.model.ViewerModelEvent` - * `net.sourceforge.pmd.util.viewer.model.ViewerModelListener` - * `net.sourceforge.pmd.util.viewer.util.NLS` - -* The following methods in `net.sourceforge.pmd.Rule` have been deprecated and will be removed in PMD 7.0.0. - All methods are replaced by their bean-like counterparts - * `void setUsesDFA()`. Use `void setDfa(boolean)` instead. - * `boolean usesDFA()`. Use `boolean isDfa()` instead. - * `void setUsesTypeResolution()`. Use `void setTypeResolution(boolean)` instead. - * `boolean usesTypeResolution()`. Use `boolean isTypeResolution()` instead. - * `void setUsesMultifile()`. Use `void setMultifile(boolean)` instead. - * `boolean usesMultifile()`. Use `boolean isMultifile()` instead. - * `boolean usesRuleChain()`. Use `boolean isRuleChain()` instead. - -### External Contributions - -* [#287](https://github.com/pmd/pmd/pull/287): \[apex] Make Rule suppression work - [Robert Sösemann](https://github.com/up2go-rsoesemann) -* [#420](https://github.com/pmd/pmd/pull/420): \[java] Fix UR anomaly in assert statements - [Clément Fournier](https://github.com/oowekyala) -* [#482](https://github.com/pmd/pmd/pull/482): \[java] Metrics testing framework + improved capabilities for metrics - [Clément Fournier](https://github.com/oowekyala) -* [#484](https://github.com/pmd/pmd/pull/484): \[core] Changed linux usage to a more unix like path - [patriksevallius](https://github.com/patriksevallius) -* [#486](https://github.com/pmd/pmd/pull/486): \[java] Add basic method typeresolution - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#492](https://github.com/pmd/pmd/pull/492): \[java] Typeresolution for overloaded methods - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#495](https://github.com/pmd/pmd/pull/495): \[core] Custom rule reinitialization code - [Clément Fournier](https://github.com/oowekyala) -* [#479](https://github.com/pmd/pmd/pull/479): \[core] Typesafe and immutable properties - [Clément Fournier](https://github.com/oowekyala) -* [#499](https://github.com/pmd/pmd/pull/499): \[java] Metrics memoization tests - [Clément Fournier](https://github.com/oowekyala) -* [#501](https://github.com/pmd/pmd/pull/501): \[java] Add support for most specific vararg method type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#502](https://github.com/pmd/pmd/pull/502): \[java] Add support for static field type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#505](https://github.com/pmd/pmd/pull/505): \[java] Followup on metrics - [Clément Fournier](https://github.com/oowekyala) -* [#506](https://github.com/pmd/pmd/pull/506): \[java] Add reduction rules to type inference - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#511](https://github.com/pmd/pmd/pull/511): \[core] Prepare abstraction of the metrics framework - [Clément Fournier](https://github.com/oowekyala) -* [#512](https://github.com/pmd/pmd/pull/512): \[java] Add incorporation to type inference - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#513](https://github.com/pmd/pmd/pull/513): \[java] Fix for maximally specific method selection - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#514](https://github.com/pmd/pmd/pull/514): \[java] Add static method type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#517](https://github.com/pmd/pmd/pull/517): \[doc] Metrics documentation - [Clément Fournier](https://github.com/oowekyala) -* [#518](https://github.com/pmd/pmd/pull/518): \[core] Properties refactoring: factorized enumerated property - [Clément Fournier](https://github.com/oowekyala) -* [#523](https://github.com/pmd/pmd/pull/523): \[java] Npath complexity metric and rule - [Clément Fournier](https://github.com/oowekyala) -* [#524](https://github.com/pmd/pmd/pull/524): \[java] Add support for explicit type arguments with method invocation - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#525](https://github.com/pmd/pmd/pull/525): \[core] Fix line ending and not ignored files issues - [Matias Comercio](https://github.com/MatiasComercio) -* [#528](https://github.com/pmd/pmd/pull/528): \[core] Fix typo - [Ayoub Kaanich](https://github.com/kayoub5) -* [#529](https://github.com/pmd/pmd/pull/529): \[java] Abstracted the Java metrics framework - [Clément Fournier](https://github.com/oowekyala) -* [#530](https://github.com/pmd/pmd/pull/530): \[java] Fix issue #527: Lombok getter annotation on enum is not recognized correctly - [Clément Fournier](https://github.com/oowekyala) -* [#533](https://github.com/pmd/pmd/pull/533): \[core] improve error message - [Dennis Kieselhorst](https://github.com/deki) -* [#535](https://github.com/pmd/pmd/pull/535): \[apex] Fix broken Apex visitor adapter - [Clément Fournier](https://github.com/oowekyala) -* [#542](https://github.com/pmd/pmd/pull/542): \[java] Metrics abstraction - [Clément Fournier](https://github.com/oowekyala) -* [#545](https://github.com/pmd/pmd/pull/545): \[apex] Apex metrics framework - [Clément Fournier](https://github.com/oowekyala) -* [#548](https://github.com/pmd/pmd/pull/548): \[java] Metrics documentation - [Clément Fournier](https://github.com/oowekyala) -* [#550](https://github.com/pmd/pmd/pull/550): \[java] Add basic resolution to type inference - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#553](https://github.com/pmd/pmd/pull/553): \[java] Refactored ParserTst into a static utility class + add getSourceFromClass - [Clément Fournier](https://github.com/oowekyala) -* [#554](https://github.com/pmd/pmd/pull/554): \[java] Fix #537: UnnecessaryParentheses fails to detect obvious scenario - [Clément Fournier](https://github.com/oowekyala) -* [#555](https://github.com/pmd/pmd/pull/555): \[java] Changed metrics/CyclomaticComplexityRule to use WMC when reporting classes - [Clément Fournier](https://github.com/oowekyala) -* [#556](https://github.com/pmd/pmd/pull/556): \[java] Fix #357: UncommentedEmptyConstructor consider annotations on Constructor - [Clément Fournier](https://github.com/oowekyala) -* [#557](https://github.com/pmd/pmd/pull/557): \[java] Fix NPath metric not counting ternaries correctly - [Clément Fournier](https://github.com/oowekyala) -* [#563](https://github.com/pmd/pmd/pull/563): \[java] Add support for basic method type inference for strict invocation - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#566](https://github.com/pmd/pmd/pull/566): \[java] New rule in migrating ruleset: ForLoopCanBeForeach - [Clément Fournier](https://github.com/oowekyala) -* [#567](https://github.com/pmd/pmd/pull/567): \[java] Last API change for metrics (metric options) - [Clément Fournier](https://github.com/oowekyala) -* [#570](https://github.com/pmd/pmd/pull/570): \[java] Model lower, upper and intersection types - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#573](https://github.com/pmd/pmd/pull/573): \[java] Data class rule - [Clément Fournier](https://github.com/oowekyala) -* [#576](https://github.com/pmd/pmd/pull/576): \[doc]\[java] Add hint for Guava users in InefficientEmptyStringCheck - [mmoehring](https://github.com/mmoehring) -* [#578](https://github.com/pmd/pmd/pull/578): \[java] Refactored god class rule - [Clément Fournier](https://github.com/oowekyala) -* [#579](https://github.com/pmd/pmd/pull/579): \[java] Update parsing to produce upper and lower bounds - [Bendegúz Nagy](https://github.com/WinterGrascph) -* [#580](https://github.com/pmd/pmd/pull/580): \[core] Add AbstractMetric to topple the class hierarchy of metrics - [Clément Fournier](https://github.com/oowekyala) -* [#581](https://github.com/pmd/pmd/pull/581): \[java] Relax AbstractClassWithoutAnyMethod when class is annotated by @AutoValue - [Niklas Baudy](https://github.com/vanniktech) -* [#583](https://github.com/pmd/pmd/pull/583): \[java] Documentation about writing metrics - [Clément Fournier](https://github.com/oowekyala) -* [#585](https://github.com/pmd/pmd/pull/585): \[java] Moved NcssCountRule to codesize.xml - [Clément Fournier](https://github.com/oowekyala) -* [#587](https://github.com/pmd/pmd/pull/587): \[core] Properties refactoring: Move static constants of ValueParser to class ValueParserConstants - [Clément Fournier](https://github.com/oowekyala) -* [#588](https://github.com/pmd/pmd/pull/588): \[java] XPath function to compute metrics - [Clément Fournier](https://github.com/oowekyala) -* [#598](https://github.com/pmd/pmd/pull/598): \[java] Fix #388: controversial.AvoidLiteralsInIfCondition 0.0 false positive - [Clément Fournier](https://github.com/oowekyala) -* [#602](https://github.com/pmd/pmd/pull/602): \[java] \[apex] Separate multifile analysis from metrics - [Clément Fournier](https://github.com/oowekyala) -* [#620](https://github.com/pmd/pmd/pull/620): \[core] Moved properties to n.s.pmd.properties - [Clément Fournier](https://github.com/oowekyala) -* [#625](https://github.com/pmd/pmd/pull/625): \[apex] empty code ruleset for apex - [Jan Aertgeerts](https://github.com/JAertgeerts) -* [#632](https://github.com/pmd/pmd/pull/632): \[apex] Add AvoidDirectAccessTriggerMap rule to the style set - [Jan Aertgeerts](https://github.com/JAertgeerts) -* [#644](https://github.com/pmd/pmd/pull/644): \[core] Prevent internal dev-properties from being displayed on CodeClimate renderer - [Filipe Esperandio](https://github.com/filipesperandio) -* [#660](https://github.com/pmd/pmd/pull/660): \[apex] avoid sosl in loops - [Jan Aertgeerts](https://github.com/JAertgeerts) -* [#661](https://github.com/pmd/pmd/pull/661): \[apex] avoid hardcoding id's - [Jan Aertgeerts](https://github.com/JAertgeerts) -* [#666](https://github.com/pmd/pmd/pull/666): \[java] Add DoNotExtendJavaLangThrowable rule - [Robert Painsi](https://github.com/robertpainsi) -* [#668](https://github.com/pmd/pmd/pull/668): \[core] Fix javadoc warnings on pmd-core - [Clément Fournier](https://github.com/oowekyala) -* [#669](https://github.com/pmd/pmd/pull/669): \[core] Builder pattern for properties - [Clément Fournier](https://github.com/oowekyala) -* [#675](https://github.com/pmd/pmd/pull/675): \[java] Fix in Java grammar: Try with final resource node error - [Gonzalo Ibars Ingman](https://github.com/gibarsin) -* [#679](https://github.com/pmd/pmd/pull/679): \[core] Token scheme generalization - [Gonzalo Ibars Ingman](https://github.com/gibarsin) -* [#694](https://github.com/pmd/pmd/pull/694): \[core] Add minor fixes to root pom - [Matias Comercio](https://github.com/MatiasComercio) -* [#696](https://github.com/pmd/pmd/pull/696): \[core] Add remove operation over nodes - [Matias Comercio](https://github.com/MatiasComercio) -* [#711](https://github.com/pmd/pmd/pull/711): \[ui] New rule designer - [Clément Fournier](https://github.com/oowekyala) -* [#722](https://github.com/pmd/pmd/pull/722): \[java] Move NPathComplexity from metrics to design - [Clément Fournier](https://github.com/oowekyala) -* [#723](https://github.com/pmd/pmd/pull/723): \[core] Rule factory refactoring - [Clément Fournier](https://github.com/oowekyala) -* [#726](https://github.com/pmd/pmd/pull/726): \[java] Fix issue #721 (NPE in InvalidSlf4jMessageFormat) - [Clément Fournier](https://github.com/oowekyala) -* [#727](https://github.com/pmd/pmd/pull/727): \[core] Fix #725: numeric property descriptors now check their default value - [Clément Fournier](https://github.com/oowekyala) -* [#733](https://github.com/pmd/pmd/pull/733): \[java] Some improvements to CommentRequired - [Clément Fournier](https://github.com/oowekyala) -* [#734](https://github.com/pmd/pmd/pull/734): \[java] Move CyclomaticComplexity from metrics to design - [Clément Fournier](https://github.com/oowekyala) -* [#736](https://github.com/pmd/pmd/pull/736): \[core] Make Saxon support multi valued XPath properties - [Clément Fournier](https://github.com/oowekyala) -* [#737](https://github.com/pmd/pmd/pull/737): \[doc] Fix NPathComplexity documentation bad rendering - [Clément Fournier](https://github.com/oowekyala) -* [#744](https://github.com/pmd/pmd/pull/744): \[doc] Added Apex to supported languages - [Michał Kuliński](https://github.com/coola) -* [#746](https://github.com/pmd/pmd/pull/746): \[doc] Fix typo in incremental analysis log message - [Clément Fournier](https://github.com/oowekyala) -* [#749](https://github.com/pmd/pmd/pull/749): \[doc] Update the documentation for properties - [Clément Fournier](https://github.com/oowekyala) -* [#758](https://github.com/pmd/pmd/pull/758): \[core] Expose the full mapping from property type id to property extractor - [Clément Fournier](https://github.com/oowekyala) -* [#764](https://github.com/pmd/pmd/pull/764): \[core] Prevent method and file property use in XPath rules - [Clément Fournier](https://github.com/oowekyala) -* [#771](https://github.com/pmd/pmd/pull/771): \[apex] Fix Apex metrics framework failing on triggers, refs #768 - [Clément Fournier](https://github.com/oowekyala) -* [#774](https://github.com/pmd/pmd/pull/774): \[java] Avoid using FileInput/Output - see JDK-8080225 - [Chas Honton](https://github.com/chonton) - +### External Contributions \ No newline at end of file diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 4278cd080d..7adfe9a6c8 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -6,6 +6,608 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: http://sourceforge.net/projects/pmd/files/pmd/ +## 15-December-2017 - 6.0.0 + +The PMD team is pleased to announce PMD 6.0.0. + +This is a major release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [New Rule Designer](#new-rule-designer) + * [Java 9 support](#java-9-support) + * [Revamped Apex CPD](#revamped-apex-cpd) + * [Java Type Resolution](#java-type-resolution) + * [Metrics Framework](#metrics-framework) + * [Error Reporting](#error-reporting) + * [Apex Rule Suppression](#apex-rule-suppression) + * [Rule Categories](#rule-categories) + * [New Rules](#new-rules) + * [Modified Rules](#modified-rules) + * [Deprecated Rules](#deprecated-rules) + * [Removed Rules](#removed-rules) + * [Java Symbol Table](#java-symbol-table) + * [Apex Parser Update](#apex-parser-update) + * [Incremental Analysis](#incremental-analysis) + * [Rule and Report Properties](#rule-and-report-properties) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) +* [External Contributions](#external-contributions) + +### New and noteworthy + +#### New Rule Designer + +Thanks to [Clément Fournier](https://github.com/oowekyala), we now have a new rule designer GUI, which +is based on JavaFX. It replaces the old designer and can be started via + +* `bin/run.sh designer` (on Unix-like platform such as Linux and Mac OS X) +* `bin\designer.bat` (on Windows) + +Note: At least Java8 is required for the designer. The old designer is still available +as `designerold` but will be removed with the next major release. + +#### Java 9 support + +The Java grammar has been updated to support analyzing Java 9 projects: + +* private methods in interfaces are possible +* The underscore "\_" is considered an invalid identifier +* Diamond operator for anonymous classes +* The module declarations in `module-info.java` can be parsed +* Concise try-with-resources statements are supported + +Java 9 support is enabled by default. You can switch back to an older java version +via the command line, e.g. `-language java -version 1.8`. + +#### Revamped Apex CPD + +We are now using the Apex Jorje Lexer to tokenize Apex code for CPD. This change means: + +* All comments are now ignored for CPD. This is consistent with how other languages such as Java and Groovy work. +* Tokenization honors the language specification, which improves accuracy. + +CPD will therefore have less false positives and false negatives. + +#### Java Type Resolution + +As part of Google Summer of Code 2017, [Bendegúz Nagy](https://github.com/WinterGrascph) worked on type resolution +for Java. For this release he has extended support for method calls for both instance and static methods. + +Method shadowing and overloading are supported, as are varargs. However, the selection of the target method upon +the presence of generics and type inference is still work in progress. Expect it in forecoming releases. + +As for fields, the basic support was in place for release 5.8.0, but has now been expanded to support static fields. + +#### Metrics Framework + +As part of Google Summer of Code 2017, [Clément Fournier](https://github.com/oowekyala) is worked +on the new metrics framework for object-oriented metrics. + +There are already a couple of metrics (e.g. ATFD, WMC, Cyclo, LoC) implemented. More metrics are planned. +Based on those metrics, rules like "GodClass" detection could be implemented more easily. +The following rules benefit from the metrics framework: NcssCount (java), NPathComplexity (java), +CyclomaticComplexity (both java and apex). + +The Metrics framework has been abstracted and is available in `pmd-core` for other languages. With this +PMD release, the metrics framework is supported for both Java and Apex. + +#### Error Reporting + +A number of improvements on error reporting have taken place, meaning changes to some of the report formats. + +Also of note, the xml report now provides a XML Schema definition, allowing easier parsing and validation. + +##### Processing Errors + +Processing errors can now provide not only the message previously included on some reports, but also a full stacktrace. +This will allow better error reports when providing feedback to the PMD team and help in debugging issues. + +The report formats providing full stacktrace of errors are: + +* html +* summaryhtml +* textcolor +* vbhtml +* xml + +##### Configuration Errors + +For a long time reports have been notified of configuration errors on rules, but they have remained hidden. +On a push to make these more evident to users, and help them get the best results out of PMD, we have started +to include them on the reports. + +So far, only reports that include processing errors are showing configuration errors. In other words, the report formats +providing configuration error reporting are: + +* csv +* html +* summaryhtml +* text +* textcolor +* vbhtml +* xml + +As we move forward we will be able to detect and report more configuration errors (ie: incomplete `auxclasspath`) +and include them to such reports. + +#### Apex Rule Suppression + +Apex violations can now be suppressed very similarly to how it's done in Java, by making use of a +`@SuppressWarnings` annotation. + +Supported syntax includes: + +``` +@SupressWarnings('PMD') // to supress all Apex rules +@SupressWarnings('all') // to supress all Apex rules +@SupressWarnings('PMD.ARuleName') // to supress only the rule named ARuleName +@SupressWarnings('PMD.ARuleName, PMD.AnotherRuleName') // to supress only the rule named ARuleName or AnotherRuleName +``` + +Notice this last scenario is slightly different to the Java syntax. This is due to differences in the Apex grammar for annotations. + +#### Rule Categories + +All built-in rules have been sorted into one of eight categories: + +1. **Best Practices**: These are rules which enforce generally accepted best practices. +2. **Code Style**: These rules enforce a specific coding style. +3. **Design**: Rules that help you discover design issues. +4. **Documentation**: These rules are related to code documentation. +5. **Error Prone**: Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors. +6. **Multithreading**: These are rules that flag issues when dealing with multiple threads of execution. +7. **Performance**: Rules that flag suboptimal code. +8. **Security**: Rules that flag potential security flaws. + +These categories help you to find rules and figure out the relevance and impact for your project. + +All rules have been moved accordingly, e.g. the rule "JumbledIncrementer", which was previously defined in the +ruleset "java-basic" has now been moved to the "Error Prone" category. The new rule reference to be used is +``. + +The old rulesets like "java-basic" are still kept for backwards-compatibility but will be removed eventually. +The rule reference documentation has been updated to reflect these changes. + +#### New Rules + +* The new Java rule `NcssCount` (category `design`) replaces the three rules "NcssConstructorCount", "NcssMethodCount", + and "NcssTypeCount". The new rule uses the metrics framework to achieve the same. It has two properties, to + define the report level for method and class sizes separately. Constructors and methods are considered the same. + +* The new Java rule `DoNotExtendJavaLangThrowable` (category `errorprone`) is a companion for the + `java-strictexception.xml/DoNotExtendJavaLangError`, detecting direct extensions of `java.lang.Throwable`. + +* The new Java rule `ForLoopCanBeForeach` (category `errorprone`) helps to identify those for-loops that can + be safely refactored into for-each-loops available since java 1.5. + +* The new Java rule `AvoidFileStream` (category `performance`) helps to identify code relying on `FileInputStream` / `FileOutputStream` + which, by using a finalizer, produces extra / unnecessary overhead to garbage collection, and should be replaced with + `Files.newInputStream` / `Files.newOutputStream` available since java 1.7. + +* The new Java rule `DataClass` (category `design`) detects simple data-holders without behaviour. This might indicate + that the behaviour is scattered elsewhere and the data class exposes the internal data structure, + which breaks encapsulation. + +* The new Apex rule `AvoidDirectAccessTriggerMap` (category `errorprone`) helps to identify direct array access to triggers, + which can produce bugs by either accessing non-existing indexes, or leaving them out. You should use for-each-loops + instead. + +* The new Apex rule `AvoidHardcodingId` (category `errorprone`) detects hardcoded strings that look like identifiers + and flags them. Record IDs change between environments, meaning hardcoded ids are bound to fail under a different + setup. + +* The new Apex rule `CyclomaticComplexity` (category `design`) detects overly complex classes and methods. The + report threshold can be configured separately for classes and methods. + +* A whole bunch of new rules has been added to Apex. They all fit into the category `errorprone`. + The 5 rules are migrated for Apex from the equivalent Java rules and include: + * `EmptyCatchBlock` to detect catch blocks completely ignoring exceptions. + * `EmptyIfStmt` for if blocks with no content, that can be safely removed. + * `EmptyTryOrFinallyBlock` for empty try / finally blocks that can be safely removed. + * `EmptyWhileStmt` for empty while loops that can be safely removed. + * `EmptyStatementBlock` for empty code blocks that can be safely removed. + +* The new Apex rule `AvoidSoslInLoops` (category `performance`) is the companion of the old + `AvoidSoqlInLoops` rule, flagging SOSL (Salesforce Object Search Language) queries when within + loops, to avoid governor issues, and hitting the database too often. + +#### Modified Rules + +* The Java rule `UnnecessaryFinalModifier` (category `codestyle`, former ruleset `java-unnecessarycode`) + has been merged into the rule `UnnecessaryModifier`. As part of this, the rule has been revamped to detect more cases. + It will now flag anonymous class' methods marked as final (can't be overridden, so it's pointless), along with + final methods overridden / defined within enum instances. It will also flag `final` modifiers on try-with-resources. + +* The Java rule `UnnecessaryParentheses` (category `codestyle`, former ruleset `java-controversial`) + has been merged into `UselessParentheses` (category `codestyle`, former ruleset `java-unnecessary`). + The rule covers all scenarios previously covered by either rule. + +* The Java rule `UncommentedEmptyConstructor` (category `documentation`, former ruleset `java-design`) + will now ignore empty constructors annotated with `javax.inject.Inject`. + +* The Java rule `AbstractClassWithoutAnyMethod` (category `bestpractices`, former ruleset `java-design`) + will now ignore classes annotated with `com.google.auto.value.AutoValue`. + +* The Java rule `GodClass` (category `design', former ruleset `java-design`) has been revamped to use + the new metrics framework. + +* The Java rule `LooseCoupling` (category `bestpractices`, former ruleset `java-coupling`) has been + replaced by the typeresolution-based implementation. + +* The Java rule `CloneMethodMustImplementCloneable` (category `errorprone`, former ruleset `java-clone`) + has been replaced by the typeresolution-based + implementation and is now able to detect cases if a class implements or extends a Cloneable class/interface. + +* The Java rule `UnusedImports` (category `bestpractices`, former ruleset `java-imports`) has been + replaced by the typeresolution-based + implementation and is now able to detect unused on-demand imports. + +* The Java rule `SignatureDeclareThrowsException` (category `design`, former ruleset 'java-strictexception') + has been replaced by the + typeresolution-based implementation. It has a new property `IgnoreJUnitCompletely`, which allows all + methods in a JUnit testcase to throw exceptions. + +* The Java rule `NPathComplexity` (category `design`, former ruleset `java-codesize`) has been revamped + to use the new metrics framework. + Its report threshold can be configured via the property `reportLevel`, which replaces the now + deprecated property `minimum`. + +* The Java rule `CyclomaticComplexity` (category `design`, former ruleset `java-codesize`) has been + revamped to use the new metrics framework. + Its report threshold can be configured via the properties `classReportLevel` and `methodReportLevel` separately. + The old property `reportLevel`, which configured the level for both total class and method complexity, + is deprecated. + +* The Java rule `CommentRequired` (category `documentation`, former ruleset `java-comments`) + has been revamped to include 2 new properties: + * `accessorCommentRequirement` to specify documentation requirements for getters and setters (default to `ignored`) + * `methodWithOverrideCommentRequirement` to specify documentation requirements for methods annotated with `@Override` (default to `ignored`) + +* The Java rule `EmptyCatchBlock` (category `errorprone`, former ruleset `java-empty`) has been changed to ignore + exceptions named `ignore` or `expected` by default. You can still override this behaviour by setting the `allowExceptionNameRegex` property. + +* The Java rule `OptimizableToArrayCall` (category `performance`, former ruleset `design`) has been + modified to fit for the current JVM implementations: It basically detects now the opposite and suggests to + use `Collection.toArray(new E[0])` with a zero-sized array. + See [Arrays of Wisdom of the Ancients](https://shipilev.net/blog/2016/arrays-wisdom-ancients/). + +#### Deprecated Rules + +* The Java rules `NcssConstructorCount`, `NcssMethodCount`, and `NcssTypeCount` (ruleset `java-codesize`) have been + deprecated. They will be replaced by the new rule `NcssCount` in the category `design`. + +* The Java rule `LooseCoupling` in ruleset `java-typeresolution` is deprecated. Use the rule with the same name + from category `bestpractices` instead. + +* The Java rule `CloneMethodMustImplementCloneable` in ruleset `java-typeresolution` is deprecated. Use the rule with + the same name from category `errorprone` instead. + +* The Java rule `UnusedImports` in ruleset `java-typeresolution` is deprecated. Use the rule with + the same name from category `bestpractices` instead. + +* The Java rule `SignatureDeclareThrowsException` in ruleset `java-typeresolution` is deprecated. Use the rule + with the same name from category `design` instead. + +* The Java rule `EmptyStaticInitializer` in ruleset `java-empty` is deprecated. Use the rule `EmptyInitializer` + from the category `errorprone`, which covers both static and non-static empty initializers.` + +* The Java rules `GuardDebugLogging` (ruleset `java-logging-jakarta-commons`) and `GuardLogStatementJavaUtil` + (ruleset `java-logging-java`) have been deprecated. Use the rule `GuardLogStatement` from the + category `bestpractices`, which covers all cases regardless of the logging framework. + +#### Removed Rules + +* The deprecated Java rule `UseSingleton` has been removed from the ruleset `java-design`. The rule has been renamed + long time ago to `UseUtilityClass` (category `design`). + +#### Java Symbol Table + +A [bug in symbol table](https://github.com/pmd/pmd/pull/549/commits/0958621ca884a8002012fc7738308c8dfc24b97c) prevented +the symbol table analysis to properly match primitive arrays types. The issue [affected the `java-unsedcode/UnusedPrivateMethod`](https://github.com/pmd/pmd/issues/521) +rule, but other rules may now produce improved results as consequence of this fix. + +#### Apex Parser Update + +The Apex parser version was bumped, from `1.0-sfdc-187` to `210-SNAPSHOT`. This update let us take full advantage +of the latest improvements from Salesforce, but introduces some breaking changes: + +* `BlockStatements` are now created for all control structures, even if no brace is used. We have therefore added + a `hasCurlyBrace` method to differentiate between both scenarios. +* New AST node types are available. In particular `CastExpression`, `ConstructorPreamble`, `IllegalStoreExpression`, + `MethodBlockStatement`, `Modifier`, `MultiStatement`, `NestedExpression`, `NestedStoreExpression`, + `NewKeyValueObjectExpression` and `StatementExecuted` +* Some nodes have been removed. Such is the case of `TestNode`, `DottedExpression` and `NewNameValueObjectExpression` + (replaced by `NewKeyValueObjectExpression`) + +All existing rules have been updated to reflect these changes. If you have custom rules, be sure to update them. + +For more info about the included Apex parser, see the new pmd module "pmd-apex-jorje", which packages and provides +the parser as a binary. + +#### Incremental Analysis + +The incremental analysis feature first introduced in PMD 5.6.0 has been enhanced. A few minor issues have been fixed, +and several improvements have been performed to make it more accurate. + +The cache will now detect changes to the JARs referenced in the `auxclasspath` instead of simply looking at their paths +and order. This means that if you are referencing a JAR you are overwriting in some way, the incremental analysis can +now detect it and invalidate it's cache to avoid false reports. + +Similarly, any changes to the execution classpath of PMD will invalidate the cache. This means that if you have custom +rules packaged in a jar, any changes to it will invalidate the cache automatically. + +We have also improved logging on the analysis code, allowing better insight into how the cache is performing, +under debug / verbose builds you can even see individual hits / misses to the cache (and the reason for any miss!) + +Finally, as this feature keeps maturing, we are gently pushing this forward. If not using incremental analysis, +a warning will now be produced suggesting users to adopt it for better performance. + +#### Rule and Report Properties + +The implementation around the properties support for rule properties and report properties has been revamped +to be fully typesafe. Along with that change, the support classes have been moved into an own +package `net.sourceforge.pmd.properties`. While there is no change necessary in the ruleset XML files, +when using/setting values for rules, there are adjustments necessary when declaring properties in Java-implemented +rules. + +Rule properties can be declared both for Java based rules and XPath rules. +This is now very well documented in [Working with properties](pmd_devdocs_working_with_properties.html). + +With PMD 6.0.0, multivalued properties are now also possible with XPath rules. + +### Fixed Issues + +* all + * [#394](https://github.com/pmd/pmd/issues/394): \[core] PMD exclude rules are failing with IllegalArgumentException with non-default minimumPriority + * [#532](https://github.com/pmd/pmd/issues/532): \[core] security concerns on URL-based rulesets + * [#538](https://github.com/pmd/pmd/issues/538): \[core] Provide an XML Schema for XML reports + * [#600](https://github.com/pmd/pmd/issues/600): \[core] Nullpointer while creating cache File + * [#604](https://github.com/pmd/pmd/issues/604): \[core] Incremental analysis should detect changes to jars in classpath + * [#608](https://github.com/pmd/pmd/issues/608): \[core] Add DEBUG log when applying incremental analysis + * [#618](https://github.com/pmd/pmd/issues/618): \[core] Incremental Analysis doesn't close file correctly on Windows upon a cache hit + * [#643](https://github.com/pmd/pmd/issues/643): \[core] PMD Properties (dev-properties) breaks markup on CodeClimateRenderer + * [#680](https://github.com/pmd/pmd/pull/680): \[core] Isolate classloaders for runtime and auxclasspath + * [#762](https://github.com/pmd/pmd/issues/762): \[core] Remove method and file property from available property descriptors for XPath rules + * [#763](https://github.com/pmd/pmd/issues/763): \[core] Turn property descriptor util into an enum and enrich its interface +* apex + * [#265](https://github.com/pmd/pmd/issues/265): \[apex] Make Rule suppression work + * [#488](https://github.com/pmd/pmd/pull/488): \[apex] Use Apex lexer for CPD + * [#489](https://github.com/pmd/pmd/pull/489): \[apex] Update Apex compiler + * [#500](https://github.com/pmd/pmd/issues/500): \[apex] Running through CLI shows jorje optimization messages + * [#605](https://github.com/pmd/pmd/issues/605): \[apex] java.lang.NoClassDefFoundError in the latest build + * [#637](https://github.com/pmd/pmd/issues/637): \[apex] Avoid SOSL in loops + * [#760](https://github.com/pmd/pmd/issues/760): \[apex] EmptyStatementBlock complains about missing rather than empty block + * [#766](https://github.com/pmd/pmd/issues/766): \[apex] Replace old Jorje parser with new one + * [#768](https://github.com/pmd/pmd/issues/768): \[apex] java.lang.NullPointerException from PMD +* cpp + * [#448](https://github.com/pmd/pmd/issues/448): \[cpp] Write custom CharStream to handle continuation characters +* java + * [#1454](https://sourceforge.net/p/pmd/bugs/1454/): \[java] OptimizableToArrayCall is outdated and invalid in current JVMs + * [#1513](https://sourceforge.net/p/pmd/bugs/1513/): \[java] Remove deprecated rule UseSingleton + * [#328](https://github.com/pmd/pmd/issues/328): \[java] java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/jsp/PageContext + * [#487](https://github.com/pmd/pmd/pull/487): \[java] Fix typeresolution for anonymous extending object + * [#496](https://github.com/pmd/pmd/issues/496): \[java] processing error on generics inherited from enclosing class + * [#510](https://github.com/pmd/pmd/issues/510): \[java] Typeresolution fails on a simple primary when the source is loaded from a class literal + * [#527](https://github.com/pmd/pmd/issues/527): \[java] Lombok getter annotation on enum is not recognized correctly + * [#534](https://github.com/pmd/pmd/issues/534): \[java] NPE in MethodTypeResolution for static methods + * [#603](https://github.com/pmd/pmd/issues/603): \[core] incremental analysis should invalidate upon Java rule plugin changes + * [#650](https://github.com/pmd/pmd/issues/650): \[java] ProcesingError analyzing code under 5.8.1 + * [#732](https://github.com/pmd/pmd/issues/732): \[java] LinkageError with aux classpath +* java-basic + * [#565](https://github.com/pmd/pmd/pull/565): \[java] False negative on DontCallThreadRun when extending Thread +* java-comments + * [#396](https://github.com/pmd/pmd/issues/396): \[java] CommentRequired: add properties to ignore @Override method and getters / setters + * [#536](https://github.com/pmd/pmd/issues/536): \[java] CommentDefaultAccessModifierRule ignores constructors +* java-controversial + * [#388](https://github.com/pmd/pmd/issues/388): \[java] controversial.AvoidLiteralsInIfCondition 0.0 false positive + * [#408](https://github.com/pmd/pmd/issues/408): \[java] DFA not analyzing asserts + * [#537](https://github.com/pmd/pmd/issues/537): \[java] UnnecessaryParentheses fails to detect obvious scenario +* java-design + * [#357](https://github.com/pmd/pmd/issues/357): \[java] UncommentedEmptyConstructor consider annotations on Constructor + * [#438](https://github.com/pmd/pmd/issues/438): \[java] Relax AbstractClassWithoutAnyMethod when class is annotated by @AutoValue + * [#590](https://github.com/pmd/pmd/issues/590): \[java] False positive on MissingStaticMethodInNonInstantiatableClass +* java-logging + * [#457](https://github.com/pmd/pmd/issues/457): \[java] Merge all log guarding rules + * [#721](https://github.com/pmd/pmd/issues/721): \[java] NPE in PMD 5.8.1 InvalidSlf4jMessageFormat +* java-sunsecure + * [#468](https://github.com/pmd/pmd/issues/468): \[java] ArrayIsStoredDirectly false positive +* java-unusedcode + * [#521](https://github.com/pmd/pmd/issues/521): \[java] UnusedPrivateMethod returns false positives with primitive data type in map argument +* java-unnecessarycode + * [#412](https://github.com/pmd/pmd/issues/412): \[java] java-unnecessarycode/UnnecessaryFinalModifier missing cases + * [#676](https://github.com/pmd/pmd/issues/676): \[java] java-unnecessarycode/UnnecessaryFinalModifier on try-with-resources + +### API Changes + +* The class `net.sourceforge.pmd.lang.dfa.NodeType` has been converted to an enum. + All node types are enum members now instead of int constants. The names for node types are retained. + +* The *Properties API* (rule and report properties) has been revamped to be fully typesafe. This is everything + around `net.sourceforge.pmd.properties.PropertyDescriptor`. + + Note: All classes related to properties have been moved into the package `net.sourceforge.pmd.properties`. + +* The rule classes `net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestClassShouldHaveAsserts` + and `net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestShouldNotUseSeeAllDataTrue` have been + renamed to `ApexUnitTestClassShouldHaveAssertsRule` and `ApexUnitTestShouldNotUseSeeAllDataTrueRule`, + respectively. This is to comply with the naming convention, that each rule class should be suffixed with "Rule". + + This change has no impact on custom rulesets, since the rule names themselves didn't change. + +* The never implemented method `PMD.processFiles(PMDConfiguration, RuleSetFactory, Collection, RuleContext, ProgressMonitor)` along with the interface `ProgressMonitor` has been removed. + +* The method `PMD.setupReport(RuleSets, RuleContext, String)` is gone. It was used to report dysfunctional + rules. But PMD does this now automatically before processing the files, so there is no need for this + method anymore. + +* All APIs deprecated in older versions are now removed. This includes: + * `Renderer.getPropertyDefinitions` + * `AbstractRenderer.defineProperty(String, String)` + * `AbstractRenderer.propertyDefinitions` + * `ReportListener` + * `Report.addListener(ReportListener)` + * `SynchronizedReportListener` + * `CPDConfiguration.CPDConfiguration(int, Language, String)` + * `CPDConfiguration.getRendererFromString(String)` + * `StreamUtil` + * `StringUtil.appendXmlEscaped(StringBuilder, String)` + * `StringUtil.htmlEncode(String)` + + +* Several methods in `net.sourceforge.pmd.util.CollectionUtil` have been deprecated and will be removed in PMD 7.0.0. In particular: + * `CollectionUtil.addWithoutDuplicates(T[], T)` + * `CollectionUtil.addWithoutDuplicates(T[], T[])` + * `CollectionUtil.areSemanticEquals(T[], T[])` + * `CollectionUtil.areEqual(Object, Object)` + * `CollectionUtil.arraysAreEqual(Object, Object)` + * `CollectionUtil.valuesAreTransitivelyEqual(Object[], Object[])` + + +* Several methods in `net.sourceforge.pmd.util.StringUtil` have been deprecated and will be removed in PMD 7.0.0. In particular: + * `StringUtil.startsWithAny(String, String[])` + * `StringUtil.isNotEmpty(String)` + * `StringUtil.isEmpty(String)` + * `StringUtil.isMissing(String)` + * `StringUtil.areSemanticEquals(String, String)` + * `StringUtil.replaceString(String, String, String)` + * `StringUtil.replaceString(String, char, String)` + * `StringUtil.substringsOf(String, char)` + * `StringUtil.substringsOf(String, String)` + * `StringUtil.asStringOn(StringBuffer, Iterator, String)` + * `StringUtil.asStringOn(StringBuilder, Object[], String)` + * `StringUtil.lpad(String, int)` + +* The class `net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition` is now abstract, and has been enhanced + to provide several new methods. + +* The constructor of `net.sourceforge.pmd.RuleSetFactory`, which took a `ClassLoader` is deprecated. + Please use the alternative constructor with the `net.sourceforge.pmd.util.ResourceLoader` instead. + +* The following GUI related classes have been deprecated and will be removed in PMD 7.0.0. + The tool "bgastviewer", that could be started via the script `bgastviewer.bat` or `run.sh bgastviewer` is + deprecated, too, and will be removed in PMD 7.0.0. + Both the "old designer" and "bgastviewer" are replaced by the [New Rule Designer](#new-rule-designer). + * `net.sourceforge.pmd.util.designer.CodeEditorTextPane` + * `net.sourceforge.pmd.util.designer.CreateXMLRulePanel` + * `net.sourceforge.pmd.util.designer.Designer` + * `net.sourceforge.pmd.util.designer.DFAPanel` + * `net.sourceforge.pmd.util.designer.LineGetter` + * `net.sourceforge.pmd.util.viewer.Viewer` + * `net.sourceforge.pmd.util.viewer.gui.ActionCommands` + * `net.sourceforge.pmd.util.viewer.gui.ASTPanel` + * `net.sourceforge.pmd.util.viewer.gui.EvaluationResultsPanel` + * `net.sourceforge.pmd.util.viewer.gui.MainFrame` + * `net.sourceforge.pmd.util.viewer.gui.ParseExceptionHandler` + * `net.sourceforge.pmd.util.viewer.gui.SourceCodePanel` + * `net.sourceforge.pmd.util.viewer.gui.XPathPanel` + * `net.sourceforge.pmd.util.viewer.gui.menu.ASTNodePopupMenu` + * `net.sourceforge.pmd.util.viewer.gui.menu.AttributesSubMenu` + * `net.sourceforge.pmd.util.viewer.gui.menu.SimpleNodeSubMenu` + * `net.sourceforge.pmd.util.viewer.gui.menu.XPathFragmentAddingItem` + * `net.sourceforge.pmd.util.viewer.model.ASTModel` + * `net.sourceforge.pmd.util.viewer.model.AttributeToolkit` + * `net.sourceforge.pmd.util.viewer.model.SimpleNodeTreeNodeAdapter` + * `net.sourceforge.pmd.util.viewer.model.ViewerModel` + * `net.sourceforge.pmd.util.viewer.model.ViewerModelEvent` + * `net.sourceforge.pmd.util.viewer.model.ViewerModelListener` + * `net.sourceforge.pmd.util.viewer.util.NLS` + +* The following methods in `net.sourceforge.pmd.Rule` have been deprecated and will be removed in PMD 7.0.0. + All methods are replaced by their bean-like counterparts + * `void setUsesDFA()`. Use `void setDfa(boolean)` instead. + * `boolean usesDFA()`. Use `boolean isDfa()` instead. + * `void setUsesTypeResolution()`. Use `void setTypeResolution(boolean)` instead. + * `boolean usesTypeResolution()`. Use `boolean isTypeResolution()` instead. + * `void setUsesMultifile()`. Use `void setMultifile(boolean)` instead. + * `boolean usesMultifile()`. Use `boolean isMultifile()` instead. + * `boolean usesRuleChain()`. Use `boolean isRuleChain()` instead. + +### External Contributions + +* [#287](https://github.com/pmd/pmd/pull/287): \[apex] Make Rule suppression work - [Robert Sösemann](https://github.com/up2go-rsoesemann) +* [#420](https://github.com/pmd/pmd/pull/420): \[java] Fix UR anomaly in assert statements - [Clément Fournier](https://github.com/oowekyala) +* [#482](https://github.com/pmd/pmd/pull/482): \[java] Metrics testing framework + improved capabilities for metrics - [Clément Fournier](https://github.com/oowekyala) +* [#484](https://github.com/pmd/pmd/pull/484): \[core] Changed linux usage to a more unix like path - [patriksevallius](https://github.com/patriksevallius) +* [#486](https://github.com/pmd/pmd/pull/486): \[java] Add basic method typeresolution - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#492](https://github.com/pmd/pmd/pull/492): \[java] Typeresolution for overloaded methods - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#495](https://github.com/pmd/pmd/pull/495): \[core] Custom rule reinitialization code - [Clément Fournier](https://github.com/oowekyala) +* [#479](https://github.com/pmd/pmd/pull/479): \[core] Typesafe and immutable properties - [Clément Fournier](https://github.com/oowekyala) +* [#499](https://github.com/pmd/pmd/pull/499): \[java] Metrics memoization tests - [Clément Fournier](https://github.com/oowekyala) +* [#501](https://github.com/pmd/pmd/pull/501): \[java] Add support for most specific vararg method type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#502](https://github.com/pmd/pmd/pull/502): \[java] Add support for static field type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#505](https://github.com/pmd/pmd/pull/505): \[java] Followup on metrics - [Clément Fournier](https://github.com/oowekyala) +* [#506](https://github.com/pmd/pmd/pull/506): \[java] Add reduction rules to type inference - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#511](https://github.com/pmd/pmd/pull/511): \[core] Prepare abstraction of the metrics framework - [Clément Fournier](https://github.com/oowekyala) +* [#512](https://github.com/pmd/pmd/pull/512): \[java] Add incorporation to type inference - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#513](https://github.com/pmd/pmd/pull/513): \[java] Fix for maximally specific method selection - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#514](https://github.com/pmd/pmd/pull/514): \[java] Add static method type resolution - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#517](https://github.com/pmd/pmd/pull/517): \[doc] Metrics documentation - [Clément Fournier](https://github.com/oowekyala) +* [#518](https://github.com/pmd/pmd/pull/518): \[core] Properties refactoring: factorized enumerated property - [Clément Fournier](https://github.com/oowekyala) +* [#523](https://github.com/pmd/pmd/pull/523): \[java] Npath complexity metric and rule - [Clément Fournier](https://github.com/oowekyala) +* [#524](https://github.com/pmd/pmd/pull/524): \[java] Add support for explicit type arguments with method invocation - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#525](https://github.com/pmd/pmd/pull/525): \[core] Fix line ending and not ignored files issues - [Matias Comercio](https://github.com/MatiasComercio) +* [#528](https://github.com/pmd/pmd/pull/528): \[core] Fix typo - [Ayoub Kaanich](https://github.com/kayoub5) +* [#529](https://github.com/pmd/pmd/pull/529): \[java] Abstracted the Java metrics framework - [Clément Fournier](https://github.com/oowekyala) +* [#530](https://github.com/pmd/pmd/pull/530): \[java] Fix issue #527: Lombok getter annotation on enum is not recognized correctly - [Clément Fournier](https://github.com/oowekyala) +* [#533](https://github.com/pmd/pmd/pull/533): \[core] improve error message - [Dennis Kieselhorst](https://github.com/deki) +* [#535](https://github.com/pmd/pmd/pull/535): \[apex] Fix broken Apex visitor adapter - [Clément Fournier](https://github.com/oowekyala) +* [#542](https://github.com/pmd/pmd/pull/542): \[java] Metrics abstraction - [Clément Fournier](https://github.com/oowekyala) +* [#545](https://github.com/pmd/pmd/pull/545): \[apex] Apex metrics framework - [Clément Fournier](https://github.com/oowekyala) +* [#548](https://github.com/pmd/pmd/pull/548): \[java] Metrics documentation - [Clément Fournier](https://github.com/oowekyala) +* [#550](https://github.com/pmd/pmd/pull/550): \[java] Add basic resolution to type inference - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#553](https://github.com/pmd/pmd/pull/553): \[java] Refactored ParserTst into a static utility class + add getSourceFromClass - [Clément Fournier](https://github.com/oowekyala) +* [#554](https://github.com/pmd/pmd/pull/554): \[java] Fix #537: UnnecessaryParentheses fails to detect obvious scenario - [Clément Fournier](https://github.com/oowekyala) +* [#555](https://github.com/pmd/pmd/pull/555): \[java] Changed metrics/CyclomaticComplexityRule to use WMC when reporting classes - [Clément Fournier](https://github.com/oowekyala) +* [#556](https://github.com/pmd/pmd/pull/556): \[java] Fix #357: UncommentedEmptyConstructor consider annotations on Constructor - [Clément Fournier](https://github.com/oowekyala) +* [#557](https://github.com/pmd/pmd/pull/557): \[java] Fix NPath metric not counting ternaries correctly - [Clément Fournier](https://github.com/oowekyala) +* [#563](https://github.com/pmd/pmd/pull/563): \[java] Add support for basic method type inference for strict invocation - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#566](https://github.com/pmd/pmd/pull/566): \[java] New rule in migrating ruleset: ForLoopCanBeForeach - [Clément Fournier](https://github.com/oowekyala) +* [#567](https://github.com/pmd/pmd/pull/567): \[java] Last API change for metrics (metric options) - [Clément Fournier](https://github.com/oowekyala) +* [#570](https://github.com/pmd/pmd/pull/570): \[java] Model lower, upper and intersection types - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#573](https://github.com/pmd/pmd/pull/573): \[java] Data class rule - [Clément Fournier](https://github.com/oowekyala) +* [#576](https://github.com/pmd/pmd/pull/576): \[doc]\[java] Add hint for Guava users in InefficientEmptyStringCheck - [mmoehring](https://github.com/mmoehring) +* [#578](https://github.com/pmd/pmd/pull/578): \[java] Refactored god class rule - [Clément Fournier](https://github.com/oowekyala) +* [#579](https://github.com/pmd/pmd/pull/579): \[java] Update parsing to produce upper and lower bounds - [Bendegúz Nagy](https://github.com/WinterGrascph) +* [#580](https://github.com/pmd/pmd/pull/580): \[core] Add AbstractMetric to topple the class hierarchy of metrics - [Clément Fournier](https://github.com/oowekyala) +* [#581](https://github.com/pmd/pmd/pull/581): \[java] Relax AbstractClassWithoutAnyMethod when class is annotated by @AutoValue - [Niklas Baudy](https://github.com/vanniktech) +* [#583](https://github.com/pmd/pmd/pull/583): \[java] Documentation about writing metrics - [Clément Fournier](https://github.com/oowekyala) +* [#585](https://github.com/pmd/pmd/pull/585): \[java] Moved NcssCountRule to codesize.xml - [Clément Fournier](https://github.com/oowekyala) +* [#587](https://github.com/pmd/pmd/pull/587): \[core] Properties refactoring: Move static constants of ValueParser to class ValueParserConstants - [Clément Fournier](https://github.com/oowekyala) +* [#588](https://github.com/pmd/pmd/pull/588): \[java] XPath function to compute metrics - [Clément Fournier](https://github.com/oowekyala) +* [#598](https://github.com/pmd/pmd/pull/598): \[java] Fix #388: controversial.AvoidLiteralsInIfCondition 0.0 false positive - [Clément Fournier](https://github.com/oowekyala) +* [#602](https://github.com/pmd/pmd/pull/602): \[java] \[apex] Separate multifile analysis from metrics - [Clément Fournier](https://github.com/oowekyala) +* [#620](https://github.com/pmd/pmd/pull/620): \[core] Moved properties to n.s.pmd.properties - [Clément Fournier](https://github.com/oowekyala) +* [#625](https://github.com/pmd/pmd/pull/625): \[apex] empty code ruleset for apex - [Jan Aertgeerts](https://github.com/JAertgeerts) +* [#632](https://github.com/pmd/pmd/pull/632): \[apex] Add AvoidDirectAccessTriggerMap rule to the style set - [Jan Aertgeerts](https://github.com/JAertgeerts) +* [#644](https://github.com/pmd/pmd/pull/644): \[core] Prevent internal dev-properties from being displayed on CodeClimate renderer - [Filipe Esperandio](https://github.com/filipesperandio) +* [#660](https://github.com/pmd/pmd/pull/660): \[apex] avoid sosl in loops - [Jan Aertgeerts](https://github.com/JAertgeerts) +* [#661](https://github.com/pmd/pmd/pull/661): \[apex] avoid hardcoding id's - [Jan Aertgeerts](https://github.com/JAertgeerts) +* [#666](https://github.com/pmd/pmd/pull/666): \[java] Add DoNotExtendJavaLangThrowable rule - [Robert Painsi](https://github.com/robertpainsi) +* [#668](https://github.com/pmd/pmd/pull/668): \[core] Fix javadoc warnings on pmd-core - [Clément Fournier](https://github.com/oowekyala) +* [#669](https://github.com/pmd/pmd/pull/669): \[core] Builder pattern for properties - [Clément Fournier](https://github.com/oowekyala) +* [#675](https://github.com/pmd/pmd/pull/675): \[java] Fix in Java grammar: Try with final resource node error - [Gonzalo Ibars Ingman](https://github.com/gibarsin) +* [#679](https://github.com/pmd/pmd/pull/679): \[core] Token scheme generalization - [Gonzalo Ibars Ingman](https://github.com/gibarsin) +* [#694](https://github.com/pmd/pmd/pull/694): \[core] Add minor fixes to root pom - [Matias Comercio](https://github.com/MatiasComercio) +* [#696](https://github.com/pmd/pmd/pull/696): \[core] Add remove operation over nodes - [Matias Comercio](https://github.com/MatiasComercio) +* [#711](https://github.com/pmd/pmd/pull/711): \[ui] New rule designer - [Clément Fournier](https://github.com/oowekyala) +* [#722](https://github.com/pmd/pmd/pull/722): \[java] Move NPathComplexity from metrics to design - [Clément Fournier](https://github.com/oowekyala) +* [#723](https://github.com/pmd/pmd/pull/723): \[core] Rule factory refactoring - [Clément Fournier](https://github.com/oowekyala) +* [#726](https://github.com/pmd/pmd/pull/726): \[java] Fix issue #721 (NPE in InvalidSlf4jMessageFormat) - [Clément Fournier](https://github.com/oowekyala) +* [#727](https://github.com/pmd/pmd/pull/727): \[core] Fix #725: numeric property descriptors now check their default value - [Clément Fournier](https://github.com/oowekyala) +* [#733](https://github.com/pmd/pmd/pull/733): \[java] Some improvements to CommentRequired - [Clément Fournier](https://github.com/oowekyala) +* [#734](https://github.com/pmd/pmd/pull/734): \[java] Move CyclomaticComplexity from metrics to design - [Clément Fournier](https://github.com/oowekyala) +* [#736](https://github.com/pmd/pmd/pull/736): \[core] Make Saxon support multi valued XPath properties - [Clément Fournier](https://github.com/oowekyala) +* [#737](https://github.com/pmd/pmd/pull/737): \[doc] Fix NPathComplexity documentation bad rendering - [Clément Fournier](https://github.com/oowekyala) +* [#744](https://github.com/pmd/pmd/pull/744): \[doc] Added Apex to supported languages - [Michał Kuliński](https://github.com/coola) +* [#746](https://github.com/pmd/pmd/pull/746): \[doc] Fix typo in incremental analysis log message - [Clément Fournier](https://github.com/oowekyala) +* [#749](https://github.com/pmd/pmd/pull/749): \[doc] Update the documentation for properties - [Clément Fournier](https://github.com/oowekyala) +* [#758](https://github.com/pmd/pmd/pull/758): \[core] Expose the full mapping from property type id to property extractor - [Clément Fournier](https://github.com/oowekyala) +* [#764](https://github.com/pmd/pmd/pull/764): \[core] Prevent method and file property use in XPath rules - [Clément Fournier](https://github.com/oowekyala) +* [#771](https://github.com/pmd/pmd/pull/771): \[apex] Fix Apex metrics framework failing on triggers, refs #768 - [Clément Fournier](https://github.com/oowekyala) +* [#774](https://github.com/pmd/pmd/pull/774): \[java] Avoid using FileInput/Output - see JDK-8080225 - [Chas Honton](https://github.com/chonton) + ## 01-July-2017 - 5.8.1 From 05d29f4c9b7ea255eed121256a996649d01fbac6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 16:21:05 +0100 Subject: [PATCH 027/413] [doc] fix documentation of rule pom/ProjectVersionAsDependencyVersion --- docs/pages/pmd/rules/pom/errorprone.md | 2 +- pmd-xml/src/main/resources/category/pom/errorprone.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/pmd/rules/pom/errorprone.md b/docs/pages/pmd/rules/pom/errorprone.md index 3315e4f3b7..d32917742d 100644 --- a/docs/pages/pmd/rules/pom/errorprone.md +++ b/docs/pages/pmd/rules/pom/errorprone.md @@ -53,7 +53,7 @@ The following types are considered valid: pom, jar, maven-plugin, ejb, war, ear, **Priority:** Medium (3) Using that expression in dependency declarations seems like a shortcut, but it can go wrong. -By far the most common problem is the use of 6.1.0-SNAPSHOT in a BOM or parent POM. +By far the most common problem is the use of ${project.version} in a BOM or parent POM. ``` //dependency/version/text[contains(@Image,'{project.version}')] diff --git a/pmd-xml/src/main/resources/category/pom/errorprone.xml b/pmd-xml/src/main/resources/category/pom/errorprone.xml index c05f70b96b..1d238a882b 100644 --- a/pmd-xml/src/main/resources/category/pom/errorprone.xml +++ b/pmd-xml/src/main/resources/category/pom/errorprone.xml @@ -62,7 +62,7 @@ The following types are considered valid: pom, jar, maven-plugin, ejb, war, ear, externalInfoUrl="${pmd.website.baseurl}/pmd_rules_pom_errorprone.html#projectversionasdependencyversion"> Using that expression in dependency declarations seems like a shortcut, but it can go wrong. -By far the most common problem is the use of ${project.version} in a BOM or parent POM. +By far the most common problem is the use of ${project.version} in a BOM or parent POM. 3 From 342b5c3c1d75b85bd8f9aee7c145cdbda283fa1d Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 16:44:52 +0100 Subject: [PATCH 028/413] [ci] fix uploading doc to pmd.github.io during release --- .travis.yml | 2 +- .travis/release.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 627df0e99c..18ce1b3815 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,7 @@ deploy: file_glob: true file: - pmd-dist/target/pmd-*.zip - - target/pmd-doc-*.zip + - docs/pmd-doc-*.zip skip_cleanup: true on: tags: true diff --git a/.travis/release.sh b/.travis/release.sh index bede0b29ea..f33d1cceaf 100755 --- a/.travis/release.sh +++ b/.travis/release.sh @@ -46,9 +46,9 @@ fi fi -if [ "${BUILD}" = "site" ]; then +if [ "${BUILD}" = "doc" ]; then -echo "Adding the site to pmd.github.io..." +echo "Adding the new doc to pmd.github.io..." # clone pmd.github.io. Note: This uses the ssh key setup earlier # In order to speed things up, we use a sparse checkout - no need to checkout all directories here mkdir pmd.github.io @@ -61,7 +61,7 @@ mkdir pmd.github.io git remote add origin git@github.com:pmd/pmd.github.io.git echo "latest/" > .git/info/sparse-checkout git pull --depth=1 origin master - rsync -a ../target/pmd-doc-${RELEASE_VERSION}/ pmd-${RELEASE_VERSION}/ + rsync -a ../docs/pmd-doc-${RELEASE_VERSION}/ pmd-${RELEASE_VERSION}/ git add pmd-${RELEASE_VERSION} git commit -q -m "Added pmd-${RELEASE_VERSION}" git rm -qr latest From 1e256119912991f0b2817bfbfe1605e2021eeb27 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 16:50:11 +0100 Subject: [PATCH 029/413] [doc] next version --- docs/_config.yml | 4 ++-- docs/pages/pmd/devdocs/releasing.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index a30a3f2dc6..9b42857d75 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,8 +1,8 @@ repository: pmd/pmd pmd: - version: 6.0.0 - date: 2017-12-15 + version: 6.1.0 + date: 2018-01-26 output: web # this property is useful for conditional filtering of content that is separate from the PDF. diff --git a/docs/pages/pmd/devdocs/releasing.md b/docs/pages/pmd/devdocs/releasing.md index f823bb50f4..135a64b552 100644 --- a/docs/pages/pmd/devdocs/releasing.md +++ b/docs/pages/pmd/devdocs/releasing.md @@ -136,6 +136,7 @@ the following template: ### Prepare the new release notes +* Update version in **docs/_config.yml** * Move version/release info from **docs/pages/release_notes.md** to **docs/pages/release_notes_old.md**. * Update version/release info in **docs/pages/release_notes.md**. Use the following template: From b3161639f895e2b3442cc949ae2b147cdf6f8071 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Dec 2017 18:26:26 +0100 Subject: [PATCH 030/413] [doc] Update Ruby/Jekyll/Gems --- docs/Dockerfile | 2 +- docs/Gemfile.lock | 107 +++++++++++++++++++++++++++------------------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index b1fa52c473..04f57ec3e0 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.1 +FROM ruby:2.4.2 MAINTAINER mrafayaleem@gmail.com RUN apt-get clean \ diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 994b58d435..86f9ec22e5 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.2.8) + activesupport (4.2.9) i18n (~> 0.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) @@ -11,9 +11,12 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.12.2) + coffee-script-source (1.11.1) colorator (1.1.0) - ethon (0.10.1) + commonmarker (0.17.7.1) + ruby-enum (~> 0.5) + concurrent-ruby (1.0.5) + ethon (0.11.0) ffi (>= 1.3.0) execjs (2.7.0) faraday (0.13.1) @@ -21,25 +24,27 @@ GEM ffi (1.9.18) forwardable-extended (2.6.0) gemoji (3.0.0) - github-pages (158) - activesupport (= 4.2.8) + github-pages (172) + activesupport (= 4.2.9) github-pages-health-check (= 1.3.5) - jekyll (= 3.5.2) - jekyll-avatar (= 0.4.2) - jekyll-coffeescript (= 1.0.1) + jekyll (= 3.6.2) + jekyll-avatar (= 0.5.0) + jekyll-coffeescript (= 1.0.2) + jekyll-commonmark-ghpages (= 0.1.3) jekyll-default-layout (= 0.1.4) jekyll-feed (= 0.9.2) jekyll-gist (= 1.4.1) - jekyll-github-metadata (= 2.9.1) + jekyll-github-metadata (= 2.9.3) jekyll-mentions (= 1.2.0) - jekyll-optional-front-matter (= 0.2.0) + jekyll-optional-front-matter (= 0.3.0) jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.1.0) + jekyll-readme-index (= 0.2.0) jekyll-redirect-from (= 0.12.1) - jekyll-relative-links (= 0.4.1) + jekyll-relative-links (= 0.5.2) + jekyll-remote-theme (= 0.2.3) jekyll-sass-converter (= 1.5.0) jekyll-seo-tag (= 2.3.0) - jekyll-sitemap (= 1.0.0) + jekyll-sitemap (= 1.1.1) jekyll-swiss (= 0.4.0) jekyll-theme-architect (= 0.1.0) jekyll-theme-cayman (= 0.1.0) @@ -54,14 +59,14 @@ GEM jekyll-theme-slate (= 0.1.0) jekyll-theme-tactile (= 0.1.0) jekyll-theme-time-machine (= 0.1.0) - jekyll-titles-from-headings (= 0.4.0) - jemoji (= 0.8.0) - kramdown (= 1.13.2) + jekyll-titles-from-headings (= 0.5.0) + jemoji (= 0.8.1) + kramdown (= 1.14.0) liquid (= 4.0.0) listen (= 3.0.6) mercenary (~> 0.3) minima (= 2.1.1) - rouge (= 1.11.1) + rouge (= 2.2.1) terminal-table (~> 1.4) github-pages-health-check (1.3.5) addressable (~> 2.3) @@ -69,52 +74,65 @@ GEM octokit (~> 4.0) public_suffix (~> 2.0) typhoeus (~> 0.7) - html-pipeline (2.7.0) + html-pipeline (2.7.1) activesupport (>= 2) nokogiri (>= 1.4) - i18n (0.8.6) - jekyll (3.5.2) + i18n (0.9.1) + concurrent-ruby (~> 1.0) + jekyll (3.6.2) addressable (~> 2.4) colorator (~> 1.0) jekyll-sass-converter (~> 1.0) jekyll-watch (~> 1.1) - kramdown (~> 1.3) + kramdown (~> 1.14) liquid (~> 4.0) mercenary (~> 0.3.3) pathutil (~> 0.9) - rouge (~> 1.7) + rouge (>= 1.7, < 3) safe_yaml (~> 1.0) - jekyll-avatar (0.4.2) + jekyll-avatar (0.5.0) jekyll (~> 3.0) - jekyll-coffeescript (1.0.1) + jekyll-coffeescript (1.0.2) coffee-script (~> 2.2) + coffee-script-source (~> 1.11.1) + jekyll-commonmark (1.1.0) + commonmarker (~> 0.14) + jekyll (>= 3.0, < 4.0) + jekyll-commonmark-ghpages (0.1.3) + commonmarker (~> 0.17.6) + jekyll-commonmark (~> 1) + rouge (~> 2) jekyll-default-layout (0.1.4) jekyll (~> 3.0) jekyll-feed (0.9.2) jekyll (~> 3.3) jekyll-gist (1.4.1) octokit (~> 4.2) - jekyll-github-metadata (2.9.1) + jekyll-github-metadata (2.9.3) jekyll (~> 3.1) octokit (~> 4.0, != 4.4.0) jekyll-mentions (1.2.0) activesupport (~> 4.0) html-pipeline (~> 2.3) jekyll (~> 3.0) - jekyll-optional-front-matter (0.2.0) + jekyll-optional-front-matter (0.3.0) jekyll (~> 3.0) jekyll-paginate (1.1.0) - jekyll-readme-index (0.1.0) + jekyll-readme-index (0.2.0) jekyll (~> 3.0) jekyll-redirect-from (0.12.1) jekyll (~> 3.3) - jekyll-relative-links (0.4.1) + jekyll-relative-links (0.5.2) jekyll (~> 3.3) + jekyll-remote-theme (0.2.3) + jekyll (~> 3.5) + rubyzip (>= 1.2.1, < 3.0) + typhoeus (>= 0.7, < 2.0) jekyll-sass-converter (1.5.0) sass (~> 3.4) jekyll-seo-tag (2.3.0) jekyll (~> 3.3) - jekyll-sitemap (1.0.0) + jekyll-sitemap (1.1.1) jekyll (~> 3.3) jekyll-swiss (0.4.0) jekyll-theme-architect (0.1.0) @@ -157,40 +175,43 @@ GEM jekyll-theme-time-machine (0.1.0) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-titles-from-headings (0.4.0) + jekyll-titles-from-headings (0.5.0) jekyll (~> 3.3) - jekyll-watch (1.5.0) - listen (~> 3.0, < 3.1) - jemoji (0.8.0) - activesupport (~> 4.0) + jekyll-watch (1.5.1) + listen (~> 3.0) + jemoji (0.8.1) + activesupport (~> 4.0, >= 4.2.9) gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0) - kramdown (1.13.2) + kramdown (1.14.0) liquid (4.0.0) listen (3.0.6) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9.7) mercenary (0.3.6) - mini_portile2 (2.2.0) + mini_portile2 (2.3.0) minima (2.1.1) jekyll (~> 3.3) minitest (5.10.3) multipart-post (2.0.0) net-dns (0.8.0) - nokogiri (1.8.0) - mini_portile2 (~> 2.2.0) + nokogiri (1.8.1) + mini_portile2 (~> 2.3.0) octokit (4.7.0) sawyer (~> 0.8.0, >= 0.5.3) - pathutil (0.14.0) + pathutil (0.16.1) forwardable-extended (~> 2.6) public_suffix (2.0.5) rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - rouge (1.11.1) + rouge (2.2.1) + ruby-enum (0.7.1) + i18n + rubyzip (1.2.1) safe_yaml (1.0.4) - sass (3.5.1) + sass (3.5.3) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -203,7 +224,7 @@ GEM thread_safe (0.3.6) typhoeus (0.8.0) ethon (>= 0.8.0) - tzinfo (1.2.3) + tzinfo (1.2.4) thread_safe (~> 0.1) unicode-display_width (1.3.0) @@ -215,4 +236,4 @@ DEPENDENCIES jekyll BUNDLED WITH - 1.15.1 + 1.16.0 From 42deb3791ce66311476ff1cf4f39487017b56cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 17 Dec 2017 12:59:49 +0100 Subject: [PATCH 031/413] Fix #785: NPE with metrics on empty declarations --- .../metrics/impl/AbstractJavaClassMetric.java | 3 ++- .../pmd/lang/java/rule/design/xml/DataClass.xml | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AbstractJavaClassMetric.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AbstractJavaClassMetric.java index 1f471c37bf..af755a3935 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AbstractJavaClassMetric.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AbstractJavaClassMetric.java @@ -18,6 +18,7 @@ import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetric; import net.sourceforge.pmd.lang.java.multifile.signature.JavaFieldSigMask; import net.sourceforge.pmd.lang.java.multifile.signature.JavaOperationSigMask; + /** * Base class for class metrics. * @@ -113,7 +114,7 @@ public abstract class AbstractJavaClassMetric extends AbstractJavaMetric decls = node.getDeclarations(); for (ASTAnyTypeBodyDeclaration decl : decls) { - if (tClass.isInstance(decl.jjtGetChild(0))) { + if (decl.jjtGetNumChildren() > 0 && tClass.isInstance(decl.jjtGetChild(0))) { result.add(tClass.cast(decl.jjtGetChild(0))); } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml index 3004926e46..0522ed0df0 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml @@ -213,5 +213,20 @@ public class TestDescriptor { + + + + - \ No newline at end of file + + + [java] [6.0.0] NPE in DataClass rule caused by empty declaration #785 + 0 + + + + From c5ddb2fa3cf91bd5476339eaed08435ca3784cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Dec 2017 12:50:45 +0100 Subject: [PATCH 032/413] Remove code ref --- .../pmd/lang/java/rule/design/xml/DataClass.xml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml index 0522ed0df0..f0d98e4924 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/DataClass.xml @@ -213,20 +213,17 @@ public class TestDescriptor { - - - - - [java] [6.0.0] NPE in DataClass rule caused by empty declaration #785 0 - + + + From 4f7786abc9e4394e79c3f61de9bad5dfe7770ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 19 Dec 2017 00:39:56 +0100 Subject: [PATCH 033/413] Update metrics tuto page --- docs/pages/pmd/devdocs/metrics_howto.md | 29 +++++++++++-------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/docs/pages/pmd/devdocs/metrics_howto.md b/docs/pages/pmd/devdocs/metrics_howto.md index e0e7812fab..9c75bc0015 100644 --- a/docs/pages/pmd/devdocs/metrics_howto.md +++ b/docs/pages/pmd/devdocs/metrics_howto.md @@ -1,10 +1,10 @@ --- title: Using code metrics in custom rules tags: [customizing] -summary: "PMD was recently enhanced with the ability to compute code metrics on Java and Apex source (the so-called +summary: "Since 6.0.0, PMD is enhanced with the ability to compute code metrics on Java and Apex source (the so-called Metrics Framework). This framework provides developers with a straightforward interface to use code metrics in their rules, and to extend the framework with their own custom metrics." -last_updated: July 20, 2017 +last_updated: December 18, 2017 permalink: pmd_devdocs_metrics_howto.html author: Clément Fournier --- @@ -12,9 +12,6 @@ author: Clément Fournier ## Using the metrics framework -{%include note.html content="Using the metrics framework is for now restricted to Java rules (with plans to support -XPath rules later)." %} - {%include note.html content="The following explains how to use the Java metrics framework. The Apex framework differs only by the name of its classes." %} @@ -54,8 +51,6 @@ or `ConstructorDeclaration`. ## For Java Rules -First, similarly to XPath rules, you should add the `metrics="true"` attribute to your rule's XML element. - The static façade class `JavaMetrics` is the single entry point to compute metrics in the Java framework. This class provides the method `get` and its overloads. The following sections describes the interface of this class. @@ -104,7 +99,7 @@ public Object visit(ASTMethodDeclaration method, Object data) { ### Metric options Some metrics define options that can be used to slightly modify the computation. You'll typically see these options -gathered inside an enum in the implementation class of the metric, for example `CycloMetric.CycloOptions`. They're +gathered inside an enum in the implementation class of the metric, for example `CycloMetric.CycloOption`. They're also documented on the [index of metrics](pmd_java_metrics_index.html). To use options with a metric, you must first bundle them into a `MetricOptions` object. `MetricOptions` provides the @@ -123,7 +118,7 @@ public Object visit(ASTMethodDeclaration method, Object data) { The version of `MetricOptions.ofOptions` using a collection is useful when you're building a `MetricOptions` from eg the value of an `EnumeratedMultiProperty`, which gives users control of the options they use. See -[CyclomaticComplexityRule]( https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/rule/CyclomaticComplexityRule.java) +[CyclomaticComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java#L35) for an example usage. ### Result options @@ -150,17 +145,18 @@ option too. The following is a sample code for a rule reporting methods with a cyclomatic complexity over 10 and classes with a total cyclo over 50. A metric option can be user-configured with a rule property. More complete examples can be found in -[CyclomaticComplexityRule]( https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/rule/CyclomaticComplexityRule.java), -[NcssCountRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/rule/NcssCountRule.java), -or [GodClassRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/GodClassRule.java). +[CyclomaticComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java#L35), +[NcssCountRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java#L30), +or [GodClassRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/GodClassRule.java#L24). ```java public class CycloRule extends AbstractJavaMetricsRule { public static final BooleanProperty COUNT_BOOLEAN_PATHS - = new BooleanProperty("countBooleanPaths", "Count boolean paths", - true, 0f); + = BooleanProperty.named("countBooleanPaths") + .desc("Count boolean paths") + .defaultValue(true).build(); private static final MetricOptions options; @@ -272,9 +268,10 @@ concrete node types you can target with class and operation metrics, by language Language | Java | Apex | -----------|------|------| -Operation declaration|`ASTMethodOrConstructorDeclaration`
>: `ASTMethodDeclaration`, `ASTConstructorDeclaration`| `ASTMethod` +Operation declaration|`ASTMethodOrConstructorDeclaration`
>: `ASTMethodDeclaration`, `ASTConstructorDeclaration`| `ASTMethod`* Type declaration|`ASTAnyTypeDeclaration` >: `ASTEnumDeclaration`,
`ASTAnnotationDeclaration`, `ASTClassOrInterfaceDeclaration`| `ASTUserClassOrInterface` >: `ASTUserClass`, `ASTUserInterface` +*Apex method metrics are also applied to triggers by default (see [#771](https://github.com/pmd/pmd/pull/771)). Finer capability checking is not available out of the box for now. What if you don't want such a generalisation? The `supports` method lets you define a predicate to check that the node is supported by your metric. For example, @@ -297,5 +294,5 @@ classes. Here's the default behaviour by language and type of metric: Language | Java | Apex | -----------|------|------| -Operation metrics| supports constructors and non abstract methods| supports any non abstract method except ``, ``, and `clone` +Operation metrics| supports constructors and non abstract methods| supports any non abstract method (including triggers), except ``, ``, and `clone` Type declaration|supports classes and enums|supports classes From 904924a0c53a437d70e9d53d8d5e77957f36cee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 19 Dec 2017 12:25:29 +0100 Subject: [PATCH 034/413] Update framework impl page --- .../adding_metrics_support_to_language.md | 76 +++++-------------- 1 file changed, 20 insertions(+), 56 deletions(-) diff --git a/docs/pages/pmd/devdocs/adding_metrics_support_to_language.md b/docs/pages/pmd/devdocs/adding_metrics_support_to_language.md index 2e14e78412..28534d4a18 100644 --- a/docs/pages/pmd/devdocs/adding_metrics_support_to_language.md +++ b/docs/pages/pmd/devdocs/adding_metrics_support_to_language.md @@ -5,12 +5,11 @@ tags: [customizing] summary: "PMD's Java module has an extensive framework for the calculation of metrics, which allows rule developers to implement and use new code metrics very simply. Most of the functionality of this framework is abstracted in such a way that any PMD supported language can implement such a framework without too much trouble. Here's how." -last_updated: August 2017 +last_updated: December 2017 permalink: pmd_devdocs_adding_metrics_support_to_language.html author: Clément Fournier --- -{% include warning.html content="WIP, unstable API" %} ## Internal architecture of the metrics framework @@ -33,43 +32,24 @@ Metrics (`Metric`) plug in to this static system and only provide behaviour t Internally, metric keys (`MetricKey`) are parameterized with their version (`MetricVersion`) to index memoisation maps (see `ParameterizedMetricKey`). This allows us to memoise several versions of the same metric without conflict. -{% include important.html content="The following will be moved when multifile analysis and metrics are separated" %} - - At the very least, a metrics framework has those two components and is just a convenient way to compute and memoize -metrics on a single file. Yet, one of the goals of the metrics framework is to allow for **multi-file analysis**, which -make it possible, for instance, to compute the coupling between two classes. This feature uses two major -components: -* A **project mirror**. This data structure that stores info about all classes and operations (and other relevant - entities, such as fields, packages, etc.) of the analysed project. This is implemented by `PackageStats` in the Java - framework. The role of this structure is to make info about other files available to rules. It's filled by a visitor before rules apply. +metrics on a single file. The expressive power of metrics can be improved by implementing *signature matching* capabilities, +which allows a metric to count signatures matching a specific pattern (a mask) over a whole class. This was originally +designed to work across files, given a working usage resolution. However, making that work with incremental analysis is +harder than it looks, and has been rescheduled to another project. - The information stored in this data structure that's accessible to metrics is mainly comprised of method and field - signatures (e.g. `JavaOperationSignature`), which describes concisely the characteristics of the method or field - (roughly, its modifiers). - -* Some kind of method and field **usage resolution**, i.e. some way to find the fully qualified name of a method from a - method call expression node. This is the trickiest part to implement. In Java it depends on type resolution. ### Abstraction layer As you may have seen, most of the functionality of the first two components are abstracted into `pmd-core`. This allows us to implement new metrics frameworks quite quickly. These abstract components are parameterized by the -node types of the class and operation AST nodes. Moreover, it makes the external behaviour of the framework is very +node types of the class and operation AST nodes. Moreover, it makes the external behaviour of the framework very stable across languages, yet each component can easily be customized by adding methods or overriding existing ones. The signature matching aspect is framed by generic interfaces, but it can't really be abstracted more -than that. For instance, the project mirror is very language specific. Java's implementation uses the natural structure -provided by the language's package system to structure the project's content. Apex, on the other, has no package -system and thus can't use the same mechanism. That explains why the interfaces framing the project mirror are very -loose. Their main goal is to provide type safety through generics. - -Moreover, usage resolution depends on the availability of type resolution for the given language, which is only implemented in -Java. For these reasons, signature matching is considered an optional feature of the metrics framework. But despite -this limitation, signature matching still provides a elegant way to find information about the class we're in. This -feature requires no usage resolution and can be used to implement sophisticated metrics, that already give access to -detection strategies. +than that. The info given in the signatures is usually very language specific, as it includes info about e.g. +visibility modifiers. So more work is required to implement that, but it can already be used to implement +sophisticated metrics, that already give access to detection strategies. ## Implementation of a new framework @@ -78,48 +58,34 @@ detection strategies. * Create a class implementing `QualifiedName`. This implementation must be tailored to the target language so that it can indentify unambiguously any class and operation in the analysed project. You must implement `equals`, `hashCode` and `toString`. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedName.java) + [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaQualifiedName.java) * Determine the AST nodes that correspond to class and method declaration in your language. These types are referred hereafter as `T` and `O`, respectively. Both these types must implement the interface `QualifiableNode`, which means they must expose a `getQualifiedName` method to give access to their qualified name. -### 2. Implement and wire the project memoizer -* Create a class extending `BasicProjectMemoizer`. There's no abstract functionality to implement. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaProjectMemoizer.java) -* Create an AST visitor that fills the project memoizer with memoizers. For that, you use `BasicProjectMemoizer`'s - `addClassMemoizer` and `addOperationMemoizer` methods with a qualified name. - [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsVisitor.java) -* Create a façade class for your visitor. This class extends a `*ParserVisitorAdapter` class and only overrides the - `initializeWith(Node)` method. It's supposed to make your real visitor accept the node in parameter. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsVisitorFacade.java) -* Override the `getMetricsVisitorFacade()` method in your language's handler (e.g. `ApexHandler`). This method gives - back a `VisitorStarter` which initializes your façade with a `Node`. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java#L100-L108) -* Your project memoizer should now get filled when the `metrics` attribute is set to `true` in the rule XML. - -### 3. Implement the façade +### 2. Implement the façade * Create a class extending `AbstractMetricsComputer`. This object will be responsible for calculating metrics given a memoizer, a node and info about the metric. Typically, this object is stateless so you might as well make it a singleton. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java) +* Create a class extending `BasicProjectMemoizer`. There's no abstract functionality to implement. + [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaProjectMemoizer.java) + [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java) * Create a class extending `AbstractMetricsFacade`. This class needs a reference to your `ProjectMemoizer` and your `MetricsComputer`. It backs the real end user façade, and handles user provided parameters before delegating to your `MetricsComputer`. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java) + [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java) * Create the static façade of your framework. This one has an instance of your `MetricsFaçade` object and delegates static methods to that instance. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetrics.java) + [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetrics.java) * Create classes `AbstractOperationMetric` and `AbstractClassMetric`. These must implement `Metric` and `Metric`, respectively. They typically provide defaults for the `supports` method of each metric. - [Example](https://github.com/pmd/pmd/blob/52d78d2fa97913cf73814d0307a1c1ae6125a437/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AbstractJavaOperationMetric.java) + [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/AbstractJavaOperationMetric.java) * Create enums `ClassMetricKey` and `OperationMetricKey`. These must implement `MetricKey` and `MetricKey`. The enums list all available metric keys for your language. [Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaOperationMetricKey.java) * Create metrics by extending your base classes, reference them in your enums, and you can start using them with your façade! -{% include important.html content="The following section will be moved when multifile analysis and metrics are separated" %} - ### Optional: Signature matching You can match the signature of anything: method, field, class, package... It depends on what's useful for you. @@ -133,8 +99,6 @@ build a `Signature` from a `N` are a good idea. * Create signature masks. A mask is an object that matches some signatures based on their features. For example, with the Java framework, you can build a `JavaOperationSigMask` that matches all method signatures with visibility `public`. A sigmask implements `SigMask`, where `S` is the type of signature your mask handles. -* Typically, the project mirror stores the signatures, so you have to implement it in a way that makes it possible to - associate a signature with the qualified name of its node. -* If you want to implement signature matching, create an `AbstractMetric` class, which gives access to a -`SignatureMatcher` to your metrics. Typically, your implementation of `ProjectMirror` implements a -custom `SignatureMatcher` interface, and your façade can give back its instance of the project mirror. +* Create utility methods in your abstract class metric class to count signatures matching a specific mask. +[Example](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/AbstractJavaClassMetric.java#L50) + From 75fe76cbf59368c037a2d2c03f5e2e72953f0d08 Mon Sep 17 00:00:00 2001 From: Tobias Weimer Date: Tue, 19 Dec 2017 20:48:45 +0100 Subject: [PATCH 035/413] Update errorprone.xml Note that Oracle has declared Object.finalize() as deprecated since JDK 9. --- .../src/main/resources/category/java/errorprone.xml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 2afdc6a818..e72a39ff5c 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -190,6 +190,8 @@ for (int i = 0; i < 10; i++) { The method Object.finalize() is called by the garbage collector on an object when garbage collection determines that there are no more references to the object. It should not be invoked by application logic. + +Note that Oracle has declared Object.finalize() as deprecated since JDK 9. 3 @@ -1401,7 +1403,7 @@ public void doSomething() { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#emptyfinalizer"> -Empty finalize methods serve no purpose and should be removed. +Empty finalize methods serve no purpose and should be removed. Note that Oracle has declared Object.finalize() as deprecated since JDK 9. 3 @@ -1765,7 +1767,7 @@ if (x == null) { // preferred class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#finalizedoesnotcallsuperfinalize"> -If the finalize() is implemented, its last action should be to call super.finalize. +If the finalize() is implemented, its last action should be to call super.finalize. Note that Oracle has declared Object.finalize() as deprecated since JDK 9. 3 @@ -1809,7 +1811,7 @@ protected void finalize() { class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#finalizeonlycallssuperfinalize"> -If the finalize() is implemented, it should do something besides just calling super.finalize(). +If the finalize() is implemented, it should do something besides just calling super.finalize(). Note that Oracle has declared Object.finalize() as deprecated since JDK 9. 3 @@ -1845,6 +1847,8 @@ protected void finalize() { Methods named finalize() should not have parameters. It is confusing and most likely an attempt to overload Object.finalize(). It will not be called by the VM. + +Note that Oracle has declared Object.finalize() as deprecated since JDK 9. 3 @@ -1877,6 +1881,8 @@ public class Foo { When overriding the finalize(), the new method should be set as protected. If made public, other classes may invoke it at inappropriate times. + +Note that Oracle has declared Object.finalize() as deprecated since JDK 9. 3 From fbd9306889cac9f5bd9fcea02c24808ce4ef0e45 Mon Sep 17 00:00:00 2001 From: Tobias Weimer Date: Tue, 19 Dec 2017 20:52:23 +0100 Subject: [PATCH 036/413] Update performance.xml Reference to Arrays.copyOf --- pmd-java/src/main/resources/category/java/performance.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 8a46350f8f..2e0cdfdeab 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -64,7 +64,7 @@ sb.append('a'); // use this instead class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#avoidarrayloops"> -Instead of manually copying data between two arrays, use the efficient System.arraycopy method instead. +Instead of manually copying data between two arrays, use the efficient Arrays.copyOf or System.arraycopy method instead. 3 From 766565a0bf707ea662a3e2e048a09b9de5e7af8c Mon Sep 17 00:00:00 2001 From: Tobias Weimer Date: Tue, 19 Dec 2017 20:55:48 +0100 Subject: [PATCH 037/413] Update performance.xml new Integer() and new Long() is deoprecated in JDK 9. --- pmd-java/src/main/resources/category/java/performance.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 2e0cdfdeab..66a871eb27 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -419,6 +419,7 @@ good.append("This is a long string, which is pre-sized"); Calling new Integer() causes memory allocation that can be avoided by the static Integer.valueOf(). It makes use of an internal cache that recycles earlier instances making it more memory efficient. +Note that new Integer() is deprecated since JDK 9 for that reason. 2 @@ -452,6 +453,7 @@ public class Foo { Calling new Long() causes memory allocation that can be avoided by the static Long.valueOf(). It makes use of an internal cache that recycles earlier instances making it more memory efficient. +Note that new Long() is deprecated since JDK 9 for that reason. 2 From f267a8f7c70cc0253f23d140aab5ac1c2cc835f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20So=CC=88semann?= Date: Wed, 20 Dec 2017 15:58:12 +0100 Subject: [PATCH 038/413] Fixed https://github.com/pmd/pmd/issues/792 --- .../resources/category/apex/errorprone.xml | 2 +- .../xml/AvoidDirectAccessTriggerMap.xml | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/pmd-apex/src/main/resources/category/apex/errorprone.xml b/pmd-apex/src/main/resources/category/apex/errorprone.xml index 9d9beb26c6..2cd0249c43 100644 --- a/pmd-apex/src/main/resources/category/apex/errorprone.xml +++ b/pmd-apex/src/main/resources/category/apex/errorprone.xml @@ -22,7 +22,7 @@ Avoid directly accessing Trigger.old and Trigger.new as it can lead to a bug. Tr diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml index 9e3c7e384e..49d7898859 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml @@ -7,15 +7,15 @@ Directly accessing the Trigger.old map. - 2 + 1 - - + + Looping through map, best practice 0 + + + Regression issue #792 + 0 + + From ef41c718708bb7e635b04eda2bd4a74da89a6c0d Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Wed, 20 Dec 2017 20:39:41 +0100 Subject: [PATCH 039/413] github - Update issue template --- .github/ISSUE_TEMPLATE.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6808cd0d3a..05b0d2124b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,6 +1,9 @@ -Please, prefix the report title with the language it applies to within brackets, such as *[java]* or *[apex]*. If not specific to a language, you can use *[core]* +Please, prefix the report title with the language it applies to within brackets, such as [java] or [apex]. +If not specific to a language, you can use [core]. -**Rule Set:** +**Affects PMD Version:** + +**Rule:** **Description:** From 64b862eef965aaa39c17db1808063e2f129d7057 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Wed, 20 Dec 2017 22:37:49 +0100 Subject: [PATCH 040/413] Fixes #793 [java] Parser error with private method in nested classes in interfaces --- docs/pages/release_notes.md | 3 ++ pmd-java/etc/grammar/Java.jjt | 6 +++- .../lang/java/ast/ASTMethodDeclaration.java | 7 +++-- .../pmd/lang/java/ast/JDKVersionTest.java | 8 ++++++ ...ivate_method_in_inner_class_interface.java | 28 +++++++++++++++++++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/private_method_in_inner_class_interface.java diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c9df30afa8..c6ff938d7c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,6 +21,9 @@ This is a bug fixing release. ### Fixed Issues +* java + * [#793](https://github.com/pmd/pmd/issues/793): \[java] Parser error with private method in nested classes in interfaces + ### API Changes ### External Contributions \ No newline at end of file diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index 71548d28e7..46f04e8fb8 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -1,4 +1,7 @@ /** + * Fixes #793 [java] Parser error with private method in nested classes in interfaces + * Andreas Dangel 12/2017 + *==================================================================== * Add support for Java 9 changes: * Private interface methods are only allowed with java9. * A single underscore "_" is an invalid identifier in java9. @@ -340,6 +343,7 @@ public class JavaParser { * Keeps track whether we are dealing with an interface or not. Needed since the tree is * is not fully constructed yet, when we check for private interface methods. * The flag is updated, if entering ClassOrInterfaceDeclaration and reset when leaving. + * The flag is also reset, if entering a anonymous inner class. */ private boolean inInterface = false; private void checkForBadPrivateInterfaceMethod(ASTMethodDeclaration node) { @@ -2009,7 +2013,7 @@ void AllocationExpression(): ( ArrayDimsAndInits() | - Arguments() [ ClassOrInterfaceBody() ] + Arguments() [ {inInterface = false;} ClassOrInterfaceBody() ] ) ) { checkForBadAnonymousDiamondUsage(); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java index ca230e0626..c85e9f24c7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java @@ -75,8 +75,11 @@ public class ASTMethodDeclaration extends AbstractJavaAccessNode implements DFAG public boolean isInterfaceMember() { - ASTClassOrInterfaceDeclaration clz = getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); - return clz != null && clz.isInterface(); + ASTClassOrInterfaceBody body = getFirstParentOfType(ASTClassOrInterfaceBody.class); + if (body != null && body.jjtGetParent() instanceof ASTClassOrInterfaceDeclaration) { + return ((ASTClassOrInterfaceDeclaration) body.jjtGetParent()).isInterface(); + } + return false; } public boolean isVoid() { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java index aef4882b70..71e49f2cd4 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/JDKVersionTest.java @@ -10,6 +10,7 @@ import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava15; import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava17; import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava18; import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava9; +import static org.junit.Assert.assertFalse; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -267,4 +268,11 @@ public class JDKVersionTest { public final void jdk9TryWithResources() { parseJava9(loadSource("jdk9_try_with_resources.java")); } + + @Test + public final void jdk7PrivateMethodInnerClassInterface() { + ASTCompilationUnit acu = parseJava17(loadSource("private_method_in_inner_class_interface.java")); + ASTMethodDeclaration privateMethod = acu.getFirstDescendantOfType(ASTMethodDeclaration.class); + assertFalse(privateMethod.isInterfaceMember()); + } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/private_method_in_inner_class_interface.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/private_method_in_inner_class_interface.java new file mode 100644 index 0000000000..8cc67a7e62 --- /dev/null +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/ast/jdkversiontests/private_method_in_inner_class_interface.java @@ -0,0 +1,28 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast.testdata; + +/** + * With Java9, private methods in interfaces are possible. + * But they must not be confused with private methods in inner classes of interfaces + * when using older java version. + * + * @see https://github.com/pmd/pmd/issues/793 + */ +public interface InterfaceWithInnerClass { + + Object FOO = new Object() { + private void privateMethod() { } + }; + + class InnerClass { + private void privateMethod() { } + } + + enum InnerEnum { + VALUE; + private void privateMethod() { } + } +} From 9304c7008433c8b5456ee97d4a3b3676bcbf2121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20S=C3=B6semann?= Date: Wed, 20 Dec 2017 23:25:40 +0100 Subject: [PATCH 041/413] Fixes #788 [apex] Method chaining breaks ApexCRUDViolation --- .../apex/rule/security/ApexCRUDViolationRule.java | 2 +- .../apex/rule/security/xml/ApexCRUDViolation.xml | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java index 2d348857d2..1e32beb021 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java @@ -322,7 +322,7 @@ public class ApexCRUDViolationRule extends AbstractApexRule { private boolean isLastMethodName(final ASTMethodCallExpression methodNode, final String className, final String methodName) { final ASTReferenceExpression reference = methodNode.getFirstChildOfType(ASTReferenceExpression.class); - if (reference.getNode().getNames().size() > 0) { + if (reference != null && reference.getNode().getNames().size() > 0) { if (reference.getNode().getNames().get(reference.getNode().getNames().size() - 1).getValue() .equalsIgnoreCase(className) && Helper.isMethodName(methodNode, methodName)) { return true; diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml index ce3933613c..2c7a4768b6 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml @@ -812,6 +812,18 @@ public class Foo { public list bar; public map baz; } +]]> + + + + Regression issue #799 detection + 0 + From 91bce046f521c0cf2046846c7e2772b299d201be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Wed, 20 Dec 2017 23:24:37 -0300 Subject: [PATCH 042/413] Update change log, refs #796 --- docs/pages/release_notes.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c9df30afa8..2f3e55a174 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,6 +21,11 @@ This is a bug fixing release. ### Fixed Issues +* apex-errorprone + * [#792](https://github.com/pmd/pmd/issues/792): \[apex] AvoidDirectAccessTriggerMap incorrectly detects array access in classes + ### API Changes -### External Contributions \ No newline at end of file +### External Contributions + +* [#796](https://github.com/pmd/pmd/pull/796): \[apex] AvoidDirectAccessTriggerMap incorrectly detects array access in classes - [Robert Sösemann](https://github.com/up2go-rsoesemann) From 0d33ba12b2479059ab2b2e0fb0b806b00684c101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Wed, 20 Dec 2017 23:31:54 -0300 Subject: [PATCH 043/413] Update changelog, refs #786 --- docs/pages/release_notes.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c9df30afa8..3e1b76be95 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,6 +21,9 @@ This is a bug fixing release. ### Fixed Issues +* java-design + * [#785](https://github.com/pmd/pmd/issues/785): \[java] NPE in DataClass rule + ### API Changes -### External Contributions \ No newline at end of file +### External Contributions From ca41530cf33e3979a80196ceda53f5f1b996cfb3 Mon Sep 17 00:00:00 2001 From: "Travis CI (pmd-bot)" Date: Thu, 21 Dec 2017 02:41:34 +0000 Subject: [PATCH 044/413] Update documentation --- docs/pages/pmd/rules/apex/errorprone.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/pmd/rules/apex/errorprone.md b/docs/pages/pmd/rules/apex/errorprone.md index f196682126..47c674f32e 100644 --- a/docs/pages/pmd/rules/apex/errorprone.md +++ b/docs/pages/pmd/rules/apex/errorprone.md @@ -16,7 +16,7 @@ keywords: Error Prone, AvoidDirectAccessTriggerMap, AvoidHardcodingId, EmptyCatc Avoid directly accessing Trigger.old and Trigger.new as it can lead to a bug. Triggers should be bulkified and iterate through the map to handle the actions for each item separately. ``` -//ArrayLoadExpression/TriggerVariableExpression | //ArrayLoadExpression/LiteralExpression +//ArrayLoadExpression[TriggerVariableExpression and LiteralExpression] ``` **Example(s):** From 3f478bc6f37a93fb75432c0cc6a0a54ae45295da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Thu, 21 Dec 2017 00:35:05 -0300 Subject: [PATCH 045/413] Update changelog, refs #799 --- docs/pages/release_notes.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c9df30afa8..69e7aaf163 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,6 +21,11 @@ This is a bug fixing release. ### Fixed Issues +* apex-security + * [#788](https://github.com/pmd/pmd/issues/788): \[apex] Method chaining breaks ApexCRUDViolation + ### API Changes -### External Contributions \ No newline at end of file +### External Contributions + +* [#799](https://github.com/pmd/pmd/pull/799): \[apex] Method chaining breaks ApexCRUDViolation - [Robert Sösemann](https://github.com/up2go-rsoesemann) From bc95ca803ca627fb137223b76e3aa822bd333a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 21 Dec 2017 16:45:24 +0100 Subject: [PATCH 046/413] Beanify controllers, new settings persistence logic Add property edition to the XPath panel --- .../sourceforge/pmd/rules/RuleBuilder.java | 4 +- pmd-ui/pom.xml | 10 + .../pmd/util/fxdesigner/Designer.java | 3 +- .../EditPropertyDialogController.java | 240 ++++++++++++ .../util/fxdesigner/EventLogController.java | 1 + .../ExportXPathWizardController.java | 62 +-- .../fxdesigner/MainDesignerController.java | 219 +++++------ .../fxdesigner/NodeInfoPanelController.java | 22 +- .../fxdesigner/SourceEditorController.java | 155 +++----- .../util/fxdesigner/XPathPanelController.java | 157 +++++--- .../pmd/util/fxdesigner/model/ASTManager.java | 42 +- .../model/ObservableRuleBuilder.java | 358 ++++++++++++++++++ .../model/ObservableXPathRuleBuilder.java | 57 +++ .../util/fxdesigner/model/XPathEvaluator.java | 33 +- .../util/fxdesigner/util/DesignerUtil.java | 130 +++++-- .../util/PropertyDescriptorSpec.java | 201 ++++++++++ .../fxdesigner/util/beans/BeanModelNode.java | 37 ++ .../util/beans/BeanModelNodeSeq.java | 76 ++++ .../util/beans/BeanNodeVisitor.java | 31 ++ .../util/beans/RestorePropertyVisitor.java | 120 ++++++ .../fxdesigner/util/beans/SettingsOwner.java | 28 ++ .../util/beans/SettingsPersistenceUtil.java | 250 ++++++++++++ .../util/beans/SimpleBeanModelNode.java | 145 +++++++ .../fxdesigner/util/beans/XmlInterface.java | 152 ++++++++ .../util/beans/XmlInterfaceVersion1.java | 160 ++++++++ .../converters/LanguageVersionConverter.java | 35 ++ .../converters/PropertyTypeIdConverter.java | 34 ++ .../converters/RulePriorityConverter.java | 34 ++ .../codearea/AvailableSyntaxHighlighters.java | 19 +- .../util/codearea/CustomCodeArea.java | 2 +- .../fxdesigner/util/codearea/StyleLayer.java | 2 +- .../XPathSyntaxHighlighter.java | 4 +- .../util/controls/PropertyTableView.java | 217 +++++++++++ .../util/controls/ScopeHierarchyTreeCell.java | 26 +- .../util/controls/ScopeHierarchyTreeItem.java | 7 +- .../fxdesigner/util/settings/AppSetting.java | 42 -- .../util/settings/SettingsOwner.java | 24 -- .../util/settings/XMLSettingsLoader.java | 82 ---- .../util/settings/XMLSettingsSaver.java | 100 ----- .../pmd/util/fxdesigner/css/designer.css | 69 ++-- .../pmd/util/fxdesigner/fxml/designer.fxml | 57 ++- .../fxdesigner/fxml/edit-property-dialog.fxml | 48 +++ .../pmd/util/fxdesigner/fxml/editor.fxml | 57 +-- .../pmd/util/fxdesigner/fxml/event-log.fxml | 67 ++-- .../pmd/util/fxdesigner/fxml/node-info.fxml | 44 +-- .../pmd/util/fxdesigner/fxml/xpath.fxml | 99 ++--- .../fxdesigner/util/DesignerUtilTest.java | 25 ++ 47 files changed, 2923 insertions(+), 864 deletions(-) create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EditPropertyDialogController.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableRuleBuilder.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableXPathRuleBuilder.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/PropertyDescriptorSpec.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNode.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNodeSeq.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanNodeVisitor.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/RestorePropertyVisitor.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsOwner.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsPersistenceUtil.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SimpleBeanModelNode.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterface.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterfaceVersion1.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/LanguageVersionConverter.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/PropertyTypeIdConverter.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/RulePriorityConverter.java create mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/PropertyTableView.java delete mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/AppSetting.java delete mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/SettingsOwner.java delete mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsLoader.java delete mode 100644 pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsSaver.java create mode 100644 pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/edit-property-dialog.fxml create mode 100644 pmd-ui/src/test/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtilTest.java 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 99063e45ca..af015a2932 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 @@ -25,7 +25,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; * @author Clément Fournier * @since 6.0.0 */ -/* default */ class RuleBuilder { +public class RuleBuilder { private List> definedProperties = new ArrayList<>(); private String name; @@ -44,7 +44,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; private boolean isUsesMultifile; private boolean isUsesTyperesolution; - /* default */ RuleBuilder(String name, String clazz, String language) { + public RuleBuilder(String name, String clazz, String language) { this.name = name; language(language); className(clazz); diff --git a/pmd-ui/pom.xml b/pmd-ui/pom.xml index 20a75bf895..e30b07a8f0 100644 --- a/pmd-ui/pom.xml +++ b/pmd-ui/pom.xml @@ -45,6 +45,16 @@ richtextfx 0.8.1 + + org.controlsfx + controlsfx + 8.40.13 + + + commons-beanutils + commons-beanutils-core + 1.8.3 + net.sourceforge.pmd pmd-apex diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/Designer.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/Designer.java index ce6871938e..88729995f8 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/Designer.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/Designer.java @@ -11,6 +11,7 @@ import java.util.Objects; import java.util.stream.Collectors; import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil; import javafx.application.Application; import javafx.collections.ObservableList; @@ -47,7 +48,7 @@ public class Designer extends Application { parseParameters(getParameters()); FXMLLoader loader - = new FXMLLoader(getClass().getResource("fxml/designer.fxml")); + = new FXMLLoader(DesignerUtil.getFxml("designer.fxml")); DesignerRoot owner = new DesignerRoot(stage); MainDesignerController mainController = new MainDesignerController(owner); diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EditPropertyDialogController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EditPropertyDialogController.java new file mode 100644 index 0000000000..bfcdb8a5f9 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EditPropertyDialogController.java @@ -0,0 +1,240 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner; + +import static net.sourceforge.pmd.properties.MultiValuePropertyDescriptor.DEFAULT_DELIMITER; +import static net.sourceforge.pmd.properties.MultiValuePropertyDescriptor.DEFAULT_NUMERIC_DELIMITER; +import static net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil.rewire; + +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +import org.controlsfx.validation.Severity; +import org.controlsfx.validation.ValidationResult; +import org.controlsfx.validation.ValidationSupport; +import org.controlsfx.validation.Validator; +import org.reactfx.util.Try; +import org.reactfx.value.Var; + +import net.sourceforge.pmd.properties.PropertyTypeId; +import net.sourceforge.pmd.properties.ValueParser; +import net.sourceforge.pmd.properties.ValueParserConstants; +import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil; +import net.sourceforge.pmd.util.fxdesigner.util.PropertyDescriptorSpec; +import net.sourceforge.pmd.util.fxdesigner.util.controls.PropertyTableView; + +import javafx.application.Platform; +import javafx.beans.property.Property; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.TextField; +import javafx.stage.Stage; + + +/** + * Property edition dialog. Use {@link #bindToDescriptor(PropertyDescriptorSpec, ObservableList)} )} + * to use this dialog to edit a descriptor spec. Typically owned by a {@link PropertyTableView}. + * The controller must be instantiated by hand. + * + * @author Clément Fournier + * @see PropertyDescriptorSpec + * @since 6.0.0 + */ +public class EditPropertyDialogController implements Initializable { + + private final Var typeId = Var.newSimpleVar(PropertyTypeId.STRING); + private final Var commitHandler = Var.newSimpleVar(null); + private Var backingDescriptor = Var.newSimpleVar(null); + private Var> backingDescriptorList = Var.newSimpleVar(null); + + private ValidationSupport validationSupport = new ValidationSupport(); + @FXML + private TextField nameField; + @FXML + private TextField descriptionField; + @FXML + private ChoiceBox typeChoiceBox; + @FXML + private TextField valueField; + @FXML + private Button commitButton; + + + public EditPropertyDialogController() { + + } + + + public EditPropertyDialogController(Runnable commitHandler) { + this.commitHandler.setValue(commitHandler); + } + + + @Override + public void initialize(URL location, ResourceBundle resources) { + + commitButton.setOnAction(e -> { + commitHandler.ifPresent(Runnable::run); + getStage().close(); + this.free(); + }); + + commitButton.disableProperty().bind(validationSupport.invalidProperty()); + + Platform.runLater(() -> { + typeId.bind(typeChoiceBox.getSelectionModel().selectedItemProperty()); + typeChoiceBox.setConverter(DesignerUtil.stringConverter(PropertyTypeId::getStringId, + PropertyTypeId::lookupMnemonic)); + typeChoiceBox.getItems().addAll(PropertyTypeId.typeIdsToConstants().values()); + FXCollections.sort(typeChoiceBox.getItems()); + }); + + Platform.runLater(this::registerBasicValidators); + + typeIdProperty().values() + .filter(Objects::nonNull) + .subscribe(this::registerTypeDependentValidators); + + } + + + private Stage getStage() { + return (Stage) commitButton.getScene().getWindow(); + } + + + /** Unbinds this dialog from its backing properties. */ + public void free() { + backingDescriptor.ifPresent(PropertyDescriptorSpec::unbind); + backingDescriptor.setValue(null); + backingDescriptorList.setValue(null); + } + + + /** + * Wires this dialog to the descriptor, so that the controls edit the descriptor. + * + * @param spec The descriptor + */ + public void bindToDescriptor(PropertyDescriptorSpec spec, ObservableList allDescriptors) { + backingDescriptor.setValue(spec); + backingDescriptorList.setValue(allDescriptors); + rewire(spec.nameProperty(), this.nameProperty(), this::setName); + rewire(spec.typeIdProperty(), this.typeIdProperty(), this::setTypeId); + rewire(spec.valueProperty(), this.valueProperty(), this::setValue); + rewire(spec.descriptionProperty(), this.descriptionProperty(), this::setDescription); + } + + + // Validators for attributes common to all properties + private void registerBasicValidators() { + Validator noWhitespaceName + = Validator.createRegexValidator("Name cannot contain whitespace", "\\S*+", Severity.ERROR); + Validator emptyName = Validator.createEmptyValidator("Name required"); + Validator uniqueName = (c, val) -> { + long sameNameDescriptors = backingDescriptorList.getOrElse(FXCollections.emptyObservableList()) + .stream() + .map(PropertyDescriptorSpec::getName) + .filter(getName()::equals) + .count(); + + return new ValidationResult().addErrorIf(c, "The name must be unique", sameNameDescriptors > 1); + }; + + validationSupport.registerValidator(nameField, Validator.combine(noWhitespaceName, emptyName, uniqueName)); + + Validator noWhitespaceDescription + = Validator.createRegexValidator("Message cannot be whitespace", "(\\s*+\\S.*)?", Severity.ERROR); + Validator emptyDescription = Validator.createEmptyValidator("Message required"); + validationSupport.registerValidator(descriptionField, Validator.combine(noWhitespaceDescription, emptyDescription)); + } + + + private void registerTypeDependentValidators(PropertyTypeId typeId) { + Validator valueValidator = (c, val) -> + ValidationResult.fromErrorIf(valueField, "The value couldn't be parsed", + Try.tryGet(() -> getValueParser(typeId).valueOf(getValue())).isFailure()); + + + validationSupport.registerValidator(valueField, valueValidator); + } + + + private ValueParser getValueParser(PropertyTypeId typeId) { + ValueParser parser = typeId.getValueParser(); + if (typeId.isPropertyMultivalue()) { + char delimiter = typeId.isPropertyNumeric() ? DEFAULT_NUMERIC_DELIMITER : DEFAULT_DELIMITER; + parser = ValueParserConstants.multi(parser, delimiter); + } + return parser; + } + + + public String getName() { + return nameField.getText(); + } + + + public void setName(String name) { + nameField.setText(name); + } + + + public Property nameProperty() { + return nameField.textProperty(); + } + + + public String getDescription() { + return descriptionField.getText(); + } + + + public void setDescription(String description) { + descriptionField.setText(description); + } + + + public Property descriptionProperty() { + return descriptionField.textProperty(); + } + + + public PropertyTypeId getTypeId() { + return typeId.getValue(); + } + + + public void setTypeId(PropertyTypeId typeId) { + typeChoiceBox.getSelectionModel().select(typeId); + } + + + public Var typeIdProperty() { + return typeId; + } + + + public String getValue() { + return valueField.getText(); + } + + + public void setValue(String value) { + valueField.setText(value); + } + + + public Property valueProperty() { + return valueField.textProperty(); + } + + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EventLogController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EventLogController.java index 3f53190522..e4ab58c36c 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EventLogController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/EventLogController.java @@ -23,6 +23,7 @@ import javafx.scene.control.TableView; import javafx.scene.control.TextArea; import javafx.scene.control.cell.PropertyValueFactory; + /** * @author Clément Fournier * @since 6.0.0 diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/ExportXPathWizardController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/ExportXPathWizardController.java index f8d4aaa34b..51570ac3f6 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/ExportXPathWizardController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/ExportXPathWizardController.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.util.fxdesigner; import java.net.URL; -import java.util.Arrays; import java.util.Map; import java.util.ResourceBundle; import java.util.WeakHashMap; @@ -58,10 +57,11 @@ public class ExportXPathWizardController implements Initializable { @Override public void initialize(URL location, ResourceBundle resources) { - languageChoiceBox.getItems().addAll(Arrays.stream(DesignerUtil.getSupportedLanguageVersions()) - .map(LanguageVersion::getLanguage) - .distinct() - .collect(Collectors.toList())); + languageChoiceBox.getItems().addAll(DesignerUtil.getSupportedLanguageVersions() + .stream() + .map(LanguageVersion::getLanguage) + .distinct() + .collect(Collectors.toList())); languageChoiceBox.setConverter(new StringConverter() { @Override @@ -117,34 +117,34 @@ public class ExportXPathWizardController implements Initializable { // TODO very inefficient, can we do better? final String template = " >\n" - + " \n" - + "%s\n" - + " \n" - + " %d\n" - + " \n" - + " \n" - + " \n" - + "\n" - + " \n" - + " \n" - + " \n" - + " \n" - + ""; + + " language=\"%s\"\n" + + " message=\"%s\"\n" + + " class=\"net.sourceforge.pmd.lang.rule.XPathRule\"\n" + + " >\n" + + " \n" + + "%s\n" + + " \n" + + " %d\n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
"; return String.format(template, - nameField.getText(), - languageChoiceBox.getSelectionModel().getSelectedItem().getTerseName(), - messageField.getText(), - "TODO", - descriptionField.getText(), // TODO format - (int) prioritySlider.getValue(), - xpathExpression.getValue() + nameField.getText(), + languageChoiceBox.getSelectionModel().getSelectedItem().getTerseName(), + messageField.getText(), + "TODO", + descriptionField.getText(), // TODO format + (int) prioritySlider.getValue(), + xpathExpression.getValue() ); } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java index fd2f8bafa8..e42621d933 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/MainDesignerController.java @@ -12,24 +12,22 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.ResourceBundle; import java.util.Stack; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; +import org.reactfx.value.Val; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil; import net.sourceforge.pmd.util.fxdesigner.util.LimitedSizeStack; -import net.sourceforge.pmd.util.fxdesigner.util.settings.AppSetting; -import net.sourceforge.pmd.util.fxdesigner.util.settings.SettingsOwner; -import net.sourceforge.pmd.util.fxdesigner.util.settings.XMLSettingsLoader; -import net.sourceforge.pmd.util.fxdesigner.util.settings.XMLSettingsSaver; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; @@ -37,10 +35,7 @@ import javafx.animation.Timeline; import javafx.application.Platform; import javafx.beans.property.DoubleProperty; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; -import javafx.scene.Parent; -import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; @@ -58,10 +53,7 @@ import javafx.scene.control.TextArea; import javafx.scene.control.ToggleButton; import javafx.scene.control.Tooltip; import javafx.stage.FileChooser; -import javafx.stage.Modality; -import javafx.stage.Stage; import javafx.util.Duration; -import javafx.util.StringConverter; /** @@ -93,8 +85,6 @@ public class MainDesignerController implements Initializable, SettingsOwner { @FXML private MenuItem exportXPathMenuItem; @FXML - private Menu exportMenu; - @FXML private Menu fileMenu; /* Center toolbar */ @FXML @@ -123,7 +113,12 @@ public class MainDesignerController implements Initializable, SettingsOwner { private SourceEditorController sourceEditorController; @FXML private EventLogController eventLogPanelController; + + // Other fields private Stack recentFiles = new LimitedSizeStack<>(5); + // Properties + private Val languageVersion = Val.constant(DesignerUtil.defaultLanguageVersion()); + private Val xpathVersion = Val.constant(DesignerUtil.defaultXPathVersion()); public MainDesignerController(DesignerRoot owner) { @@ -133,22 +128,26 @@ public class MainDesignerController implements Initializable, SettingsOwner { @Override public void initialize(URL location, ResourceBundle resources) { - try { - XMLSettingsLoader loader = new XMLSettingsLoader(DesignerUtil.getSettingsFile()); - loadSettings(loader.getSettings()); - } catch (IOException ioe) { - // no big deal + SettingsPersistenceUtil.restoreProperties(this, DesignerUtil.getSettingsFile()); + } catch (Exception e) { + // shouldn't prevent the app from opening + // in case the file is corrupted, it will be overwritten on shutdown + e.printStackTrace(); } - initializeLanguageVersionMenu(); initializeViewAnimation(); xpathPanelController.initialiseVersionChoiceBox(xpathVersionChoiceBox); - sourceEditorController.languageVersionProperty().bind(languageChoiceBox.getSelectionModel().selectedItemProperty()); - xpathPanelController.xpathVersionProperty().bind(xpathVersionChoiceBox.getSelectionModel().selectedItemProperty()); + languageVersion = Val.wrap(languageChoiceBox.getSelectionModel().selectedItemProperty()); + DesignerUtil.rewire(sourceEditorController.languageVersionProperty(), + languageVersion, this::setLanguageVersion); + + xpathVersion = Val.wrap(xpathVersionChoiceBox.getSelectionModel().selectedItemProperty()); + DesignerUtil.rewire(xpathPanelController.xpathVersionProperty(), + xpathVersion, this::setXpathVersion); refreshASTButton.setOnAction(e -> onRefreshASTClicked()); licenseMenuItem.setOnAction(e -> showLicensePopup()); @@ -158,41 +157,32 @@ public class MainDesignerController implements Initializable, SettingsOwner { fileMenu.setOnShowing(e -> onFileMenuShowing()); exportXPathMenuItem.setOnAction(e -> { try { - onExportXPathToRuleClicked(); - } catch (IOException ex) { - ex.printStackTrace(); - // pretend it didn't happen + xpathPanelController.showExportXPathToRuleWizard(); + } catch (IOException e1) { + e1.printStackTrace(); } }); sourceEditorController.refreshAST(); + xpathPanelController.evaluateXPath(sourceEditorController.getCompilationUnit(), + getLanguageVersion()); Platform.runLater(() -> sourceEditorController.moveCaret(0, 0)); + Platform.runLater(() -> { // fixes choicebox bad rendering on first opening + languageChoiceBox.show(); + languageChoiceBox.hide(); + }); } private void initializeLanguageVersionMenu() { - List supported = Arrays.asList(DesignerUtil.getSupportedLanguageVersions()); + List supported = DesignerUtil.getSupportedLanguageVersions(); supported.sort(LanguageVersion::compareTo); languageChoiceBox.getItems().addAll(supported); + languageChoiceBox.setConverter(DesignerUtil.languageVersionStringConverter()); - languageChoiceBox.setConverter(new StringConverter() { - @Override - public String toString(LanguageVersion object) { - return object.getShortName(); - } - - - @Override - public LanguageVersion fromString(String string) { - return LanguageRegistry.findLanguageVersionByTerseName(string.toLowerCase()); - } - }); - - LanguageVersion defaultLangVersion = LanguageRegistry.getLanguage("Java").getDefaultVersion(); - languageChoiceBox.getSelectionModel().select(defaultLangVersion); + languageChoiceBox.getSelectionModel().select(DesignerUtil.defaultLanguageVersion()); languageChoiceBox.show(); - } @@ -200,7 +190,7 @@ public class MainDesignerController implements Initializable, SettingsOwner { // gets captured in the closure final double defaultMainHorizontalSplitPaneDividerPosition - = mainHorizontalSplitPane.getDividerPositions()[0]; + = mainHorizontalSplitPane.getDividerPositions()[0]; // show/ hide bottom pane @@ -223,9 +213,7 @@ public class MainDesignerController implements Initializable, SettingsOwner { public void shutdown() { try { - XMLSettingsSaver saver = XMLSettingsSaver.forFile(DesignerUtil.getSettingsFile()); - this.saveSettings(saver); - saver.save(); + SettingsPersistenceUtil.persistProperties(this, DesignerUtil.getSettingsFile()); } catch (IOException ioe) { // nevermind } @@ -238,7 +226,7 @@ public class MainDesignerController implements Initializable, SettingsOwner { private void onRefreshASTClicked() { sourceEditorController.refreshAST(); xpathPanelController.evaluateXPath(sourceEditorController.getCompilationUnit(), - sourceEditorController.getLanguageVersion()); + getLanguageVersion()); } @@ -255,13 +243,15 @@ public class MainDesignerController implements Initializable, SettingsOwner { public void onNameDeclarationSelected(NameDeclaration declaration) { sourceEditorController.clearNodeHighlight(); + + List occ = declaration.getNode().getScope().getDeclarations().get(declaration); + if (occ != null) { + sourceEditorController.highlightNodesSecondary(occ.stream() + .map(NameOccurrence::getLocation) + .collect(Collectors.toList())); + } + sourceEditorController.highlightNodePrimary(declaration.getNode()); - sourceEditorController.highlightNodesSecondary(declaration.getNode().getScope() - .getDeclarations() - .get(declaration) - .stream() - .map(NameOccurrence::getLocation) - .collect(Collectors.toList())); } @@ -282,27 +272,6 @@ public class MainDesignerController implements Initializable, SettingsOwner { } - private void onExportXPathToRuleClicked() throws IOException { - // doesn't work for some reason - ExportXPathWizardController wizard - = new ExportXPathWizardController(xpathPanelController.xpathExpressionProperty()); - - FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/xpath-export-wizard.fxml")); - loader.setController(wizard); - - final Stage dialog = new Stage(); - dialog.initOwner(designerRoot.getMainStage()); - dialog.setOnCloseRequest(e -> wizard.shutdown()); - dialog.initModality(Modality.WINDOW_MODAL); - - Parent root = loader.load(); - Scene scene = new Scene(root); - //stage.setTitle("PMD Rule Designer (v " + PMD.VERSION + ')'); - dialog.setScene(scene); - dialog.show(); - } - - private void onFileMenuShowing() { openRecentMenu.setDisable(recentFiles.size() == 0); } @@ -316,15 +285,15 @@ public class MainDesignerController implements Initializable, SettingsOwner { sourceEditorController.clearStyleLayers(); } - private void loadSourceFromFile(File file) { if (file != null) { try { String source = IOUtils.toString(new FileInputStream(file)); - sourceEditorController.replaceText(source); + sourceEditorController.setText(source); LanguageVersion guess = DesignerUtil.getLanguageVersionFromExtension(file.getName()); if (guess != null) { // guess the language from the extension languageChoiceBox.getSelectionModel().select(guess); + onRefreshASTClicked(); } recentFiles.push(file); @@ -337,6 +306,7 @@ public class MainDesignerController implements Initializable, SettingsOwner { private void updateRecentFilesMenu() { List items = new ArrayList<>(); + List filesToClear = new ArrayList<>(); for (final File f : recentFiles) { if (f.exists() && f.isFile()) { @@ -346,9 +316,11 @@ public class MainDesignerController implements Initializable, SettingsOwner { Tooltip.install(item.getContent(), new Tooltip(f.getAbsolutePath())); items.add(item); } else { - recentFiles.remove(f); + filesToClear.add(f); } } + recentFiles.removeAll(filesToClear); + if (items.isEmpty()) { openRecentMenu.setDisable(true); return; @@ -369,48 +341,49 @@ public class MainDesignerController implements Initializable, SettingsOwner { } - @Override - public List getSettings() { - List settings = new ArrayList<>(); - settings.add(new AppSetting("recentFiles", this::getRecentFiles, this::setRecentFiles)); - settings.add(new AppSetting("isMaximized", this::isMaximized, this::setIsMaximized)); - settings.add(new AppSetting("bottomExpandedTab", this::getBottomExpandedTab, this::setBottomExpandedTab)); - return settings; + public void invalidateAst() { + nodeInfoPanelController.invalidateInfo(); + xpathPanelController.invalidateResults(false); + sourceEditorController.clearNodeHighlight(); } - private void saveSettings(XMLSettingsSaver saver) { - saveSettingsOf(this, saver); - saveSettingsOf(sourceEditorController, saver); - saveSettingsOf(xpathPanelController, saver); + public LanguageVersion getLanguageVersion() { + return languageVersion.getValue(); } - private void saveSettingsOf(SettingsOwner owner, XMLSettingsSaver saver) { - for (AppSetting s : owner.getSettings()) { - saver.put(s.getKeyName(), s.getValue()); + public void setLanguageVersion(LanguageVersion version) { + if (languageChoiceBox.getItems().contains(version)) { + languageChoiceBox.getSelectionModel().select(version); } } - private void loadSettings(Map settings) { - loadSettingsOf(sourceEditorController, settings); - loadSettingsOf(xpathPanelController, settings); - loadSettingsOf(this, settings); + public Val languageVersionProperty() { + return languageVersion; } - private void loadSettingsOf(SettingsOwner owner, Map loaded) { - for (AppSetting s : owner.getSettings()) { - String val = loaded.get(s.getKeyName()); - if (val != null) { - s.setValue(val); - } + public String getXpathVersion() { + return xpathVersion.getValue(); + } + + + public void setXpathVersion(String version) { + if (xpathVersionChoiceBox.getItems().contains(version)) { + xpathVersionChoiceBox.getSelectionModel().select(version); } } - private String getRecentFiles() { + public Val xpathVersionProperty() { + return xpathVersion; + } + + + @PersistentProperty + public String getRecentFiles() { StringBuilder sb = new StringBuilder(); for (File f : recentFiles) { sb.append(',').append(f.getAbsolutePath()); @@ -419,7 +392,7 @@ public class MainDesignerController implements Initializable, SettingsOwner { } - private void setRecentFiles(String files) { + public void setRecentFiles(String files) { List fileNames = Arrays.asList(files.split(",")); Collections.reverse(fileNames); for (String fileName : fileNames) { @@ -429,34 +402,42 @@ public class MainDesignerController implements Initializable, SettingsOwner { } - private String isMaximized() { - return Boolean.toString(designerRoot.getMainStage().isMaximized()); + @PersistentProperty + public boolean isMaximized() { + return designerRoot.getMainStage().isMaximized(); } - private void setIsMaximized(String bool) { - boolean b = Boolean.parseBoolean(bool); + public void setMaximized(boolean b) { designerRoot.getMainStage().setMaximized(!b); // trigger change listener anyway designerRoot.getMainStage().setMaximized(b); } - private String getBottomExpandedTab() { - return (bottomTabsToggle.isSelected() ? "expanded:" : "collapsed:") - + bottomTabPane.getSelectionModel().getSelectedIndex(); + @PersistentProperty + public boolean isBottomTabExpanded() { + return bottomTabsToggle.isSelected(); } - private void setBottomExpandedTab(String id) { - String[] info = id.split(":"); - bottomTabsToggle.setSelected("expanded".equals(info[0])); - bottomTabPane.getSelectionModel().select(Integer.parseInt(info[1])); + public void setBottomTabExpanded(boolean b) { + bottomTabsToggle.setSelected(b); } - public void invalidateAst() { - nodeInfoPanelController.invalidateInfo(); - xpathPanelController.invalidateResults(false); - sourceEditorController.clearNodeHighlight(); + @PersistentProperty + public int getBottomTabIndex() { + return bottomTabPane.getSelectionModel().getSelectedIndex(); + } + + + public void setBottomTabIndex(int i) { + bottomTabPane.getSelectionModel().select(i); + } + + + @Override + public List getChildrenSettingsNodes() { + return Arrays.asList(xpathPanelController, sourceEditorController); } } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java index 904162b826..0205fb36d5 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/NodeInfoPanelController.java @@ -9,6 +9,8 @@ import java.util.Collections; import java.util.Objects; import java.util.ResourceBundle; +import org.reactfx.EventStreams; + import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.xpath.Attribute; import net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator; @@ -16,6 +18,7 @@ import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.util.fxdesigner.model.MetricEvaluator; import net.sourceforge.pmd.util.fxdesigner.model.MetricResult; +import net.sourceforge.pmd.util.fxdesigner.util.controls.ScopeHierarchyTreeCell; import net.sourceforge.pmd.util.fxdesigner.util.controls.ScopeHierarchyTreeItem; import javafx.collections.FXCollections; @@ -58,7 +61,7 @@ public class NodeInfoPanelController implements Initializable { private MetricEvaluator metricEvaluator = new MetricEvaluator(); - NodeInfoPanelController(DesignerRoot root, MainDesignerController mainController) { + public NodeInfoPanelController(DesignerRoot root, MainDesignerController mainController) { this.designerRoot = root; parent = mainController; } @@ -66,11 +69,13 @@ public class NodeInfoPanelController implements Initializable { @Override public void initialize(URL location, ResourceBundle resources) { - scopeHierarchyTreeView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> { - if (newVal != null && newVal.getValue() instanceof NameDeclaration) { - parent.onNameDeclarationSelected((NameDeclaration) newVal.getValue()); - } - }); + EventStreams.valuesOf(scopeHierarchyTreeView.getSelectionModel().selectedItemProperty()) + .filter(Objects::nonNull) + .map(TreeItem::getValue) + .filterMap(o -> o instanceof NameDeclaration, o -> (NameDeclaration) o) + .subscribe(parent::onNameDeclarationSelected); + + scopeHierarchyTreeView.setCellFactory(view -> new ScopeHierarchyTreeCell(parent)); } @@ -93,7 +98,8 @@ public class NodeInfoPanelController implements Initializable { .filter(result -> !result.isNaN()) .count()); - + // TODO maybe a better way would be to build all the scope TreeItem hierarchy once + // and only expand the ascendants of the node. TreeItem rootScope = ScopeHierarchyTreeItem.buildAscendantHierarchy(node); scopeHierarchyTreeView.setRoot(rootScope); } @@ -134,7 +140,7 @@ public class NodeInfoPanelController implements Initializable { while (attributeAxisIterator.hasNext()) { Attribute attribute = attributeAxisIterator.next(); result.add(attribute.getName() + " = " - + ((attribute.getValue() != null) ? attribute.getStringValue() : "null")); + + ((attribute.getValue() != null) ? attribute.getStringValue() : "null")); } if (node instanceof TypeNode) { diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java index ae653a0b6a..c6c71c98a6 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/SourceEditorController.java @@ -5,32 +5,31 @@ package net.sourceforge.pmd.util.fxdesigner; import java.net.URL; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.fxmisc.richtext.LineNumberFactory; +import org.reactfx.EventStreams; +import org.reactfx.value.Val; +import org.reactfx.value.Var; -import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.util.fxdesigner.model.ASTManager; import net.sourceforge.pmd.util.fxdesigner.model.ParseAbortedException; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty; import net.sourceforge.pmd.util.fxdesigner.util.codearea.AvailableSyntaxHighlighters; import net.sourceforge.pmd.util.fxdesigner.util.codearea.CustomCodeArea; import net.sourceforge.pmd.util.fxdesigner.util.codearea.SyntaxHighlighter; import net.sourceforge.pmd.util.fxdesigner.util.controls.ASTTreeItem; -import net.sourceforge.pmd.util.fxdesigner.util.settings.AppSetting; -import net.sourceforge.pmd.util.fxdesigner.util.settings.SettingsOwner; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; @@ -56,7 +55,7 @@ public class SourceEditorController implements Initializable, SettingsOwner { private TreeView astTreeView; @FXML private CustomCodeArea codeEditorArea; - private BooleanProperty isSyntaxHighlightingEnabled = new SimpleBooleanProperty(true); + private ASTManager astManager; @@ -69,51 +68,32 @@ public class SourceEditorController implements Initializable, SettingsOwner { @Override public void initialize(URL location, ResourceBundle resources) { - initializeSyntaxHighlighting(); - initializeASTTreeView(); + languageVersionProperty().values() + .filterMap(Objects::nonNull, LanguageVersion::getLanguage) + .distinct() + .subscribe(this::updateSyntaxHighlighter); + + EventStreams.valuesOf(astTreeView.getSelectionModel().selectedItemProperty()) + .filterMap(Objects::nonNull, TreeItem::getValue) + .subscribe(parent::onNodeItemSelected); codeEditorArea.setParagraphGraphicFactory(LineNumberFactory.get(codeEditorArea)); } - private void initializeSyntaxHighlighting() { - - isSyntaxHighlightingEnabled.bind(codeEditorArea.syntaxHighlightingEnabledProperty()); - - isSyntaxHighlightingEnabled.addListener(((observable, wasEnabled, isEnabled) -> { - if (!wasEnabled && isEnabled) { - updateSyntaxHighlighter(); - } else if (!isEnabled) { - codeEditorArea.disableSyntaxHighlighting(); - } - })); - - astManager.languageVersionProperty().addListener((obs, oldVal, newVal) -> { - if (newVal != null && !newVal.equals(oldVal)) { - updateSyntaxHighlighter(); - } - }); - - } - - - private void initializeASTTreeView() { - - astTreeView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> { - if (newVal != null && newVal.getValue() != null) { - parent.onNodeItemSelected(newVal.getValue()); - } - }); - } - - /** * Refreshes the AST. */ public void refreshAST() { - String source = codeEditorArea.getText(); - Node previous = astManager.compilationUnitProperty().get(); + String source = getText(); + Node previous = getCompilationUnit(); Node current; + + if (StringUtils.isBlank(source)) { + astTreeView.setRoot(null); + return; + } + try { current = astManager.updateCompilationUnit(source); } catch (ParseAbortedException e) { @@ -122,11 +102,8 @@ public class SourceEditorController implements Initializable, SettingsOwner { } if (previous != current) { parent.invalidateAst(); + setUpToDateCompilationUnit(current); } - - setUpToDateCompilationUnit(current); - codeEditorArea.clearPrimaryStyleLayer(); - } @@ -142,10 +119,11 @@ public class SourceEditorController implements Initializable, SettingsOwner { } - private void updateSyntaxHighlighter() { - SyntaxHighlighter computer = AvailableSyntaxHighlighters.getComputerForLanguage(astManager.getLanguageVersion().getLanguage()); - if (computer != null) { - codeEditorArea.setSyntaxHighlightingEnabled(computer); + private void updateSyntaxHighlighter(Language language) { + Optional highlighter = AvailableSyntaxHighlighters.getHighlighterForLanguage(language); + + if (highlighter.isPresent()) { + codeEditorArea.setSyntaxHighlightingEnabled(highlighter.get()); } else { codeEditorArea.disableSyntaxHighlighting(); } @@ -162,7 +140,7 @@ public class SourceEditorController implements Initializable, SettingsOwner { } - private void highlightNodes(Collection nodes, Set cssClasses) { + private void highlightNodes(Collection nodes, Set cssClasses) { for (Node node : nodes) { if (codeEditorArea.isInRange(node)) { codeEditorArea.styleCss(node, cssClasses); @@ -176,7 +154,7 @@ public class SourceEditorController implements Initializable, SettingsOwner { } - public void highlightNodesSecondary(Collection nodes) { + public void highlightNodesSecondary(Collection nodes) { highlightNodes(nodes, Collections.singleton("secondary-highlight")); } @@ -202,44 +180,45 @@ public class SourceEditorController implements Initializable, SettingsOwner { codeEditorArea.requestFollowCaret(); } - - public boolean isSyntaxHighlightingEnabled() { - return isSyntaxHighlightingEnabled.get(); - } - - - public ReadOnlyBooleanProperty syntaxHighlightingEnabledProperty() { - return isSyntaxHighlightingEnabled; - } - - - public ObservableValue sourceCodeProperty() { - return codeEditorArea.textProperty(); - } - - + @PersistentProperty public LanguageVersion getLanguageVersion() { return astManager.getLanguageVersion(); } - public ObjectProperty languageVersionProperty() { + public void setLanguageVersion(LanguageVersion version) { + astManager.setLanguageVersion(version); + } + + + public Var languageVersionProperty() { return astManager.languageVersionProperty(); } public Node getCompilationUnit() { - return astManager.updateCompilationUnit(); + return astManager.getCompilationUnit(); } - public ObjectProperty compilationUnitProperty() { + public Val compilationUnitProperty() { return astManager.compilationUnitProperty(); } - public void replaceText(String source) { - codeEditorArea.replaceText(source); + @PersistentProperty + public String getText() { + return codeEditorArea.getText(); + } + + + public void setText(String expression) { + codeEditorArea.replaceText(expression); + } + + + public Val textProperty() { + return Val.wrap(codeEditorArea.textProperty()); } @@ -247,26 +226,4 @@ public class SourceEditorController implements Initializable, SettingsOwner { codeEditorArea.clearStyleLayers(); } - - @Override - public List getSettings() { - List settings = new ArrayList<>(); - settings.add(new AppSetting("langVersion", () -> getLanguageVersion().getTerseName(), - this::restoreLanguageVersion)); - - settings.add(new AppSetting("code", () -> codeEditorArea.getText(), - e -> codeEditorArea.replaceText(e))); - - return settings; - } - - - private void restoreLanguageVersion(String name) { - LanguageVersion version = LanguageRegistry.findLanguageVersionByTerseName(name); - if (version != null) { - astManager.languageVersionProperty().setValue(version); - } - } - - } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/XPathPanelController.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/XPathPanelController.java index f1beafec32..5c7a275ce0 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/XPathPanelController.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/XPathPanelController.java @@ -4,41 +4,54 @@ package net.sourceforge.pmd.util.fxdesigner; +import java.io.IOException; import java.net.URL; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.ResourceBundle; import org.apache.commons.lang3.StringUtils; +import org.reactfx.EventStreams; +import org.reactfx.value.Val; +import org.reactfx.value.Var; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery; import net.sourceforge.pmd.util.fxdesigner.model.LogEntry; import net.sourceforge.pmd.util.fxdesigner.model.LogEntry.Category; +import net.sourceforge.pmd.util.fxdesigner.model.ObservableXPathRuleBuilder; import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException; import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluator; +import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty; import net.sourceforge.pmd.util.fxdesigner.util.codearea.CustomCodeArea; import net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.XPathSyntaxHighlighter; -import net.sourceforge.pmd.util.fxdesigner.util.settings.AppSetting; -import net.sourceforge.pmd.util.fxdesigner.util.settings.SettingsOwner; +import net.sourceforge.pmd.util.fxdesigner.util.controls.PropertyTableView; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ObservableValue; +import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; +import javafx.scene.Parent; +import javafx.scene.Scene; import javafx.scene.control.ChoiceBox; import javafx.scene.control.ListView; import javafx.scene.control.TitledPane; -import javafx.util.StringConverter; +import javafx.stage.Modality; +import javafx.stage.Stage; /** * XPath panel controller. * * @author Clément Fournier + * @see ExportXPathWizardController * @since 6.0.0 */ public class XPathPanelController implements Initializable, SettingsOwner { @@ -48,19 +61,26 @@ public class XPathPanelController implements Initializable, SettingsOwner { private final XPathEvaluator xpathEvaluator = new XPathEvaluator(); + private final ObservableXPathRuleBuilder ruleBuilder = new ObservableXPathRuleBuilder(); + + + @FXML + private PropertyTableView propertyView; @FXML private CustomCodeArea xpathExpressionArea; @FXML private TitledPane violationsTitledPane; @FXML private ListView xpathResultListView; - + // Actually a child of the main view toolbar, but this controller is responsible for it private ChoiceBox xpathVersionChoiceBox; - XPathPanelController(DesignerRoot owner, MainDesignerController mainController) { + public XPathPanelController(DesignerRoot owner, MainDesignerController mainController) { this.designerRoot = owner; parent = mainController; + + getRuleBuilder().setClazz(XPathRule.class); } @@ -68,12 +88,24 @@ public class XPathPanelController implements Initializable, SettingsOwner { public void initialize(URL location, ResourceBundle resources) { xpathExpressionArea.setSyntaxHighlightingEnabled(new XPathSyntaxHighlighter()); - xpathResultListView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> { - if (newVal != null) { - parent.onNodeItemSelected(newVal); - } - }); + EventStreams.valuesOf(xpathResultListView.getSelectionModel().selectedItemProperty()) + .filter(Objects::nonNull) + .subscribe(parent::onNodeItemSelected); + Platform.runLater(this::bindToParent); + } + + + // Binds the underlying rule parameters to the parent UI, disconnecting it from the wizard if need be + private void bindToParent() { + DesignerUtil.rewire(getRuleBuilder().languageProperty(), + Val.map(parent.languageVersionProperty(), LanguageVersion::getLanguage)); + + DesignerUtil.rewire(getRuleBuilder().xpathVersionProperty(), parent.xpathVersionProperty()); + DesignerUtil.rewire(getRuleBuilder().xpathExpressionProperty(), xpathExpressionProperty()); + + DesignerUtil.rewire(getRuleBuilder().rulePropertiesProperty(), + propertyView.rulePropertiesProperty(), propertyView::setRuleProperties); } @@ -85,20 +117,8 @@ public class XPathPanelController implements Initializable, SettingsOwner { versionItems.add(XPathRuleQuery.XPATH_1_0_COMPATIBILITY); versionItems.add(XPathRuleQuery.XPATH_2_0); - xpathVersionChoiceBox.getSelectionModel().select(xpathEvaluator.xpathVersionProperty().get()); - - choiceBox.setConverter(new StringConverter() { - @Override - public String toString(String object) { - return "XPath " + object; - } - - - @Override - public String fromString(String string) { - return string.substring(6); - } - }); + xpathVersionChoiceBox.getSelectionModel().select(XPathRuleQuery.XPATH_2_0); + choiceBox.setConverter(DesignerUtil.stringConverter(s -> "XPath " + s, s -> s.substring(6))); } @@ -111,7 +131,7 @@ public class XPathPanelController implements Initializable, SettingsOwner { public void evaluateXPath(Node compilationUnit, LanguageVersion version) { try { - String xpath = xpathExpressionArea.getText(); + String xpath = getXpathExpression(); if (StringUtils.isBlank(xpath)) { xpathResultListView.getItems().clear(); @@ -119,7 +139,11 @@ public class XPathPanelController implements Initializable, SettingsOwner { } ObservableList results - = FXCollections.observableArrayList(xpathEvaluator.evaluateQuery(compilationUnit, version, xpath)); + = FXCollections.observableArrayList(xpathEvaluator.evaluateQuery(compilationUnit, + version, + getXpathVersion(), + xpath, + ruleBuilder.getRuleProperties())); xpathResultListView.setItems(results); violationsTitledPane.setText("Matched nodes\t(" + results.size() + ")"); } catch (XPathEvaluationException e) { @@ -138,32 +162,71 @@ public class XPathPanelController implements Initializable, SettingsOwner { } + public void showExportXPathToRuleWizard() throws IOException { + // doesn't work for some reason + ExportXPathWizardController wizard + = new ExportXPathWizardController(xpathExpressionProperty()); + + FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/xpath-export-wizard.fxml")); + loader.setController(wizard); + + final Stage dialog = new Stage(); + dialog.initOwner(designerRoot.getMainStage()); + dialog.setOnCloseRequest(e -> wizard.shutdown()); + dialog.initModality(Modality.WINDOW_MODAL); + + Parent root = loader.load(); + Scene scene = new Scene(root); + //stage.setTitle("PMD Rule Designer (v " + PMD.VERSION + ')'); + dialog.setScene(scene); + dialog.show(); + } + + public void shutdown() { xpathExpressionArea.disableSyntaxHighlighting(); } - public StringProperty xpathVersionProperty() { - return xpathEvaluator.xpathVersionProperty(); + @PersistentProperty + public String getXpathExpression() { + return xpathExpressionArea.getText(); + } + + + public void setXpathExpression(String expression) { + xpathExpressionArea.replaceText(expression); + } + + + public Val xpathExpressionProperty() { + return Val.wrap(xpathExpressionArea.textProperty()); + } + + + @PersistentProperty + public String getXpathVersion() { + return getRuleBuilder().getXpathVersion(); + } + + + public void setXpathVersion(String xpathVersion) { + getRuleBuilder().setXpathVersion(xpathVersion); + } + + + public Var xpathVersionProperty() { + return getRuleBuilder().xpathVersionProperty(); + } + + + private ObservableXPathRuleBuilder getRuleBuilder() { + return ruleBuilder; } @Override - public List getSettings() { - List settings = new ArrayList<>(); - settings.add(new AppSetting("xpathVersion", () -> xpathEvaluator.xpathVersionProperty().getValue(), - v -> { - if (!"".equals(v)) { - xpathEvaluator.xpathVersionProperty().setValue(v); - } - })); - settings.add(new AppSetting("xpathCode", () -> xpathExpressionArea.getText(), (v) -> xpathExpressionArea.replaceText(v))); - - return settings; - } - - - public ObservableValue xpathExpressionProperty() { - return xpathExpressionArea.textProperty(); + public List getChildrenSettingsNodes() { + return Collections.singletonList(getRuleBuilder()); } } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java index 0ede04da5f..39006ecc92 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ASTManager.java @@ -7,6 +7,8 @@ package net.sourceforge.pmd.util.fxdesigner.model; import java.io.StringReader; import org.apache.commons.lang3.StringUtils; +import org.reactfx.value.Val; +import org.reactfx.value.Var; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -16,9 +18,6 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.util.fxdesigner.DesignerRoot; import net.sourceforge.pmd.util.fxdesigner.model.LogEntry.Category; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; - /** * Main class of the model. Manages a compilation unit. @@ -41,15 +40,11 @@ public class ASTManager { /** * Latest computed compilation unit (only null before the first call to {@link #updateCompilationUnit(String)}) */ - private ObjectProperty compilationUnit = new SimpleObjectProperty<>(); + private Var compilationUnit = Var.newSimpleVar(null); /** * Selected language version. */ - private ObjectProperty languageVersion = new SimpleObjectProperty<>(); - - { - languageVersion.setValue(LanguageRegistry.findLanguageVersionByTerseName("java 8")); - } + private Var languageVersion = Var.newSimpleVar(LanguageRegistry.getDefaultLanguage().getDefaultVersion()); public ASTManager(DesignerRoot owner) { @@ -58,21 +53,26 @@ public class ASTManager { public LanguageVersion getLanguageVersion() { - return languageVersion.get(); + return languageVersion.getValue(); } - public ObjectProperty languageVersionProperty() { + public void setLanguageVersion(LanguageVersion version) { + languageVersion.setValue(version); + } + + + public Var languageVersionProperty() { return languageVersion; } - public Node updateCompilationUnit() { - return compilationUnit.get(); + public Node getCompilationUnit() { + return compilationUnit.getValue(); } - public ObjectProperty compilationUnitProperty() { + public Val compilationUnitProperty() { return compilationUnit; } @@ -81,14 +81,16 @@ public class ASTManager { * Refreshes the compilation unit given the current parameters of the model. * * @param source Source code + * * @throws ParseAbortedException if parsing fails and cannot recover */ public Node updateCompilationUnit(String source) throws ParseAbortedException { - if (compilationUnit.get() != null - && languageVersion.get().equals(lastLanguageVersion) && StringUtils.equals(source, lastValidSource)) { - return compilationUnit.get(); + if (compilationUnit.isPresent() + && getLanguageVersion().equals(lastLanguageVersion) + && StringUtils.equals(source, lastValidSource)) { + return getCompilationUnit(); } - LanguageVersionHandler languageVersionHandler = languageVersion.get().getLanguageVersionHandler(); + LanguageVersionHandler languageVersionHandler = getLanguageVersion().getLanguageVersionHandler(); Parser parser = languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions()); Node node; @@ -111,8 +113,8 @@ public class ASTManager { compilationUnit.setValue(node); lastValidSource = source; - lastLanguageVersion = languageVersion.get(); - return compilationUnit.get(); + lastLanguageVersion = getLanguageVersion(); + return getCompilationUnit(); } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableRuleBuilder.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableRuleBuilder.java new file mode 100644 index 0000000000..71eb8ed60b --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableRuleBuilder.java @@ -0,0 +1,358 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.model; + +import java.util.Optional; + +import org.reactfx.collection.LiveArrayList; +import org.reactfx.value.Var; + +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RulePriority; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.rules.RuleBuilder; +import net.sourceforge.pmd.util.fxdesigner.util.PropertyDescriptorSpec; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentSequence; + +import javafx.beans.property.ListProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + + +/** + * Holds info about a rule, and can build it to validate it. + * + * @author Clément Fournier + * @since 6.0.0 + */ +public class ObservableRuleBuilder implements SettingsOwner { + + private Var language = Var.newSimpleVar(LanguageRegistry.getDefaultLanguage()); + private Var name = Var.newSimpleVar(""); + private Var> clazz = Var.newSimpleVar(null); + + // doesn't contain the "xpath" and "version" properties for XPath rules + private ListProperty ruleProperties = new SimpleListProperty<>(FXCollections.observableArrayList(PropertyDescriptorSpec.extractor())); + private Var> examples = Var.newSimpleVar(new LiveArrayList<>()); + + private Var minimumVersion = Var.newSimpleVar(null); + private Var maximumVersion = Var.newSimpleVar(null); + + private Var since = Var.newSimpleVar(""); + + private Var message = Var.newSimpleVar(""); + private Var externalInfoUrl = Var.newSimpleVar(""); + private Var description = Var.newSimpleVar(""); + + private Var priority = Var.newSimpleVar(RulePriority.MEDIUM); + private Var deprecated = Var.newSimpleVar(false); + private Var usesDfa = Var.newSimpleVar(false); + private Var usesMultifile = Var.newSimpleVar(false); + private Var usesTypeResolution = Var.newSimpleVar(false); + + + public Language getLanguage() { + return language.getValue(); + } + + + public void setLanguage(Language language) { + this.language.setValue(language); + } + + + public Var languageProperty() { + return language; + } + + + @PersistentProperty + public String getName() { + return name.getValue(); + } + + + public void setName(String name) { + this.name.setValue(name); + } + + + public Var nameProperty() { + return name; + } + + + @PersistentProperty + public Class getClazz() { + return clazz.getValue(); + } + + + public void setClazz(Class clazz) { + this.clazz.setValue(clazz); + } + + + public Var> clazzProperty() { + return clazz; + } + + + @PersistentSequence + public ObservableList getRuleProperties() { + return ruleProperties.getValue(); + } + + + public void setRuleProperties(ObservableList ruleProperties) { + this.ruleProperties.setValue(ruleProperties); + } + + + public ListProperty rulePropertiesProperty() { + return ruleProperties; + } + + + public LanguageVersion getMinimumVersion() { + return minimumVersion.getValue(); + } + + + public void setMinimumVersion(LanguageVersion minimumVersion) { + this.minimumVersion.setValue(minimumVersion); + } + + + public Var minimumVersionProperty() { + return minimumVersion; + } + + + public LanguageVersion getMaximumVersion() { + return maximumVersion.getValue(); + } + + + public void setMaximumVersion(LanguageVersion maximumVersion) { + this.maximumVersion.setValue(maximumVersion); + } + + + public Var maximumVersionProperty() { + return maximumVersion; + } + + + @PersistentProperty + public String getSince() { + return since.getValue(); + } + + + public void setSince(String since) { + this.since.setValue(since); + } + + + public Var sinceProperty() { + return since; + } + + + @PersistentProperty + public String getMessage() { + return message.getValue(); + } + + + public void setMessage(String message) { + this.message.setValue(message); + } + + + public Var messageProperty() { + return message; + } + + + @PersistentProperty + public String getExternalInfoUrl() { + return externalInfoUrl.getValue(); + } + + + public void setExternalInfoUrl(String externalInfoUrl) { + this.externalInfoUrl.setValue(externalInfoUrl); + } + + + public Var externalInfoUrlProperty() { + return externalInfoUrl; + } + + + @PersistentProperty + public String getDescription() { + return description.getValue(); + } + + + public void setDescription(String description) { + this.description.setValue(description); + } + + + public Var descriptionProperty() { + return description; + } + + + public Var> getExamples() { + return examples; + } + + + public void setExamples(ObservableList examples) { + this.examples.setValue(examples); + } + + + @PersistentProperty + public RulePriority getPriority() { + return priority.getValue(); + } + + + public void setPriority(RulePriority priority) { + this.priority.setValue(priority); + } + + + public Var priorityProperty() { + return priority; + } + + + public boolean isDeprecated() { + return deprecated.getValue(); + } + + + public void setDeprecated(boolean deprecated) { + this.deprecated.setValue(deprecated); + } + + + public Var deprecatedProperty() { + return deprecated; + } + + + public boolean isUsesDfa() { + return usesDfa.getValue(); + } + + + public void setUsesDfa(boolean usesDfa) { + this.usesDfa.setValue(usesDfa); + } + + + public Var usesDfaProperty() { + return usesDfa; + } + + + public boolean isUsesMultifile() { + return usesMultifile.getValue(); + } + + + public void setUsesMultifile(boolean usesMultifile) { + this.usesMultifile.setValue(usesMultifile); + } + + + public Var usesMultifileProperty() { + return usesMultifile; + } + + + public boolean getUsesTypeResolution() { + return usesTypeResolution.getValue(); + } + + + public void setUsesTypeResolution(boolean usesTypeResolution) { + this.usesTypeResolution.setValue(usesTypeResolution); + } + + + public Var usesTypeResolutionProperty() { + return usesTypeResolution; + } + + + /** + * Returns true if the parameters of the rule are consistent and the rule can be built. + * + * @return whether the rule can be built + */ + public boolean canBuild() { + try { + build(); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + + /** + * Builds the rule. + * + * @return the built rule. + * + * @throws IllegalArgumentException if parameters are incorrect + */ + public Optional build() throws IllegalArgumentException { + + try { + RuleBuilder builder = new RuleBuilder(name.getValue(), + clazz.getValue().getCanonicalName(), + language.getValue().getTerseName()); + + builder.minimumLanguageVersion(minimumVersion.getValue().getTerseName()); + builder.maximumLanguageVersion(maximumVersion.getValue().getTerseName()); + + builder.message(message.getValue()); + builder.since(since.getValue()); + builder.externalInfoUrl(externalInfoUrl.getValue()); + builder.description(description.getValue()); + builder.priority(priority.getValue().getPriority()); + + builder.setDeprecated(deprecated.getValue()); + builder.usesDFA(usesDfa.getValue()); + builder.usesTyperesolution(usesTypeResolution.getValue()); + builder.usesMultifile(usesMultifile.getValue()); + + ruleProperties.getValue().stream().map(PropertyDescriptorSpec::build).forEach(builder::defineProperty); + examples.getValue().forEach(builder::addExample); + + return Optional.of(builder.build()); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { + e.printStackTrace(); + return Optional.empty(); + } + + } + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableXPathRuleBuilder.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableXPathRuleBuilder.java new file mode 100644 index 0000000000..a3a694d724 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/ObservableXPathRuleBuilder.java @@ -0,0 +1,57 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.model; + +import java.util.Optional; + +import org.reactfx.value.Var; + +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil; + + +/** + * Specialises rule builders for XPath rules. + * + * @author Clément Fournier + * @since 6.0.0 + */ +public class ObservableXPathRuleBuilder extends ObservableRuleBuilder { + + + private final Var xpathVersion = Var.newSimpleVar(DesignerUtil.defaultXPathVersion()); + private final Var xpathExpression = Var.newSimpleVar(""); + + + public String getXpathVersion() { + return xpathVersion.getValue(); + } + + + public void setXpathVersion(String xpathVersion) { + this.xpathVersion.setValue(xpathVersion); + } + + + public Var xpathVersionProperty() { + return xpathVersion; + } + + + public String getXpathExpression() { + return xpathExpression.getValue(); + } + + + public Var xpathExpressionProperty() { + return xpathExpression; + } + + + @Override + public Optional build() throws IllegalArgumentException { + return super.build(); //TODO + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/XPathEvaluator.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/XPathEvaluator.java index 6b02576313..37e838c4cc 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/XPathEvaluator.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/model/XPathEvaluator.java @@ -17,10 +17,8 @@ import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.XPathRule; -import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery; +import net.sourceforge.pmd.util.fxdesigner.util.PropertyDescriptorSpec; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; /** * Evaluates XPath expressions. @@ -31,31 +29,22 @@ import javafx.beans.property.StringProperty; public class XPathEvaluator { - private final StringProperty xpathVersion = new SimpleStringProperty(XPathRuleQuery.XPATH_2_0); - - - public String getXpathVersion() { - return xpathVersion.get(); - } - - - public StringProperty xpathVersionProperty() { - return xpathVersion; - } - - /** * Evaluates an XPath query on the compilation unit. * * @param compilationUnit AST root * @param languageVersion language version - * @param xpathQuery query + * @param xpathVersion XPath version + * @param xpathQuery XPath query + * @param properties Properties of the rule * * @throws XPathEvaluationException if there was an error during the evaluation. The cause is preserved */ public List evaluateQuery(Node compilationUnit, LanguageVersion languageVersion, - String xpathQuery) throws XPathEvaluationException { + String xpathVersion, + String xpathQuery, + List properties) throws XPathEvaluationException { if (StringUtils.isBlank(xpathQuery)) { return Collections.emptyList(); @@ -71,11 +60,15 @@ public class XPathEvaluator { } }; + xpathRule.setMessage(""); xpathRule.setLanguage(languageVersion.getLanguage()); xpathRule.setXPath(xpathQuery); - xpathRule.setVersion(xpathVersion.get()); + xpathRule.setVersion(xpathVersion); + properties.stream() + .map(PropertyDescriptorSpec::build) + .forEach(xpathRule::definePropertyDescriptor); final RuleSet ruleSet = new RuleSetFactory().createSingleRuleRuleSet(xpathRule); @@ -94,6 +87,4 @@ public class XPathEvaluator { throw new XPathEvaluationException(e); } } - - } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java index 3f2af1dd00..ed48f91ef0 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/DesignerUtil.java @@ -5,20 +5,27 @@ package net.sourceforge.pmd.util.fxdesigner.util; import java.io.File; +import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.LanguageVersionHandler; import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery; + +import javafx.beans.property.Property; +import javafx.beans.value.ObservableValue; +import javafx.util.StringConverter; /** @@ -29,10 +36,10 @@ public class DesignerUtil { private static final Path PMD_SETTINGS_DIR = Paths.get(System.getProperty("user.home"), ".pmd"); - private static final File DESIGNER_SETTINGS_FILE = PMD_SETTINGS_DIR.resolve("pmd_new_designer.xml").toFile(); + private static final File DESIGNER_SETTINGS_FILE = PMD_SETTINGS_DIR.resolve("designer.xml").toFile(); - private static LanguageVersion[] supportedLanguageVersions; + private static List supportedLanguageVersions; private static Map extensionsToLanguage; @@ -41,28 +48,25 @@ public class DesignerUtil { } - private static Map getExtensionsToLanguageMap() { - Map result = new HashMap<>(); - Arrays.stream(getSupportedLanguageVersions()) - .map(LanguageVersion::getLanguage) - .distinct() - .collect(Collectors.toMap(Language::getExtensions, Language::getDefaultVersion)) - .forEach((key, value) -> key.forEach(ext -> result.put(ext, value))); - return result; + public static String defaultXPathVersion() { + return XPathRuleQuery.XPATH_2_0; } - public static LanguageVersion getLanguageVersionFromExtension(String filename) { - if (extensionsToLanguage == null) { - extensionsToLanguage = getExtensionsToLanguageMap(); - } + public static LanguageVersion defaultLanguageVersion() { + return LanguageRegistry.getDefaultLanguage().getDefaultVersion(); + } - if (filename.indexOf('.') > 0) { - String[] tokens = filename.split("\\."); - return extensionsToLanguage.get(tokens[tokens.length - 1]); - } - return null; + /** + * Gets the URL to an fxml file from its simple name. + * + * @param simpleName Simple name of the file, i.e. with no directory prefixes + * + * @return A URL to an fxml file + */ + public static URL getFxml(String simpleName) { + return DesignerUtil.class.getResource("/net/sourceforge/pmd/util/fxdesigner/fxml/" + simpleName); } @@ -76,20 +80,86 @@ public class DesignerUtil { } - public static LanguageVersion[] getSupportedLanguageVersions() { + public static StringConverter stringConverter(Function toString, Function fromString) { + return new StringConverter() { + @Override + public String toString(T object) { + return toString.apply(object); + } + + + @Override + public T fromString(String string) { + return fromString.apply(string); + } + }; + } + + + public static StringConverter languageVersionStringConverter() { + return DesignerUtil.stringConverter(LanguageVersion::getShortName, + s -> LanguageRegistry.findLanguageVersionByTerseName(s.toLowerCase())); + } + + + private static Map getExtensionsToLanguageMap() { + Map result = new HashMap<>(); + getSupportedLanguageVersions().stream() + .map(LanguageVersion::getLanguage) + .distinct() + .collect(Collectors.toMap(Language::getExtensions, + Language::getDefaultVersion)) + .forEach((key, value) -> key.forEach(ext -> result.put(ext, value))); + return result; + } + + + public static LanguageVersion getLanguageVersionFromExtension(String filename) { + if (extensionsToLanguage == null) { + extensionsToLanguage = getExtensionsToLanguageMap(); + } + + if (filename.indexOf('.') > 0) { + String[] tokens = filename.split("\\."); + return extensionsToLanguage.get(tokens[tokens.length - 1]); + } + return null; + } + + + public static List getSupportedLanguageVersions() { if (supportedLanguageVersions == null) { List languageVersions = new ArrayList<>(); for (LanguageVersion languageVersion : LanguageRegistry.findAllVersions()) { - LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler(); - if (languageVersionHandler != null) { - Parser parser = languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions()); - if (parser != null && parser.canParse()) { - languageVersions.add(languageVersion); - } - } + Optional.ofNullable(languageVersion.getLanguageVersionHandler()) + .map(handler -> handler.getParser(handler.getDefaultParserOptions())) + .filter(Parser::canParse) + .ifPresent(p -> languageVersions.add(languageVersion)); } - supportedLanguageVersions = languageVersions.toArray(new LanguageVersion[languageVersions.size()]); + supportedLanguageVersions = languageVersions; } return supportedLanguageVersions; } + + + /** + * Binds the underlying property to a source of values. The source property is also initialised using the setter. + * + * @param underlying The underlying property + * @param ui The property exposed to the user (the one in this wizard) + * @param setter Setter to initialise the UI value + * @param Type of values + */ + public static void rewire(Property underlying, ObservableValue ui, Consumer setter) { + setter.accept(underlying.getValue()); + underlying.unbind(); + underlying.bind(ui); // Bindings are garbage collected after the popup dies + } + + /** Like rewire, with no initialisation. */ + public static void rewire(Property underlying, ObservableValue source) { + underlying.unbind(); + underlying.bind(source); + } + } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/PropertyDescriptorSpec.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/PropertyDescriptorSpec.java new file mode 100644 index 0000000000..ae7cc0e4c7 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/PropertyDescriptorSpec.java @@ -0,0 +1,201 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util; + + +import java.util.HashMap; +import java.util.Map; + +import org.reactfx.value.Val; +import org.reactfx.value.Var; + +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.util.fxdesigner.util.beans.SettingsOwner; +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty; + +import javafx.beans.Observable; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.util.Callback; + + +/** + * Stores enough data to build a property descriptor, can be displayed within table views. + * + * @author Clément Fournier + * @since 6.0.0 + */ +public class PropertyDescriptorSpec implements SettingsOwner { + + private static final String DEFAULT_STRING = "TODO"; + + private final Val isNumerical; + private final Val isPackaged; + private final Val isMultivalue; + + private final Var typeId = Var.newSimpleVar(PropertyTypeId.STRING); + private final Var name = Var.newSimpleVar(DEFAULT_STRING); + private final Var value = Var.newSimpleVar(DEFAULT_STRING); + private final Var description = Var.newSimpleVar(DEFAULT_STRING); + + + public PropertyDescriptorSpec() { + isNumerical = typeId.map(PropertyTypeId::isPropertyNumeric); + isPackaged = typeId.map(PropertyTypeId::isPropertyPackaged); + isMultivalue = typeId.map(PropertyTypeId::isPropertyMultivalue); + } + + + public Boolean getIsNumerical() { + return isNumerical.getValue(); + } + + + public Val isNumericalProperty() { + return isNumerical; + } + + + public Boolean getIsPackaged() { + return isPackaged.getValue(); + } + + + public Val isPackagedProperty() { + return isPackaged; + } + + + public Boolean getIsMultivalue() { + return isMultivalue.getValue(); + } + + + public Val isMultivalueProperty() { + return isMultivalue; + } + + + @PersistentProperty + public String getDescription() { + return description.getValue(); + } + + + public void setDescription(String description) { + this.description.setValue(description); + } + + + public Var descriptionProperty() { + return description; + } + + + @PersistentProperty + public PropertyTypeId getTypeId() { + return typeId.getValue(); + } + + + public void setTypeId(PropertyTypeId typeId) { + this.typeId.setValue(typeId); + } + + + public Var typeIdProperty() { + return typeId; + } + + + @PersistentProperty + public String getName() { + return name.getValue(); + } + + + public void setName(String name) { + this.name.setValue(name); + } + + + public Var nameProperty() { + return name; + } + + + @PersistentProperty + public String getValue() { + return value.getValue(); + } + + + public void setValue(String value) { + this.value.setValue(value); + } + + + public Var valueProperty() { + return value; + } + + + /** + * Returns an xml string of this property definition. + * + * @return An xml string + */ + public String toXml() { + return String.format("", + getName(), getTypeId().getStringId(), getValue()); + } + + + @Override + public String toString() { + return toXml(); + } + + + /** + * Builds the descriptor. May throw IllegalArgumentException. + * + * @return the descriptor if it can be built + */ + public PropertyDescriptor build() { + PropertyDescriptorExternalBuilder externalBuilder = getTypeId().getFactory(); + Map values = new HashMap<>(); + values.put(PropertyDescriptorField.NAME, getName()); + values.put(PropertyDescriptorField.DEFAULT_VALUE, getValue()); + values.put(PropertyDescriptorField.DESCRIPTION, getDescription()); + + return externalBuilder.build(values); + } + + + /** + * Removes bindings from this property spec. + */ + public void unbind() { + typeIdProperty().unbind(); + nameProperty().unbind(); + descriptionProperty().unbind(); + valueProperty().unbind(); + } + + + /** Extractor for observable lists. */ + public static Callback extractor() { + return spec -> new Observable[]{spec.nameProperty(), spec.typeIdProperty(), spec.valueProperty()}; + } + + + public static ObservableList observableList() { + return FXCollections.observableArrayList(extractor()); + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNode.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNode.java new file mode 100644 index 0000000000..679dc8bb34 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNode.java @@ -0,0 +1,37 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +import java.util.Collections; +import java.util.List; + + +/** + * Represents a node in the settings model. The settings model is a + * tree of such nodes, mirroring the state hierarchy of the application. + * + *

Each node can be serialised to XML. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public abstract class BeanModelNode { + + /** Makes the children accept the visitor. */ + public void childrenAccept(BeanNodeVisitor visitor, T data) { + for (BeanModelNode child : getChildrenNodes()) { + child.accept(visitor, data); + } + } + + + /** Accepts a visitor. */ + protected abstract void accept(BeanNodeVisitor visitor, T data); + + + public List getChildrenNodes() { + return Collections.emptyList(); + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNodeSeq.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNodeSeq.java new file mode 100644 index 0000000000..c079b900d2 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanModelNodeSeq.java @@ -0,0 +1,76 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentSequence; + + +/** + * Represents an indexed list of nodes sharing the same type. + * This type of node is flagged with a {@link PersistentSequence}, + * which is applied to a getter of a collection. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public class BeanModelNodeSeq extends BeanModelNode { + + private final String propertyName; + private final List children = new ArrayList<>(); + + + public BeanModelNodeSeq(String name) { + this.propertyName = name; + } + + + public void addChild(T node) { + children.add(node); + } + + + /** Returns the elements of the sequence. */ + @Override + public List getChildrenNodes() { + return children; + } + + + /** Returns the name of the property that contains the collection. */ + public String getPropertyName() { + return propertyName; + } + + + protected void accept(BeanNodeVisitor visitor, U data) { + visitor.visit(this, data); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BeanModelNodeSeq that = (BeanModelNodeSeq) o; + return Objects.equals(propertyName, that.propertyName) + && Objects.equals(children, that.children); + } + + + @Override + public int hashCode() { + + return Objects.hash(propertyName, children); + } + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanNodeVisitor.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanNodeVisitor.java new file mode 100644 index 0000000000..82417391e5 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/BeanNodeVisitor.java @@ -0,0 +1,31 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +/** + * Implements a visitor pattern over bean nodes. Used to restore properties + * from a model and build an XML document to represent the model. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public abstract class BeanNodeVisitor { + + public void visit(BeanModelNode node, T data) { + node.childrenAccept(this, data); + } + + + public void visit(BeanModelNodeSeq node, T data) { + visit((BeanModelNode) node, data); + } + + + public void visit(SimpleBeanModelNode node, T data) { + visit((BeanModelNode) node, data); + } + + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/RestorePropertyVisitor.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/RestorePropertyVisitor.java new file mode 100644 index 0000000000..a06b19d222 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/RestorePropertyVisitor.java @@ -0,0 +1,120 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import org.apache.commons.beanutils.PropertyUtils; + +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty; + + +/** + * Visits a bean model and restores the properties described by the nodes + * into their respective settings owner. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public class RestorePropertyVisitor extends BeanNodeVisitor { + + + @Override + public void visit(SimpleBeanModelNode model, SettingsOwner target) { + if (model == null) { + return; // possibly it wasn't saved during the previous save cycle + } + + if (target == null) { + throw new IllegalArgumentException(); + } + + if (target.getClass() != model.getNodeType()) { + throw new IllegalArgumentException("Incorrect settings restoration target, expected " + + model.getNodeType() + ", actual " + target.getClass()); + } + + Map descriptors = Arrays.stream(PropertyUtils.getPropertyDescriptors(target)) + .filter(d -> d.getReadMethod().isAnnotationPresent(PersistentProperty.class)) + .collect(Collectors.toMap(PropertyDescriptor::getName, d -> d)); + + for (Entry saved : model.getSettingsValues().entrySet()) { + if (descriptors.containsKey(saved.getKey())) { + try { + PropertyUtils.setProperty(target, saved.getKey(), saved.getValue()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + } + + for (BeanModelNodeSeq seq : model.getSequenceProperties()) { + this.visit(seq, target); + } + + for (SettingsOwner child : target.getChildrenSettingsNodes()) { + model.getChildrenByType().get(child.getClass()).accept(this, child); + } + } + + + @Override + public void visit(BeanModelNodeSeq model, SettingsOwner target) { + if (model == null) { + return; // possibly it wasn't saved during the previous save cycle + } + + if (target == null) { + throw new IllegalArgumentException(); + } + + Collection container; + try { + @SuppressWarnings("unchecked") + Collection tmp = (Collection) PropertyUtils.getProperty(target, model.getPropertyName()); + container = tmp; + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + return; + } + + + Iterator existingItems = container.iterator(); + Class itemType = null; + for (SimpleBeanModelNode child : model.getChildrenNodes()) { + SettingsOwner item; + if (existingItems.hasNext()) { + item = existingItems.next(); + } else { + if (itemType == null) { + itemType = child.getNodeType(); + } + + try { + item = (SettingsOwner) itemType.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + continue; // try hard + } + } + + child.accept(this, item); + container.add(item); + } + + try { + PropertyUtils.setProperty(target, model.getPropertyName(), container); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + } + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsOwner.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsOwner.java new file mode 100644 index 0000000000..2eb05b141e --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsOwner.java @@ -0,0 +1,28 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +import java.util.Collections; +import java.util.List; + + +/** + * Marker interface for settings owners. Settings owners form a + * tree-like hierarchy, which is explored recursively to build + * a model of the settings to persist, under the form of a + * {@link SimpleBeanModelNode}. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public interface SettingsOwner { + + + /** Gets the children of this node in order. */ + default List getChildrenSettingsNodes() { + return Collections.emptyList(); + } + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsPersistenceUtil.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsPersistenceUtil.java new file mode 100644 index 0000000000..a47a9cabd8 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SettingsPersistenceUtil.java @@ -0,0 +1,250 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +import java.beans.PropertyDescriptor; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Optional; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.io.IOUtils; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import net.sourceforge.pmd.RulePriority; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.properties.PropertyTypeId; +import net.sourceforge.pmd.util.fxdesigner.util.beans.converters.LanguageVersionConverter; +import net.sourceforge.pmd.util.fxdesigner.util.beans.converters.PropertyTypeIdConverter; +import net.sourceforge.pmd.util.fxdesigner.util.beans.converters.RulePriorityConverter; + + +/** + * Utility methods to persist settings of the application. + * + * @author Clément Fournier + * @see SimpleBeanModelNode + * @see SettingsOwner + * @since 6.1.0 + */ +public class SettingsPersistenceUtil { + + static { + // register converters for custom types + ConvertUtils.register(new RulePriorityConverter(), RulePriority.class); + ConvertUtils.register(new PropertyTypeIdConverter(), PropertyTypeId.class); + ConvertUtils.register(new LanguageVersionConverter(), LanguageVersion.class); + } + + + private SettingsPersistenceUtil() { + } + + + /** + * Restores properties contained in the file into the given object. + * + * @param root Root of the hierarchy + * @param file Properties file + */ + public static void restoreProperties(SettingsOwner root, File file) { + Optional odoc = getDocument(file); + + odoc.flatMap(XmlFormatRevision::getSuitableReader) + .map(rev -> rev.xmlInterface) + .flatMap(xmlInterface -> odoc.flatMap(xmlInterface::parseXml)) + .ifPresent(n -> restoreSettings(root, n)); + } + + + /** + * Save properties of this object and descendants into the given file. + * + * @param root Root of the hierarchy + * @param file Properties file + */ + public static void persistProperties(SettingsOwner root, File file) throws IOException { + SimpleBeanModelNode node = SettingsPersistenceUtil.buildSettingsModel(root); + XmlFormatRevision.getLatest().xmlInterface.writeModelToXml(file, node); + } + + + /** + * Returns an XML document for the given file if it exists and can be parsed. + * + * @param file File to parse + */ + private static Optional getDocument(File file) { + InputStream stream = null; + if (file.exists()) { + try { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + stream = new FileInputStream(file); + Document document = builder.parse(stream); + return Optional.of(document); + } catch (SAXException | ParserConfigurationException | IOException e) { + e.printStackTrace(); + } finally { + IOUtils.closeQuietly(stream); + } + } + + return Optional.empty(); + } + + + /** + * Builds a settings model recursively for the given settings owner. + * The properties which have a getter tagged with {@link PersistentProperty} + * are retrieved for later serialisation. + * + * @param root The root of the settings owner hierarchy. + * + * @return The built model + */ + // test only + static SimpleBeanModelNode buildSettingsModel(SettingsOwner root) { + SimpleBeanModelNode node = new SimpleBeanModelNode(root.getClass()); + + for (PropertyDescriptor d : PropertyUtils.getPropertyDescriptors(root)) { + if (d.getReadMethod() == null) { + continue; + } + + try { + if (d.getReadMethod().isAnnotationPresent(PersistentSequence.class)) { + + Object val = d.getReadMethod().invoke(root); + if (!Collection.class.isAssignableFrom(val.getClass())) { + continue; + } + + @SuppressWarnings("unchecked") + Collection values = (Collection) val; + + BeanModelNodeSeq seq = new BeanModelNodeSeq<>(d.getName()); + + for (SettingsOwner item : values) { + seq.addChild(buildSettingsModel(item)); + } + + node.addChild(seq); + } else if (d.getReadMethod().isAnnotationPresent(PersistentProperty.class)) { + node.addProperty(d.getName(), d.getReadMethod().invoke(root), d.getPropertyType()); + } + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + + } + + for (SettingsOwner child : root.getChildrenSettingsNodes()) { + node.addChild(buildSettingsModel(child)); + } + + return node; + } + + + /** + * Restores the settings from the model into the target. Dual of + * {@link #buildSettingsModel(SettingsOwner)}. Traverses all the + * tree. + * + * @param target Object in which to restore the properties + * @param model The model + */ + // test only + static void restoreSettings(SettingsOwner target, BeanModelNode model) { + if (model == null) { + return; // possibly it wasn't saved during the previous save cycle + } + + if (target == null) { + throw new IllegalArgumentException(); + } + + model.accept(new RestorePropertyVisitor(), target); + } + + + /** Enumerates different formats for compatibility. */ + private enum XmlFormatRevision implements Comparable { + V1(new XmlInterfaceVersion1(1)); + + private final XmlInterface xmlInterface; + + + XmlFormatRevision(XmlInterface xmlI) { + this.xmlInterface = xmlI; + } + + + public static XmlFormatRevision getLatest() { + return Arrays.stream(values()).max(Comparator.comparingInt(x -> x.xmlInterface.getRevisionNumber())).get(); + } + + + /** + * Gets a handler capable of reading the given document. + * + * @param doc The revision number + * + * @return A handler, if it can be found + */ + public static Optional getSuitableReader(Document doc) { + return Arrays.stream(values()) + .filter(rev -> rev.xmlInterface.canParse(doc)) + .findAny(); + } + } + + + /** + * Tags the *getter* of a property as suitable for persistence. + * The property will be serialized and restored on startup, so + * it must have a setter. + * + *

Properties setters and getters must respect JavaBeans + * conventions. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface PersistentProperty { + } + + + /** + * Tags the getter of a collection for persistence. The collection + * elements must implement {@link SettingsOwner} and have a noargs + * constructor. This is a solution to the problem of serializing + * collections of items of arbitrary complexity. + * + *

When restoring such a property, we assume that the property + * already has a value, and we either update existing items with + * the properties or we instantiate new items if not enough are + * already available. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface PersistentSequence { + } + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SimpleBeanModelNode.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SimpleBeanModelNode.java new file mode 100644 index 0000000000..0b8e9b8b9c --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/SimpleBeanModelNode.java @@ -0,0 +1,145 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +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.Objects; +import java.util.Set; + +import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentSequence; + + +/** + * Represents a node in the settings owner tree, and stores the values of the properties that + * should be saved and restored. A node can have other nodes as children, in which case they are + * identified using their type at restore time. To persist the properties of multiple children with + * the same type, see {@link PersistentSequence} and {@link BeanModelNodeSeq}. + * + *

This intermediary representation decouples the XML representation from the business logic, + * allowing several parsers / serializers to coexist for different versions of the schema. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public class SimpleBeanModelNode extends BeanModelNode { + + + private final Class nodeType; + + private final Map propertyValues = new HashMap<>(); + private final Map> propertyTypes = new HashMap<>(); + private final Map, BeanModelNode> children = new HashMap<>(); + private final Set> sequenceProperties = new HashSet<>(); + + + public SimpleBeanModelNode(Class nodeType) { + this.nodeType = nodeType; + } + + + /** + * Add one more property with its value. + * + * @param propertyKey Unique name identifying the property. + * @param value Value + * @param type Type of the property + */ + public void addProperty(String propertyKey, Object value, Class type) { + propertyValues.put(propertyKey, value); + propertyTypes.put(propertyKey, type); + } + + + /** + * Add a sequence of nodes as a child of this node. + * + * @param seq Sequence of nodes + */ + public void addChild(BeanModelNodeSeq seq) { + sequenceProperties.add(seq); + } + + + /** + * Add a node to the children of this node. + * + * @param child Node + */ + public void addChild(SimpleBeanModelNode child) { + children.put(child.nodeType, child); + } + + + /** Returns a map of property names to their value. */ + public Map getSettingsValues() { + return Collections.unmodifiableMap(propertyValues); + } + + + /** Returns a map of property names to their type. */ + public Map> getSettingsTypes() { + return Collections.unmodifiableMap(propertyTypes); + } + + + /** Returns a map of children by type. */ + public Map, BeanModelNode> getChildrenByType() { + return Collections.unmodifiableMap(children); + } + + + @Override + public List getChildrenNodes() { + Set allChildren = new HashSet<>(children.values()); + allChildren.addAll(sequenceProperties); + return new ArrayList<>(allChildren); + } + + + /** Gets the sequences of nodes registered as children. */ + public Set> getSequenceProperties() { + return sequenceProperties; + } + + + /** Get the type of the settings owner represented by this node. */ + public Class getNodeType() { + return nodeType; + } + + + @Override + protected void accept(BeanNodeVisitor visitor, U data) { + visitor.visit(this, data); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SimpleBeanModelNode that = (SimpleBeanModelNode) o; + return Objects.equals(nodeType, that.nodeType) + && Objects.equals(propertyValues, that.propertyValues) + && Objects.equals(propertyTypes, that.propertyTypes) + && Objects.equals(children, that.children) + && Objects.equals(sequenceProperties, that.sequenceProperties); + } + + + @Override + public int hashCode() { + return Objects.hash(nodeType, propertyValues, propertyTypes, children, sequenceProperties); + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterface.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterface.java new file mode 100644 index 0000000000..4667b4386b --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterface.java @@ -0,0 +1,152 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Optional; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +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.w3c.dom.Document; +import org.w3c.dom.Element; + + +/** + * Represents a version of the Xml format used to store settings. The + * parser and serializer must understand each other, so they're kept + * together. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public abstract class XmlInterface { + + // modifying these will break compatibility + private static final String SCHEMA_MODEL_VERSION = "revision"; + private static final String SCHEMA_DOCUMENT_ELEMENT = "designer-settings"; + + private final int revisionNumber; + + + public XmlInterface(int rev) { + this.revisionNumber = rev; + } + + + public int getRevisionNumber() { + return revisionNumber; + } + + + /** + * Parses a XML document produced by {@link #writeModelToXml(File, SimpleBeanModelNode)} + * into a settings node. + * + * @param document The document to parse + * + * @return The root of the model hierarchy, or empty if the revision is not supported + */ + public final Optional parseXml(Document document) { + if (canParse(document)) { + Element rootNodeElement = (Element) document.getDocumentElement().getChildNodes().item(1); + return Optional.ofNullable(parseSettingsOwnerNode(rootNodeElement)); + } + return Optional.empty(); + } + + + /** + * Returns true if the document can be read by this object. + * + * @param document Document to test + */ + public boolean canParse(Document document) { + int docVersion = Integer.parseInt(document.getDocumentElement().getAttribute(SCHEMA_MODEL_VERSION)); + return docVersion == getRevisionNumber(); + } + + + private Document initDocument() throws IOException { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder; + try { + documentBuilder = documentBuilderFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new IOException("Failed to create settings document builder", e); + } + Document document = documentBuilder.newDocument(); + + Element settingsElement = document.createElement(SCHEMA_DOCUMENT_ELEMENT); + settingsElement.setAttribute(SCHEMA_MODEL_VERSION, "" + getRevisionNumber()); + document.appendChild(settingsElement); + return document; + } + + + /** Saves parameters to disk. */ + private void save(Document document, File outputFile) throws IOException { + try { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + + Source source = new DOMSource(document); + outputFile.getParentFile().mkdirs(); + Result result = new StreamResult(new FileWriter(outputFile)); + transformer.transform(source, result); + } catch (TransformerException e) { + throw new IOException("Failed to save settings", e); + } + } + + + /** + * Writes the model to a file. + * + * @param output The output file + * @param model The model to serialize + * + * @throws IOException If saving the settings failed + */ + public final void writeModelToXml(File output, SimpleBeanModelNode model) throws IOException { + Document document = initDocument(); + model.accept(getDocumentMakerVisitor(), document.getDocumentElement()); + save(document, output); + } + + + /** + * Parses a settings node and its descendants recursively. + * + * @param nodeElement Element to parse + * + * @return The model described by the element + */ + protected abstract SimpleBeanModelNode parseSettingsOwnerNode(Element nodeElement); + + + /** + * Gets a visitor which populates xml elements with corresponding nodes. + * + * @return A visitor + */ + protected abstract BeanNodeVisitor getDocumentMakerVisitor(); + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterfaceVersion1.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterfaceVersion1.java new file mode 100644 index 0000000000..cad6ebe6dc --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/XmlInterfaceVersion1.java @@ -0,0 +1,160 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import org.apache.commons.beanutils.ConvertUtils; +import org.apache.commons.lang3.ClassUtils; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +/** + * V0, really. + * + * @author Clément Fournier + * @since 6.1.0 + */ +public class XmlInterfaceVersion1 extends XmlInterface { + + + // names used in the Xml schema + private static final String SCHEMA_NODE_ELEMENT = "node"; + private static final String SCHEMA_NODESEQ_ELEMENT = "nodeseq"; + private static final String SCHEMA_NODE_CLASS_ATTRIBUTE = "class"; + private static final String SCHEMA_PROPERTY_ELEMENT = "property"; + private static final String SCHEMA_PROPERTY_NAME = "name"; + private static final String SCHEMA_PROPERTY_TYPE = "type"; + + + public XmlInterfaceVersion1(int revisionNumber) { + super(revisionNumber); + } + + + private List getChildrenByTagName(Element element, String tagName) { + NodeList children = element.getChildNodes(); + List elts = new ArrayList<>(); + for (int i = 0; i < children.getLength(); i++) { + if (children.item(i).getNodeType() == Node.ELEMENT_NODE && tagName.equals(children.item(i).getNodeName())) { + elts.add((Element) children.item(i)); + } + } + + return elts; + } + + + @Override + protected SimpleBeanModelNode parseSettingsOwnerNode(Element nodeElement) { + Class clazz; + try { + clazz = Class.forName(nodeElement.getAttribute(SCHEMA_NODE_CLASS_ATTRIBUTE)); + } catch (ClassNotFoundException e) { + return null; + } + + SimpleBeanModelNode node = new SimpleBeanModelNode(clazz); + + for (Element setting : getChildrenByTagName(nodeElement, SCHEMA_PROPERTY_ELEMENT)) { + parseSingleProperty(setting, node); + } + + for (Element child : getChildrenByTagName(nodeElement, SCHEMA_NODE_ELEMENT)) { + try { + if (node.getChildrenByType().get(Class.forName(child.getAttribute(SCHEMA_NODE_CLASS_ATTRIBUTE))) == null) { // FIXME + node.addChild(parseSettingsOwnerNode(child)); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + for (Element seq : getChildrenByTagName(nodeElement, SCHEMA_NODESEQ_ELEMENT)) { + parseNodeSeq(seq, node); + } + + return node; + } + + + private void parseSingleProperty(Element propertyElement, SimpleBeanModelNode owner) { + String typeName = propertyElement.getAttribute(SCHEMA_PROPERTY_TYPE); + String name = propertyElement.getAttribute(SCHEMA_PROPERTY_NAME); + Class type; + try { + type = ClassUtils.getClass(typeName); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return; + } + + ConvertUtils.convert(new Object()); + Object value = ConvertUtils.convert(propertyElement.getTextContent(), type); + + owner.addProperty(name, value, type); + } + + + private void parseNodeSeq(Element nodeSeq, SimpleBeanModelNode parent) { + BeanModelNodeSeq built = new BeanModelNodeSeq<>(nodeSeq.getAttribute(SCHEMA_PROPERTY_NAME)); + for (Element child : getChildrenByTagName(nodeSeq, SCHEMA_NODE_ELEMENT)) { + built.addChild(parseSettingsOwnerNode(child)); + } + parent.addChild(built); + } + + + @Override + protected BeanNodeVisitor getDocumentMakerVisitor() { + return new DocumentMakerVisitor(); + } + + + public static class DocumentMakerVisitor extends BeanNodeVisitor { + + + @Override + public void visit(SimpleBeanModelNode node, Element parent) { + Element nodeElement = parent.getOwnerDocument().createElement(SCHEMA_NODE_ELEMENT); + nodeElement.setAttribute(SCHEMA_NODE_CLASS_ATTRIBUTE, node.getNodeType().getCanonicalName()); + + for (Entry keyValue : node.getSettingsValues().entrySet()) { + // I don't think the API is intended to be used like that + // but ConvertUtils wouldn't use the convertToString methods + // defined in the converters otherwise. + // Even when a built-in converter is available, objects are + // still converted with Object::toString which fucks up the + // conversion... + String value = (String) ConvertUtils.lookup(keyValue.getValue().getClass()).convert(String.class, keyValue.getValue()); + if (value == null) { + continue; + } + + Element setting = parent.getOwnerDocument().createElement(SCHEMA_PROPERTY_ELEMENT); + setting.setAttribute(SCHEMA_PROPERTY_NAME, keyValue.getKey()); + setting.setAttribute(SCHEMA_PROPERTY_TYPE, node.getSettingsTypes().get(keyValue.getKey()).getCanonicalName()); + setting.appendChild(parent.getOwnerDocument().createCDATASection(value)); + nodeElement.appendChild(setting); + } + + parent.appendChild(nodeElement); + super.visit(node, nodeElement); + } + + + @Override + public void visit(BeanModelNodeSeq node, Element parent) { + Element nodeElement = parent.getOwnerDocument().createElement(SCHEMA_NODESEQ_ELEMENT); + nodeElement.setAttribute(SCHEMA_PROPERTY_NAME, node.getPropertyName()); + parent.appendChild(nodeElement); + super.visit(node, nodeElement); + } + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/LanguageVersionConverter.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/LanguageVersionConverter.java new file mode 100644 index 0000000000..58b584b54c --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/LanguageVersionConverter.java @@ -0,0 +1,35 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans.converters; + +import org.apache.commons.beanutils.converters.AbstractConverter; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; + + +/** + * @author Clément Fournier + * @since 6.1.0 + */ +public class LanguageVersionConverter extends AbstractConverter { + + @Override + protected String convertToString(Object value) { + return ((LanguageVersion) value).getTerseName(); + } + + + @Override + protected Object convertToType(Class aClass, Object o) { + return LanguageRegistry.findLanguageVersionByTerseName(o.toString()); + } + + + @Override + protected Class getDefaultType() { + return LanguageVersion.class; + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/PropertyTypeIdConverter.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/PropertyTypeIdConverter.java new file mode 100644 index 0000000000..0967cd816c --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/PropertyTypeIdConverter.java @@ -0,0 +1,34 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans.converters; + +import org.apache.commons.beanutils.converters.AbstractConverter; + +import net.sourceforge.pmd.properties.PropertyTypeId; + + +/** + * @author Clément Fournier + * @since 6.1.0 + */ +public class PropertyTypeIdConverter extends AbstractConverter { + + @Override + protected String convertToString(Object value) { + return ((PropertyTypeId) value).getStringId(); + } + + + @Override + protected Object convertToType(Class aClass, Object o) { + return PropertyTypeId.lookupMnemonic(o.toString()); + } + + + @Override + protected Class getDefaultType() { + return PropertyTypeId.class; + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/RulePriorityConverter.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/RulePriorityConverter.java new file mode 100644 index 0000000000..22bd3dc432 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/beans/converters/RulePriorityConverter.java @@ -0,0 +1,34 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.beans.converters; + +import org.apache.commons.beanutils.converters.AbstractConverter; + +import net.sourceforge.pmd.RulePriority; + + +/** + * @author Clément Fournier + * @since 6.1.0 + */ +public class RulePriorityConverter extends AbstractConverter { + + @Override + protected String convertToString(Object value) { + return Integer.toString(((RulePriority) value).getPriority()); + } + + + @Override + protected Object convertToType(Class aClass, Object o) { + return RulePriority.valueOf(Integer.parseInt(o.toString())); + } + + + @Override + protected Class getDefaultType() { + return RulePriority.class; + } +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/AvailableSyntaxHighlighters.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/AvailableSyntaxHighlighters.java index cc47be38ac..f5402a1d19 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/AvailableSyntaxHighlighters.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/AvailableSyntaxHighlighters.java @@ -13,6 +13,7 @@ import net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.Java import net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.XPathSyntaxHighlighter; import net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.XmlSyntaxHighlighter; + /** * Lists the available syntax highlighter engines by language. * @@ -40,21 +41,17 @@ public enum AvailableSyntaxHighlighters { /** - * Gets the highlighter for a language if available, otherwise returns null. + * Gets the highlighter for a language if available. * * @param language Language to look for * - * @return A highlighter if available, otherwise null + * @return A highlighter, if available */ - public static SyntaxHighlighter getComputerForLanguage(Language language) { - Optional found = Arrays.stream(AvailableSyntaxHighlighters.values()) - .filter(e -> e.language.equals(language.getTerseName())) - .findFirst(); - if (found.isPresent()) { - return found.get().engine; - } else { - return null; - } + public static Optional getHighlighterForLanguage(Language language) { + return Arrays.stream(AvailableSyntaxHighlighters.values()) + .filter(e -> e.language.equals(language.getTerseName())) + .findFirst() + .map(h -> h.engine); } } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/CustomCodeArea.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/CustomCodeArea.java index d6f4fb8638..1d69468540 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/CustomCodeArea.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/CustomCodeArea.java @@ -147,7 +147,7 @@ public class CustomCodeArea extends CodeArea { try { // refresh the highlighting. Task>> t = computeHighlightingAsync(this.getText()); - t.setOnSucceeded((e) -> { + t.setOnSucceeded(e -> { StyleLayer layer = styleContext.getLayer(SYNTAX_HIGHLIGHT_LAYER_ID); layer.reset(t.getValue()); this.paintCss(); diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/StyleLayer.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/StyleLayer.java index 3a68eb7d3a..2f8ed3a075 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/StyleLayer.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/StyleLayer.java @@ -73,7 +73,7 @@ class StyleLayer { Set cssClasses) { if (endLine > codeArea.getParagraphs().size() - || endLine == codeArea.getParagraphs().size() && endColumn > codeArea.getParagraph(endLine).length()) { + || endLine == codeArea.getParagraphs().size() && endColumn > codeArea.getParagraph(endLine - 1).length()) { throw new IllegalArgumentException("Cannot style, the region is out of bounds"); } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/syntaxhighlighting/XPathSyntaxHighlighter.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/syntaxhighlighting/XPathSyntaxHighlighter.java index 92c504a98a..96c0559a3e 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/syntaxhighlighting/XPathSyntaxHighlighter.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/codearea/syntaxhighlighting/XPathSyntaxHighlighter.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting; import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.BRACKET; import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.KEYWORD; +import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.NUMBER; import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.PAREN; import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.SINGLEL_COMMENT; import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.STRING; @@ -41,8 +42,9 @@ public class XPathSyntaxHighlighter extends SimpleRegexSyntaxHighlighter { .or(XPATH_PATH.css, "//?") .or(PAREN.css, "[()]") .or(BRACKET.css, "[\\[\\]]") + .or(NUMBER.css, "\\b\\d+\\b") .or(STRING.css, "('([^'\\\\]|\\\\.)*')|(\"([^\"\\\\]|\\\\.)*\")") - .or(SINGLEL_COMMENT.css, "\\(:.*:\\)") + .or(SINGLEL_COMMENT.css, "\\(:.*?:\\)") .create(); diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/PropertyTableView.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/PropertyTableView.java new file mode 100644 index 0000000000..79a2e4d608 --- /dev/null +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/PropertyTableView.java @@ -0,0 +1,217 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.fxdesigner.util.controls; + +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.util.Collections; +import java.util.function.Consumer; + +import org.reactfx.value.Var; + +import net.sourceforge.pmd.properties.PropertyTypeId; +import net.sourceforge.pmd.util.fxdesigner.EditPropertyDialogController; +import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil; +import net.sourceforge.pmd.util.fxdesigner.util.PropertyDescriptorSpec; + +import javafx.application.Platform; +import javafx.beans.property.ObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SeparatorMenuItem; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.ChoiceBoxTableCell; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.control.cell.TextFieldTableCell; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.util.StringConverter; + + +/** + * Controls a table view used to inspect and edit the properties of + * the rule being built. This component is made to be reused in several + * views. + *

+ * TODO: would be great to make it directly editable without compromising content validation + * + * @author Clément Fournier + * @since 6.0.0 + */ +public class PropertyTableView extends TableView { + + private TableColumn propertyNameColumn = new TableColumn<>("Name"); + private TableColumn propertyTypeColumn = new TableColumn<>("Type"); + private TableColumn propertyValueColumn = new TableColumn<>("Value"); + + private SoftReference editPropertyDialogCache; + + private Var> onEditCommit = Var.newSimpleVar(null); + + + public PropertyTableView() { + initialize(); + } + + + private void initialize() { + + this.getColumns().add(propertyNameColumn); + this.getColumns().add(propertyTypeColumn); + this.getColumns().add(propertyValueColumn); + this.setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY); + this.setTableMenuButtonVisible(true); + + ObservableList availableBuilders = FXCollections.observableArrayList(PropertyTypeId.typeIdsToConstants().values()); + Collections.sort(availableBuilders); + StringConverter converter = DesignerUtil.stringConverter(PropertyTypeId::getStringId, PropertyTypeId::lookupMnemonic); + propertyTypeColumn.setCellFactory(ChoiceBoxTableCell.forTableColumn(converter, availableBuilders)); + propertyNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + propertyValueColumn.setCellValueFactory(new PropertyValueFactory<>("value")); + propertyTypeColumn.setCellValueFactory(new PropertyValueFactory<>("typeId")); + + this.setPlaceholder(new Label("Right-click to add properties")); + + MenuItem editItem = new MenuItem("Edit..."); + editItem.setOnAction(e -> { + PropertyDescriptorSpec spec = this.getSelectionModel().getSelectedItem(); + if (spec != null) { + popEditPropertyDialog(spec); + } + }); + + MenuItem removeItem = new MenuItem("Remove"); + removeItem.setOnAction(e -> { + PropertyDescriptorSpec selected = this.getSelectionModel().getSelectedItem(); + if (selected != null) { + this.getItems().remove(selected); + } + }); + + MenuItem addItem = new MenuItem("Add property..."); + addItem.setOnAction(e -> onAddPropertyClicked()); + + ContextMenu fullMenu = new ContextMenu(); + fullMenu.getItems().addAll(editItem, removeItem, new SeparatorMenuItem(), addItem); + + // Reduced context menu, for when there are no properties or none is selected + MenuItem addItem2 = new MenuItem("Add property..."); + addItem2.setOnAction(e -> onAddPropertyClicked()); + + ContextMenu smallMenu = new ContextMenu(); + smallMenu.getItems().add(addItem2); + + this.addEventHandler(MouseEvent.MOUSE_CLICKED, t -> { + if (t.getButton() == MouseButton.SECONDARY + || t.getButton() == MouseButton.PRIMARY && t.getClickCount() > 1) { + if (this.getSelectionModel().getSelectedItem() != null) { + fullMenu.show(this, t.getScreenX(), t.getScreenY()); + } else { + smallMenu.show(this, t.getScreenX(), t.getScreenY()); + } + } + }); + + propertyNameColumn.setCellFactory(TextFieldTableCell.forTableColumn()); + propertyValueColumn.setCellFactory(TextFieldTableCell.forTableColumn()); + this.setEditable(false); + } + + + private void onAddPropertyClicked() { + PropertyDescriptorSpec spec = new PropertyDescriptorSpec(); + this.getItems().add(spec); + popEditPropertyDialog(spec); + } + + + /** + * Pops an edition dialog for the given descriptor spec. The dialog is cached and + * reused, so that it's parsed a minimal amount of time. + * + * @param edited The edited property descriptor + */ + private void popEditPropertyDialog(PropertyDescriptorSpec edited) { + if (editPropertyDialogCache == null || editPropertyDialogCache.get() == null) { + try { + editPropertyDialogCache = new SoftReference<>(createEditPropertyDialog()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + Stage dialog = editPropertyDialogCache.get(); + EditPropertyDialogController wizard = (EditPropertyDialogController) dialog.getUserData(); + Platform.runLater(() -> wizard.bindToDescriptor(edited, getRuleProperties())); + dialog.setOnHiding(e -> { + edited.unbind(); + onEditCommit.ifPresent(handler -> handler.accept(edited)); + }); + dialog.show(); + } + + + private Stage createEditPropertyDialog() throws IOException { + EditPropertyDialogController wizard = new EditPropertyDialogController(); + + FXMLLoader loader = new FXMLLoader(DesignerUtil.getFxml("edit-property-dialog.fxml")); + loader.setController(wizard); + + final Stage dialog = new Stage(); + dialog.initOwner(this.getScene().getWindow()); + dialog.initModality(Modality.WINDOW_MODAL); + dialog.initStyle(StageStyle.UNDECORATED); + + Parent root = loader.load(); + + Scene scene = new Scene(root); + dialog.setTitle("Edit property"); + dialog.setScene(scene); + dialog.setUserData(wizard); + return dialog; + } + + + public ObservableList getRuleProperties() { + return this.getItems(); + } + + + public void setRuleProperties(ObservableList ruleProperties) { + this.setItems(ruleProperties); + } + + + public ObjectProperty> rulePropertiesProperty() { + return this.itemsProperty(); + } + + + public Consumer getOnEditCommit() { + return onEditCommit.getValue(); + } + + + public Var> onEditCommitProperty() { + return onEditCommit; + } + + + public void setOnEditCommit(Consumer onEditCommit) { + this.onEditCommit.setValue(onEditCommit); + } + + +} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java index d965754c79..b2a259e3cf 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeCell.java @@ -10,10 +10,11 @@ import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.Scope; +import net.sourceforge.pmd.util.fxdesigner.MainDesignerController; import javafx.scene.control.TreeCell; -import javafx.scene.control.TreeView; -import javafx.util.Callback; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; /** @@ -24,6 +25,13 @@ import javafx.util.Callback; */ public class ScopeHierarchyTreeCell extends TreeCell { + private final MainDesignerController controller; + + + public ScopeHierarchyTreeCell(MainDesignerController controller) { + this.controller = controller; + } + @Override protected void updateItem(Object item, boolean empty) { super.updateItem(item, empty); @@ -35,6 +43,15 @@ public class ScopeHierarchyTreeCell extends TreeCell { setText(item instanceof Scope ? getTextForScope((Scope) item) : getTextForDeclaration((NameDeclaration) item)); } + + this.addEventHandler(MouseEvent.MOUSE_CLICKED, t -> { + if (t.getButton() == MouseButton.PRIMARY + && getTreeView().getSelectionModel().getSelectedItem() == item) { + if (item instanceof NameDeclaration) { + controller.onNodeItemSelected(((NameDeclaration) item).getNode()); + } + } + }); } @@ -71,9 +88,4 @@ public class ScopeHierarchyTreeCell extends TreeCell { return sb.toString(); } - - public static Callback, ScopeHierarchyTreeCell> callback() { - return param -> new ScopeHierarchyTreeCell(); - } - } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeItem.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeItem.java index 3b26d5ced2..72faf47399 100644 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeItem.java +++ b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/controls/ScopeHierarchyTreeItem.java @@ -32,6 +32,7 @@ public class ScopeHierarchyTreeItem extends TreeItem { * Gets the scope hierarchy of a node. * * @param node Node + * * @return Root of the tree */ public static ScopeHierarchyTreeItem buildAscendantHierarchy(Node node) { @@ -67,7 +68,9 @@ public class ScopeHierarchyTreeItem extends TreeItem { if (parent == null) { return scopeTreeNode; } else { - parent.getChildren().add(scopeTreeNode); + if (scopeTreeNode.getChildren().size() > 0) { // hides empty scopes + parent.getChildren().add(scopeTreeNode); + } return scopeTreeNode; } } @@ -79,5 +82,5 @@ public class ScopeHierarchyTreeItem extends TreeItem { } return null; } - + } diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/AppSetting.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/AppSetting.java deleted file mode 100644 index 4be4c527fe..0000000000 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/AppSetting.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.util.fxdesigner.util.settings; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -/** - * @author Clément Fournier - * @since 6.0.0 - */ -public class AppSetting { - - private final String keyName; - private final Supplier getValueFunction; - private final Consumer setValueFunction; - - - public AppSetting(String keyName, Supplier getValueFunction, - Consumer setValueFunction) { - this.keyName = keyName; - this.getValueFunction = getValueFunction; - this.setValueFunction = setValueFunction; - } - - - public String getValue() { - return getValueFunction.get(); - } - - - public void setValue(String value) { - setValueFunction.accept(value); - } - - - public String getKeyName() { - return keyName; - } -} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/SettingsOwner.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/SettingsOwner.java deleted file mode 100644 index 0fd67f3379..0000000000 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/SettingsOwner.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.util.fxdesigner.util.settings; - -import java.util.List; - -/** - * @author Clément Fournier - * @since 6.0.0 - */ -public interface SettingsOwner { - - - /** - * Gets the settings of this specific object. - * - * @return The settings - */ - List getSettings(); - - -} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsLoader.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsLoader.java deleted file mode 100644 index 5af9e225aa..0000000000 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsLoader.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.util.fxdesigner.util.settings; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.apache.commons.io.IOUtils; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - - -/** - * Loads settings stored in the format of {@link XMLSettingsSaver}. - * - * @author Clément Fournier - * @since 6.0.0 - */ -public class XMLSettingsLoader { - - private final File settingsFile; - - - public XMLSettingsLoader(File settingsPath) { - this.settingsFile = settingsPath; - } - - - private Set getSettingNodes(Document document) { - NodeList nodes = document.getElementsByTagName("setting"); - Set set = new HashSet<>(); - for (int i = 0; i < nodes.getLength(); i++) { - set.add((Element) nodes.item(i)); - } - - return set; - } - - - public Map getSettings() throws IOException { - InputStream stream = null; - try { - - - if (settingsFile.exists()) { - - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - stream = new FileInputStream(settingsFile); - Document document = builder.parse(stream); - - Set settings = getSettingNodes(document); - - return settings.stream() - .collect(Collectors.toMap((elt) -> elt.getAttribute("key"), - Node::getTextContent)); - - } - } catch (SAXException | ParserConfigurationException | IOException e) { - throw new IOException("Failed to load settings", e); - } finally { - IOUtils.closeQuietly(stream); - - } - - return Collections.emptyMap(); - } -} diff --git a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsSaver.java b/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsSaver.java deleted file mode 100644 index ba068a1348..0000000000 --- a/pmd-ui/src/main/java/net/sourceforge/pmd/util/fxdesigner/util/settings/XMLSettingsSaver.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.util.fxdesigner.util.settings; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -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.w3c.dom.Document; -import org.w3c.dom.Element; - - -/** - * Saves settings to disk as key-value pairs. This implementation stores them into an XML file. - * - * @author Clément Fournier - * @since 6.0.0 - */ -public class XMLSettingsSaver { - - private final File outputFile; - private Document document; - - - private XMLSettingsSaver(File output) throws IOException { - this.outputFile = output; - - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder documentBuilder; - try { - documentBuilder = documentBuilderFactory.newDocumentBuilder(); - } catch (ParserConfigurationException e) { - throw new IOException("Failed to create settings document builder", e); - } - document = documentBuilder.newDocument(); - - Element settingsElement = document.createElement("settings"); - document.appendChild(settingsElement); - } - - - /** - * Saves a key value pair. - */ - public XMLSettingsSaver put(String key, String value) { - Element settingElement = document.createElement("setting"); - settingElement.setAttribute("key", key); - settingElement.appendChild(document.createCDATASection(value)); - document.getDocumentElement().appendChild(settingElement); - - return this; - } - - - /** Saves parameters to disk. */ - public void save() throws IOException { - try { - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); - - Source source = new DOMSource(document); - outputFile.getParentFile().mkdirs(); - Result result = new StreamResult(new FileWriter(outputFile)); - transformer.transform(source, result); - } catch (TransformerException e) { - throw new IOException("Failed to save settings", e); - - } - } - - - /** - * Get an instance. - * - * @throws IOException if initialisation fails - */ - public static XMLSettingsSaver forFile(File file) throws IOException { - return new XMLSettingsSaver(file); - } - - -} diff --git a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/css/designer.css b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/css/designer.css index 6a025d44c0..0bff0a645e 100644 --- a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/css/designer.css +++ b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/css/designer.css @@ -24,6 +24,13 @@ -fx-padding: -1 0 0 0; } +.table-view .show-hide-columns-button { + -fx-background-color: derive(-fx-base, -10%); + -fx-border-color: derive(-fx-base, -20%); + -fx-border-style: none solid solid solid; + -fx-padding: -1 0 0 0; +} + .text-area { -fx-background-insets: 0; -fx-background-color: transparent, white, transparent, white; @@ -36,12 +43,10 @@ -fx-border-width: -1; } - .text-area .content { -fx-background-color: transparent, white, transparent, white; } - /**************/ /* Split pane */ /**************/ @@ -61,11 +66,6 @@ } -.bottom-pane-split-pane.split-pane > .split-pane-divider { - /*-fx-background-color: derive(-fx-base, -20%);*/ -} - - .secondary-panel { -fx-border-style: none; } @@ -76,7 +76,6 @@ -fx-min-width: 300px; } - /************/ /* Toolbars */ /************/ @@ -91,20 +90,26 @@ -fx-pref-height: 24.0; -fx-border-radius: 0.0; -fx-background-radius: 0.0; - -fx-background-color: derive(-fx-base, -10%); + -fx-background-color: derive(-fx-base, -14%); } -.titled-pane > .title { - +.titled-pane.accent-header > .title, +.tool-bar.accent-header, +.tool-bar.info-title-bar, +.split-pane.accent-header > .split-pane-divider, +#main-toolbar, #main-vertical-split-pane > .split-pane-divider { + -fx-background-color: derive(-fx-base, -14%); } -.menu-bar { - -fx-background-color: derive(-fx-base, -10%); - -fx-border-style: none none solid none; - -fx-border-color: derive(-fx-base, -20%); +.accordion .titled-pane .tool-bar { + -fx-background-color: derive(-fx-base, -15%); } -#mainToolbar { +.accordion .titled-pane .tool-bar .button { + -fx-padding: 2 5 2 5; +} + +.tool-bar { -fx-pref-height: 30px; -fx-max-height: 30px; -fx-min-height: 30px; @@ -112,18 +117,24 @@ -fx-border-width: .6; } -.button { +.tool-bar .button { + -fx-padding: -3 5 -3 5; -fx-background-color: derive(-fx-base, 10%); -fx-border-color: derive(-fx-base, -20%); -fx-border-radius: 3; } -.choice-box { +.tool-bar .choice-box { -fx-background-color: derive(-fx-base, 10%); -fx-border-color: derive(-fx-base, -20%); -fx-border-radius: 3; } +.titled-pane.bar-sep > .title { + -fx-border-style: none none solid none; + -fx-border-width: 1; +} + .toggle-button:selected.expand-toggle { -fx-shape: "M 0 0 h 7 l -3.5 4 z"; } @@ -141,7 +152,6 @@ -fx-pref-width: 10; } - /************/ /* Tab pane */ /************/ @@ -160,12 +170,13 @@ .tab { -fx-background-insets: 0.0; -fx-background-radius: 0.0; + -fx-padding: 0 30 0 30; -fx-border-color: transparent; -fx-background-color: transparent; } .tab:selected { - -fx-background-color: derive(-fx-base, -20%); + -fx-background-color: derive(-fx-base, -23%); } .tab:focused { @@ -176,8 +187,21 @@ -fx-background-color: transparent; } -.tab { - -fx-padding: 0 30 0 30; +.tab-pane.bottom-pane-tab-pane .tab-header-area .tab:selected { + -fx-border-style: none none solid none; + -fx-border-insets: 0 0 1pt 0; + -fx-border-width: 0 0 1pt 0; + /*-fx-border-color: derive(-fx-base, -10%);*/ +} + + +.tab-pane .tab-header-area .tab-header-background, +.menu-bar { + -fx-border-style: none none solid none; + -fx-border-insets: 0 0 1pt 0; + -fx-border-width: 0 0 1pt 0; + -fx-background-color: -fx-base, derive(-fx-base, -4%); + -fx-border-color: transparent; } .tab-pane .tab-header-background { @@ -203,7 +227,6 @@ /* Scroll bars */ /***************/ - .scroll-bar * { -fx-background-color: white; } diff --git a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml index 9865f7efd8..77ea090688 100644 --- a/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml +++ b/pmd-ui/src/main/resources/net/sourceforge/pmd/util/fxdesigner/fxml/designer.fxml @@ -2,14 +2,9 @@ - - - - - @@ -22,43 +17,39 @@ + - - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +
- + @@ -70,7 +61,7 @@
- +