diff --git a/pmd/regress/test/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java b/pmd/regress/test/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java new file mode 100644 index 0000000000..b42c6b3152 --- /dev/null +++ b/pmd/regress/test/net/sourceforge/pmd/properties/AbstractPropertyDescriptorTester.java @@ -0,0 +1,141 @@ +package test.net.sourceforge.pmd.properties; + +import junit.framework.TestCase; +import net.sourceforge.pmd.PropertyDescriptor; +import net.sourceforge.pmd.util.CollectionUtil; + +/** + * + * @author Brian Remedios + */ +public abstract class AbstractPropertyDescriptorTester extends TestCase { + + private static final int maxCardinality = 10; + + public static final String punctuationChars = "!@#$%^&*()_-+=[]{}\\|;:'\",.<>/?`~"; + public static final String whitespaceChars = " \t\n"; + public static final String digitChars = "0123456789"; + public static final String alphaChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmniopqrstuvwxyz"; + public static final String alphaNumericChars = digitChars + alphaChars; + public static final String allChars = punctuationChars + whitespaceChars + alphaNumericChars; + + + protected AbstractPropertyDescriptorTester() { } + + /** + * Method createValue. + * @param count int + * @return Object + */ + protected abstract Object createValue(int count); + /** + * Method createProperty. + * @param maxCount int + * @return PropertyDescriptor + */ + protected abstract PropertyDescriptor createProperty(int maxCount); + + public void testAsDelimitedString() { + + Object testValue = createValue(maxCardinality); + PropertyDescriptor pmdProp = createProperty(maxCardinality); + + String storeValue = pmdProp.asDelimitedString(testValue); + + Object returnedValue = pmdProp.valueFrom(storeValue); + + assertTrue(CollectionUtil.areEqual(returnedValue, testValue)); + } + + public void testValueFrom() { + + Object testValue = createValue(1); + PropertyDescriptor pmdProp = createProperty(1); + + String storeValue = pmdProp.asDelimitedString(testValue); + + Object returnedValue = pmdProp.valueFrom(storeValue); + + assertTrue(CollectionUtil.areEqual(returnedValue, testValue)); + } + + + public void testErrorFor() { + + Object testValue = createValue(1); + PropertyDescriptor pmdProp = createProperty(1); + String errorMsg = pmdProp.errorFor(testValue); + assertTrue(errorMsg == null); + + testValue = createValue(maxCardinality); + pmdProp = createProperty(maxCardinality); + errorMsg = pmdProp.errorFor(testValue); + assertTrue(errorMsg == null); + } + + public void testType() { + + PropertyDescriptor pmdProp = createProperty(1); + + assertTrue(pmdProp.type() != null); + } + + /** + * Method randomInt. + * @return int + */ + public static int randomInt() { + + int randomVal = (int) (Math.random() * 100 + 1D); + return randomVal + (int) (Math.random() * 100000D); + } + + /** + * Method randomInt. + * @param min int + * @param max int + * @return int + */ + public static int randomInt(int min, int max) { + if (max < min) max = min; + int range = Math.abs(max - min); + int x = (int) ((range * Math.random()) + .5); + return x + min; + } + + /** + * Method randomChar. + * @param characters char[] + * @return char + */ + public static char randomChar(char[] characters) { + return characters[randomInt(0, characters.length-1)]; + } + + /** + * Method randomChoice. + * @param items Object[] + * @return Object + */ + public static Object randomChoice(Object[] items) { + return items[randomInt(0, items.length-1)]; + } + + /** + * Method filter. + * @param chars char[] + * @param removeChar char + * @return char[] + */ + protected static final char[] filter(char[] chars, char removeChar) { + int count = 0; + for (int i=0; i 0 ? + Boolean.TRUE : Boolean.FALSE; + + Boolean[] values = new Boolean[valueCount]; + for (int i=0; i 0.0 + * description 1.0 + * minValue -> 2.0 + * maxValue -> 2.1 + * + * ..would have their fields placed like: + * + * name: [ ] + * description: [ ] + * minimum: [ ] maximum: [ ] + * + * @return float + */ + float uiOrder(); + /** + * If the property is multi-valued then return the separate values after + * parsing the propertyString provided. If it isn't a multi-valued + * property then the value will be returned within an array of size[1]. + * + * @param propertyString String + * @return Object + * @throws IllegalArgumentException + */ + Object valueFrom(String propertyString) throws IllegalArgumentException; + /** + * Formats the object onto a string suitable for storage within the property map. + * @param value Object + * @return String + */ + String asDelimitedString(Object value); + + /** + * Returns a set of choice tuples of available, returns null if none present. + * @return Object[] + */ + Object[][] choices(); + + /** + * A convenience method that returns an error string if the rule holds onto a + * property value that has a problem. Returns null otherwise. + * + * @param rule Rule + * @return String + */ + String propertyErrorFor(Rule rule); + + /** + * Return the character being used to delimit multiple property values within + * a single string. You must ensure that this character does not appear within + * any rule property values to avoid deserialization errors. + * + * @return char + */ + char multiValueDelimiter(); + + /** + * If the datatype is a String then return the preferred number of rows to + * allocate in the text widget, returns a value of one for all other types. + * Useful for multi-line XPATH editors. + * + * @return int + */ + int preferredRowCount(); +} diff --git a/pmd/src/net/sourceforge/pmd/properties/AbstractPMDProperty.java b/pmd/src/net/sourceforge/pmd/properties/AbstractPMDProperty.java new file mode 100644 index 0000000000..754bb0c572 --- /dev/null +++ b/pmd/src/net/sourceforge/pmd/properties/AbstractPMDProperty.java @@ -0,0 +1,275 @@ +package net.sourceforge.pmd.properties; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import net.sourceforge.pmd.PropertyDescriptor; +import net.sourceforge.pmd.Rule; + + +/** + * + * @author Brian Remedios + * @version $Revision$ + */ +public abstract class AbstractPMDProperty implements PropertyDescriptor { + + private String name; + private String description; + private Object defaultValue; + private boolean isRequired = false; + private int maxValueCount = 1; + private float uiOrder; + + protected char multiValueDelimiter = '|'; + + /** + * Constructor for AbstractPMDProperty. + * @param theName String + * @param theDescription String + * @param theDefault Object + * @param theUIOrder float + */ + protected AbstractPMDProperty(String theName, String theDescription, Object theDefault, float theUIOrder) { + name = theName; + description = theDescription; + defaultValue = theDefault; + uiOrder = theUIOrder; + } + + /** + * Method multiValueDelimiter. + * @param aDelimiter char + */ + protected void multiValueDelimiter(char aDelimiter) { + multiValueDelimiter = aDelimiter; + } + + /** + * Method multiValueDelimiter. + * @return char + * @see net.sourceforge.pmd.PropertyDescriptor#multiValueDelimiter() + */ + public char multiValueDelimiter() { + return multiValueDelimiter; + } + + /** + * Method name. + * @return String + * @see net.sourceforge.pmd.PropertyDescriptor#name() + */ + public String name() { + return name; + } + + /** + * Method description. + * @return String + * @see net.sourceforge.pmd.PropertyDescriptor#description() + */ + public String description() { + return description; + } + + /** + * + * @return Object + * @see net.sourceforge.pmd.PropertyDescriptor#defaultValue() + */ + public Object defaultValue() { + return defaultValue; + } + + /** + * Method maxValueCount. + * @return int + * @see net.sourceforge.pmd.PropertyDescriptor#maxValueCount() + */ + public int maxValueCount() { + return maxValueCount; + } + + /** + * Method maxValueCount. + * @param theCount int + * @see net.sourceforge.pmd.PropertyDescriptor#maxValueCount() + */ + protected void maxValueCount(int theCount) { + maxValueCount = theCount; + } + + /** + * Method isRequired. + * @return boolean + * @see net.sourceforge.pmd.PropertyDescriptor#isRequired() + */ + public boolean isRequired() { + return isRequired; + } + + /** + * Method uiOrder. + * @return float + * @see net.sourceforge.pmd.PropertyDescriptor#uiOrder() + */ + public float uiOrder() { + return uiOrder; + } + + /** + * Return the value as a string that can be easily recognized and parsed + * when we see it again. + * + * @param value Object + * @return String + */ + protected String asString(Object value) { + return value == null ? "" : value.toString(); + } + + + /** + * Method asDelimitedString. + * @param values Object + * @return String + * @see net.sourceforge.pmd.PropertyDescriptor#asDelimitedString(Object) + */ + public String asDelimitedString(Object values) { + + if (values == null) return ""; + + if (values instanceof Object[]) { + Object[] valueSet = (Object[])values; + if (valueSet.length == 0) return ""; + if (valueSet.length == 1) return asString(valueSet[0]); + + StringBuffer sb = new StringBuffer(); + sb.append(asString(valueSet[0])); + for (int i=1; i 1) { + if (!isArray(value)) { + return "Value is not an array of type: " + type(); + } + + Class arrayType = value.getClass().getComponentType(); + if (arrayType == null || !arrayType.isAssignableFrom(type())) { + return "Value is not an array of type: " + type(); + } + return null; + } + + if (!type().isAssignableFrom(value.getClass())) { + return "" + value + " is not an instance of " + type(); + } + + return null; + } + + /** + * Method propertyErrorFor. + * @param rule Rule + * @return String + * @see net.sourceforge.pmd.PropertyDescriptor#propertyErrorFor(Rule) + */ + public String propertyErrorFor(Rule rule) { + String strValue = rule.getStringProperty(name()); + if (strValue == null && !isRequired()) return null; + Object realValue = valueFrom(strValue); + return errorFor(realValue); + } + + /** + * Method choices. + * @return Object[][] + * @see net.sourceforge.pmd.PropertyDescriptor#choices() + */ + public Object[][] choices() { + return null; + } + + /** + * Method preferredRowCount. + * @return int + * @see net.sourceforge.pmd.PropertyDescriptor#preferredRowCount() + */ + public int preferredRowCount() { + return 1; + } + + /** + * Method areEqual. + * @param value Object + * @param otherValue Object + * @return boolean + */ + public static final boolean areEqual(Object value, Object otherValue) { + if (value == otherValue) return true; + if (value == null) return false; + if (otherValue == null) return false; + + return value.equals(otherValue); + } +} diff --git a/pmd/src/net/sourceforge/pmd/properties/AbstractScalarProperty.java b/pmd/src/net/sourceforge/pmd/properties/AbstractScalarProperty.java new file mode 100644 index 0000000000..bb00d4199f --- /dev/null +++ b/pmd/src/net/sourceforge/pmd/properties/AbstractScalarProperty.java @@ -0,0 +1,56 @@ +package net.sourceforge.pmd.properties; + +import net.sourceforge.pmd.util.StringUtil; + +/** + * No, subclasses are not necessarily scalar per se, they're just easy to parse without error. + * If you can come up with a better name... + * + * @author Brian Remedios + * @version $Revision$ + */ +public abstract class AbstractScalarProperty extends AbstractPMDProperty { + + /** + * Constructor for AbstractScalarProperty. + * @param theName String + * @param theDescription String + * @param theDefault Object + * @param theUIOrder float + */ + public AbstractScalarProperty(String theName, String theDescription, Object theDefault, float theUIOrder) { + super(theName, theDescription, theDefault, theUIOrder); + } + + /** + * Method createFrom. + * @param value String + * @return Object + */ + protected abstract Object createFrom(String value); + + /** + * Method arrayFor. + * @param size int + * @return Object[] + */ + protected abstract Object[] arrayFor(int size); + + /** + * Method valueFrom. + * @param valueString String + * @return Object[] + * @throws IllegalArgumentException + * @see net.sourceforge.pmd.PropertyDescriptor#valueFrom(String) + */ + public Object valueFrom(String valueString) throws IllegalArgumentException { + + if (maxValueCount() == 1) return createFrom(valueString); + + String[] strValues = StringUtil.substringsOf(valueString, multiValueDelimiter); + + Object[] values = arrayFor(strValues.length); + for (int i=0; i 1) throw new IllegalArgumentException(valueString); + return new Character(valueString.charAt(0)); + } + + String[] values = StringUtil.substringsOf(valueString, multiValueDelimiter); + + Character[] chars = new Character[values.length]; + for (int i=0; i= 0; + } + + private final String illegalCharMsg() { + return "Value cannot contain the \"" + multiValueDelimiter + "\" character"; + } + + /** + * + * @param value Object + * @return String + */ + protected String valueErrorFor(Object value) { + + if (maxValueCount() == 1) { + String testValue = (String)value; + if (!containsDelimiter(testValue)) return null; + return illegalCharMsg(); + } + + String[] values = (String[])value; + for (int i=0; i