Completed Method & Type property descriptors and refactored them under a common class that can filter by optional package prefixes. Matching test cases for same.
Updated TypeMap to catch errors, new test cases. Updated ClassUtil with new functionality for the new descriptors and restored lost indentation formatting. git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/trunk@6388 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
1 parent
3e984b1220
commit
bfa41bb6cd
16 files changed
+870
-215
No files matched your search
+4
-5
@@ -6,8 +6,11 @@ import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.util.CollectionUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* Base functionality for all concrete subclasses that evaluate type-specific property descriptors.
|
||||
* Checks for error conditions during construction, error value detection, serialization, etc.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public abstract class AbstractPropertyDescriptorTester {
|
||||
@@ -225,8 +228,4 @@ public abstract class AbstractPropertyDescriptorTester {
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// public static junit.framework.Test suite() {
|
||||
// return new junit.framework.JUnit4TestAdapter(AbstractPropertyDescriptorTester.class);
|
||||
// }
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.lang.rule.properties.DoubleProperty;
|
||||
|
||||
/**
|
||||
* Evaluates the functionality of the DoubleProperty descriptor by testing its ability to catch creation
|
||||
* errors (illegal args), flag out-of-range test values, and serialize/deserialize groups of double values
|
||||
* onto/from a string buffer.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,9 @@ import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.lang.rule.properties.FloatProperty;
|
||||
|
||||
/**
|
||||
* Evaluates the functionality of the FloatProperty descriptor by testing its ability to catch creation
|
||||
* errors (illegal args), flag out-of-range test values, and serialize/deserialize groups of float values
|
||||
* onto/from a string buffer.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,9 @@ import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.lang.rule.properties.IntegerProperty;
|
||||
|
||||
/**
|
||||
* Evaluates the functionality of the IntegerProperty descriptor by testing its ability to catch creation
|
||||
* errors (illegal args), flag out-of-range test values, and serialize/deserialize groups of integers
|
||||
* onto/from a string buffer.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
@@ -14,16 +17,16 @@ public class IntegerPropertyTest extends AbstractPropertyDescriptorTester {
|
||||
private static final int MIN = 1;
|
||||
private static final int MAX = 12;
|
||||
private static final int SHIFT = 3;
|
||||
|
||||
|
||||
/**
|
||||
* Method createValue.
|
||||
* @param count int
|
||||
* @return Object
|
||||
*/
|
||||
protected Object createValue(int count) {
|
||||
|
||||
|
||||
if (count == 1) return Integer.valueOf((int)(System.currentTimeMillis() % 100));
|
||||
|
||||
|
||||
Integer[] values = new Integer[count];
|
||||
for (int i=0; i<values.length; i++) values[i] = (Integer)createValue(1);
|
||||
return values;
|
||||
@@ -36,29 +39,28 @@ public class IntegerPropertyTest extends AbstractPropertyDescriptorTester {
|
||||
* @return Object
|
||||
*/
|
||||
protected Object createBadValue(int count) {
|
||||
|
||||
|
||||
if (count == 1) return Integer.valueOf(
|
||||
randomBool() ?
|
||||
randomInt(MIN - SHIFT, MIN) :
|
||||
randomInt(MAX, MAX + SHIFT)
|
||||
);
|
||||
|
||||
|
||||
Integer[] values = new Integer[count];
|
||||
for (int i=0; i<values.length; i++) values[i] = (Integer)createBadValue(1);
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testErrorForBad() { } // not until int properties get ranges
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Method createProperty.
|
||||
* @param multiValue boolean
|
||||
* @return PropertyDescriptor
|
||||
*/
|
||||
protected PropertyDescriptor createProperty(boolean multiValue) {
|
||||
|
||||
|
||||
return multiValue ?
|
||||
new IntegerProperty("testInteger", "Test integer property", new int[] {-1,0,1,2}, 1.0f) :
|
||||
new IntegerProperty("testInteger", "Test integer property", 9, 1.0f);
|
||||
@@ -70,12 +72,12 @@ public class IntegerPropertyTest extends AbstractPropertyDescriptorTester {
|
||||
* @return PropertyDescriptor
|
||||
*/
|
||||
protected PropertyDescriptor createBadProperty(boolean multiValue) {
|
||||
|
||||
|
||||
return multiValue ?
|
||||
new IntegerProperty("testInteger", "", new int[] {-1,0,1,2}, 1.0f) :
|
||||
new IntegerProperty("", "Test integer property", 9, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
public static junit.framework.Test suite() {
|
||||
return new junit.framework.JUnit4TestAdapter(IntegerPropertyTest.class);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package test.net.sourceforge.pmd.properties;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.lang.rule.properties.MethodProperty;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Evaluates the functionality of the MethodProperty descriptor by testing its ability to catch creation
|
||||
* errors (illegal args), flag invalid methods per the allowable packages, and serialize/deserialize
|
||||
* groups of methods onto/from a string buffer.
|
||||
*
|
||||
* We're using methods from java.lang classes for 'normal' constructors and applying ones from
|
||||
* java.util types as ones we expect to fail.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public class MethodPropertyTest extends AbstractPropertyDescriptorTester {
|
||||
|
||||
private static final String[] methodSignatures = new String[] {
|
||||
"String#isEmpty()",
|
||||
"java.lang.String#isEmpty()",
|
||||
"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)"
|
||||
};
|
||||
|
||||
public MethodPropertyTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsStringOn() {
|
||||
|
||||
Method method = null;
|
||||
|
||||
for (int i=0; i<methodSignatures.length; i++) {
|
||||
method = MethodProperty.methodFrom(
|
||||
methodSignatures[i],
|
||||
MethodProperty.CLASS_METHOD_DELIMITER,
|
||||
MethodProperty.METHOD_ARG_DELIMITER
|
||||
);
|
||||
assertTrue("Unable to identify method: " + methodSignatures[i], method != null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsMethodOn() {
|
||||
|
||||
Method[] methods = new Method[methodSignatures.length];
|
||||
|
||||
for (int i=0; i<methodSignatures.length; i++) {
|
||||
methods[i] = MethodProperty.methodFrom(
|
||||
methodSignatures[i],
|
||||
MethodProperty.CLASS_METHOD_DELIMITER,
|
||||
MethodProperty.METHOD_ARG_DELIMITER
|
||||
);
|
||||
assertTrue("Unable to identify method: " + methodSignatures[i], methods[i] != null);
|
||||
}
|
||||
|
||||
String translatedMethod = null;
|
||||
for (int i=0; i<methods.length; i++) {
|
||||
translatedMethod = MethodProperty.asStringFor(methods[i]);
|
||||
assertTrue(
|
||||
"Translated method does not match",
|
||||
methodSignatures[i].equals(translatedMethod)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyDescriptor createBadProperty(boolean multiValue) {
|
||||
|
||||
Method[] methods = String.class.getDeclaredMethods();
|
||||
|
||||
return multiValue ?
|
||||
new MethodProperty("methodProperty", "asdf", new Method[] { methods[2], methods[3] }, new String[] { "java.util" } , 1.0f) :
|
||||
new MethodProperty("methodProperty", "asdf", methods[1], new String[] { "java.util" }, 1.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createBadValue(int count) {
|
||||
|
||||
Method[] allMethods = HashMap.class.getDeclaredMethods();
|
||||
|
||||
if (count == 1) {
|
||||
return (Method)randomChoice(allMethods);
|
||||
}
|
||||
|
||||
Method[] methods = new Method[count];
|
||||
for (int i=0; i<count; i++) {
|
||||
methods[i] = allMethods[i];
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyDescriptor createProperty(boolean multiValue) {
|
||||
|
||||
Method[] methods = String.class.getDeclaredMethods();
|
||||
|
||||
return multiValue ?
|
||||
new MethodProperty("methodProperty", "asdf", new Method[] { methods[2], methods[3] }, new String[] { "java.lang" } , 1.0f) :
|
||||
new MethodProperty("methodProperty", "asdf", methods[1], new String[] { "java.lang" }, 1.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createValue(int count) {
|
||||
|
||||
Method[] allMethods = String.class.getDeclaredMethods();
|
||||
|
||||
if (count == 1) {
|
||||
return (Method)randomChoice(allMethods);
|
||||
}
|
||||
|
||||
Method[] methods = new Method[count];
|
||||
for (int i=0; i<count; i++) {
|
||||
methods[i] = allMethods[i];
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
public static junit.framework.Test suite() {
|
||||
return new junit.framework.JUnit4TestAdapter(MethodPropertyTest.class);
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,8 @@ class NonRuleWithAllPropertyTypes extends AbstractJavaRule {
|
||||
public static final PropertyDescriptor singleFloat = new FloatProperty("singleFloat", "Property with a single float value", 9f, 10f, .9f, 5.0f);
|
||||
public static final PropertyDescriptor multiFloat = new FloatProperty("multiFloat", "Property with multiple float values", 0f, 5f, new float[] {1,2,3}, 6.0f);
|
||||
|
||||
public static final PropertyDescriptor singleType = new TypeProperty("singleType", "Property with a single type value", String.class, 5.0f);
|
||||
public static final PropertyDescriptor multiType = new TypeProperty("multiType", "Property with multiple type values", new Class[] {Integer.class, Object.class}, 6.0f);
|
||||
public static final PropertyDescriptor singleType = new TypeProperty("singleType", "Property with a single type value", String.class, new String[] { "java.lang" }, 5.0f);
|
||||
public static final PropertyDescriptor multiType = new TypeProperty("multiType", "Property with multiple type values", new Class[] {Integer.class, Object.class}, new String[] { "java.lang" }, 6.0f);
|
||||
|
||||
public static final PropertyDescriptor enumType = new EnumeratedProperty<Class>("enumType", "Property with a enumerated choices", new String[] {"String", "Object"}, new Class[] {String.class, Object.class}, 1, 5.0f);
|
||||
public static final PropertyDescriptor multiEnumType = new EnumeratedProperty<Class>("enumType", "Property with a enumerated choices", new String[] {"String", "Object"}, new Class[] {String.class, Object.class}, new int[] {0,1}, 5.0f);
|
||||
|
||||
@@ -4,6 +4,9 @@ import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.lang.rule.properties.StringProperty;
|
||||
|
||||
/**
|
||||
* Evaluates the functionality of the StringProperty descriptor by testing its ability to catch creation
|
||||
* errors (illegal args), flag invalid strings per any specified expressions, and serialize/deserialize
|
||||
* groups of strings onto/from a string buffer.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
package test.net.sourceforge.pmd.properties;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import java.util.Observer;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.lang.rule.properties.TypeProperty;
|
||||
|
||||
/**
|
||||
* Evaluates the functionality of the TypeProperty descriptor by testing its ability to catch creation
|
||||
* errors (illegal args), flag invalid Type values per the allowable packages, and serialize/deserialize
|
||||
* groups of types onto/from a string buffer.
|
||||
*
|
||||
* We're using java.lang classes for 'normal' constructors and applying java.util types as ones we expect
|
||||
* to fail.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public class TypePropertyTest extends AbstractPropertyDescriptorTester {
|
||||
|
||||
public static final Class[] classes = new Class[] { String.class, Integer.class, int.class, HashMap.class, Map.class };
|
||||
private static final Class[] javaLangClasses = new Class[] { String.class, Integer.class, Thread.class, Object.class, Runtime.class };
|
||||
private static final Class[] javaUtilTypes = new Class[] { HashMap.class, Map.class, Comparator.class, Set.class, Observer.class };
|
||||
|
||||
public TypePropertyTest() {
|
||||
super();
|
||||
@@ -27,7 +35,7 @@ public class TypePropertyTest extends AbstractPropertyDescriptorTester {
|
||||
*/
|
||||
protected Object createValue(int count) {
|
||||
|
||||
if (count == 1) return randomChoice(classes);
|
||||
if (count == 1) return randomChoice(javaLangClasses);
|
||||
|
||||
Object[] values = new Object[count];
|
||||
for (int i=0; i<values.length; i++) values[i] = createValue(1);
|
||||
@@ -41,27 +49,23 @@ public class TypePropertyTest extends AbstractPropertyDescriptorTester {
|
||||
*/
|
||||
protected Object createBadValue(int count) {
|
||||
|
||||
if (count == 1) return null;
|
||||
|
||||
if (count == 1) return randomChoice(javaUtilTypes);
|
||||
|
||||
Object[] values = new Object[count];
|
||||
for (int i=0; i<values.length; i++) values[i] = createBadValue(1);
|
||||
return values;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorForBad() { } // not until type properties get illegal packages
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Method createProperty.
|
||||
* @param multiValue boolean
|
||||
* @return PropertyDescriptor
|
||||
*/
|
||||
protected PropertyDescriptor createProperty(boolean multiValue) {
|
||||
|
||||
|
||||
return multiValue ?
|
||||
new TypeProperty("testType", "Test type property", classes, 1.0f) :
|
||||
new TypeProperty("testType", "Test type property", Byte.class, 1.0f);
|
||||
new TypeProperty("testType", "Test type property", javaLangClasses, new String[] { "java.lang" }, 1.0f) :
|
||||
new TypeProperty("testType", "Test type property", javaLangClasses[0], new String[] { "java.lang" }, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,10 +76,10 @@ public class TypePropertyTest extends AbstractPropertyDescriptorTester {
|
||||
protected PropertyDescriptor createBadProperty(boolean multiValue) {
|
||||
|
||||
return multiValue ?
|
||||
new TypeProperty("testType", "Test type property", new Class[0], 1.0f) :
|
||||
new TypeProperty("testType", "", Byte.class, 1.0f);
|
||||
new TypeProperty("testType", "Test type property", Set.class, new String[] { "java.lang" }, 1.0f) :
|
||||
new TypeProperty("testType", "Test type property", javaLangClasses, new String[] { "java.util" }, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
public static junit.framework.Test suite() {
|
||||
return new junit.framework.JUnit4TestAdapter(TypePropertyTest.class);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package test.net.sourceforge.pmd.util;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import net.sourceforge.pmd.util.TypeMap;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Evaluates all major functionality of the TypeMap class.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public class TypeMapTest {
|
||||
|
||||
@Test
|
||||
public void testAddClassOfQ() {
|
||||
|
||||
TypeMap map = new TypeMap(2);
|
||||
map.add(java.util.List.class);
|
||||
|
||||
try {
|
||||
map.add(java.awt.List.class);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return; // caught ok
|
||||
}
|
||||
|
||||
fail("Uncaught error inserting type with same root names");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsClassOfQ() {
|
||||
|
||||
TypeMap map = new TypeMap(2);
|
||||
map.add(String.class);
|
||||
map.add(List.class);
|
||||
|
||||
Assert.assertTrue(map.contains(String.class));
|
||||
Assert.assertTrue(map.contains(List.class));
|
||||
Assert.assertFalse(map.contains(Map.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsString() {
|
||||
|
||||
TypeMap map = new TypeMap(2);
|
||||
map.add(String.class);
|
||||
map.add(List.class);
|
||||
|
||||
Assert.assertTrue(map.contains("String"));
|
||||
Assert.assertTrue(map.contains("java.lang.String"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeFor() {
|
||||
|
||||
TypeMap map = new TypeMap(2);
|
||||
map.add(String.class);
|
||||
map.add(List.class);
|
||||
|
||||
Assert.assertTrue(map.typeFor("String") == String.class);
|
||||
Assert.assertTrue(map.typeFor("java.lang.String") == String.class);
|
||||
Assert.assertTrue(map.typeFor("List") == List.class);
|
||||
Assert.assertTrue(map.typeFor("java.util.List") == List.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSize() {
|
||||
|
||||
TypeMap map = new TypeMap(4);
|
||||
map.add(String.class);
|
||||
map.add(HashMap.class);
|
||||
map.add(Integer.class);
|
||||
|
||||
Assert.assertTrue(map.size() == 6);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package net.sourceforge.pmd.lang.rule.properties;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public abstract class AbstractPackagedProperty extends AbstractProperty {
|
||||
|
||||
private String[] legalPackageNames;
|
||||
|
||||
protected static final char DELIMITER = '|';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param theName
|
||||
* @param theDescription
|
||||
* @param theDefault
|
||||
* @param theLegalPackageNames
|
||||
* @param theUIOrder
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
protected AbstractPackagedProperty(String theName, String theDescription, Object theDefault, String[] theLegalPackageNames, float theUIOrder) {
|
||||
this(theName, theDescription, new Object[] {theDefault}, theLegalPackageNames, theUIOrder);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param theName
|
||||
* @param theDescription
|
||||
* @param theDefaults
|
||||
* @param theLegalPackageNames
|
||||
* @param theUIOrder
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
protected AbstractPackagedProperty(String theName, String theDescription, Object[] theDefaults, String[] theLegalPackageNames, float theUIOrder) {
|
||||
super(theName, theDescription, theDefaults, theUIOrder);
|
||||
|
||||
checkValidPackages(theDefaults, theLegalPackageNames);
|
||||
|
||||
legalPackageNames = theLegalPackageNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the names of the items against the allowable name prefixes. If one or more of them
|
||||
* do not have valid prefixes then an exception will be thrown.
|
||||
*
|
||||
* @param items
|
||||
* @param legalNamePrefixes
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private void checkValidPackages(Object[] items, String[] legalNamePrefixes) {
|
||||
|
||||
String[] names = new String[items.length];
|
||||
Set<String> nameSet = new HashSet<String>(items.length);
|
||||
String name = null;
|
||||
|
||||
for (int i=0; i<items.length; i++) {
|
||||
name = packageNameOf(items[i]);
|
||||
names[i] = name;
|
||||
nameSet.add(name);
|
||||
}
|
||||
|
||||
for (int i=0; i<names.length; i++) {
|
||||
for (int l=0; l<legalNamePrefixes.length; l++) {
|
||||
if (names[i].startsWith(legalNamePrefixes[l])) {
|
||||
nameSet.remove(names[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameSet.isEmpty()) { return; }
|
||||
|
||||
throw new IllegalArgumentException("Invalid items: " + nameSet);
|
||||
}
|
||||
|
||||
abstract protected String itemTypeName();
|
||||
|
||||
/**
|
||||
* Method valueErrorFor.
|
||||
* @param value Object
|
||||
* @return String
|
||||
*/
|
||||
protected String valueErrorFor(Object value) {
|
||||
|
||||
if (value == null) {
|
||||
String err = super.valueErrorFor(null);
|
||||
if (err != null) { return err; }
|
||||
}
|
||||
|
||||
if (legalPackageNames == null) {
|
||||
return null; // no restriction
|
||||
}
|
||||
|
||||
String name = packageNameOf(value);
|
||||
|
||||
for (int i=0; i<legalPackageNames.length; i++) {
|
||||
if (name.startsWith(legalPackageNames[i])) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return "Disallowed " + itemTypeName() + ": " + name;
|
||||
}
|
||||
|
||||
abstract protected String packageNameOf(Object item);
|
||||
|
||||
public String[] legalPackageNames() {
|
||||
return legalPackageNames;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,12 +13,12 @@ import net.sourceforge.pmd.util.StringUtil;
|
||||
*/
|
||||
public abstract class AbstractProperty implements PropertyDescriptor {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private Object defaultValue;
|
||||
private boolean isRequired = false;
|
||||
private boolean isMultiValue = false;
|
||||
private float uiOrder;
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final Object defaultValue;
|
||||
private final boolean isRequired;
|
||||
private boolean isMultiValue = false;
|
||||
private final float uiOrder;
|
||||
|
||||
protected char multiValueDelimiter = '|';
|
||||
|
||||
@@ -34,9 +34,13 @@ public abstract class AbstractProperty implements PropertyDescriptor {
|
||||
name = checkNotEmpty(theName, "name");
|
||||
description = checkNotEmpty(theDescription, "description");
|
||||
defaultValue = theDefault;
|
||||
isRequired = false; // TODO - do we need this?
|
||||
uiOrder = checkPositive(theUIOrder, "UI order");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private static String checkNotEmpty(String arg, String argId) {
|
||||
|
||||
if (StringUtil.isEmpty(arg)) {
|
||||
@@ -45,7 +49,10 @@ public abstract class AbstractProperty implements PropertyDescriptor {
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private static float checkPositive(float arg, String argId) {
|
||||
if (arg < 0) {
|
||||
throw new IllegalArgumentException("Property attribute " + argId + "' must be zero or positive");
|
||||
@@ -97,6 +104,22 @@ public abstract class AbstractProperty implements PropertyDescriptor {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
protected boolean defaultHasNullValue() {
|
||||
|
||||
if (defaultValue == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isMultiValue && isArray(defaultValue)) {
|
||||
Object[] defaults = (Object[])defaultValue;
|
||||
for (int i=0; i<defaults.length; i++) {
|
||||
if (defaults[i] == null) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method isMultiValue.
|
||||
* @return boolean
|
||||
@@ -108,8 +131,7 @@ public abstract class AbstractProperty implements PropertyDescriptor {
|
||||
|
||||
/**
|
||||
* Method isMultiValue.
|
||||
* @param flag boolean
|
||||
* @see net.sourceforge.pmd.PropertyDescriptor#isMultiValue()
|
||||
* @param flag
|
||||
*/
|
||||
protected void isMultiValue(boolean flag) {
|
||||
isMultiValue = flag;
|
||||
@@ -212,7 +234,14 @@ public abstract class AbstractProperty implements PropertyDescriptor {
|
||||
* @return String
|
||||
*/
|
||||
protected String valueErrorFor(Object value) {
|
||||
// override as required
|
||||
|
||||
if (value == null) {
|
||||
if (defaultHasNullValue()) {
|
||||
return null;
|
||||
} else {
|
||||
return "missing value";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,25 +3,185 @@
|
||||
*/
|
||||
package net.sourceforge.pmd.lang.rule.properties;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.pmd.util.ClassUtil;
|
||||
import net.sourceforge.pmd.util.StringUtil;
|
||||
|
||||
/**
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public class MethodProperty extends AbstractProperty {
|
||||
public class MethodProperty extends AbstractPackagedProperty {
|
||||
|
||||
public static final char CLASS_METHOD_DELIMITER = '#';
|
||||
public static final char METHOD_ARG_DELIMITER = ',';
|
||||
public static final char[] METHOD_GROUP_DELIMITERS = new char[] { '(', ')' };
|
||||
|
||||
private static final Map<Class, String> TYPE_SHORTCUTS = ClassUtil.getClassShortNames();
|
||||
|
||||
private static String shortestNameFor(Class cls) {
|
||||
String compactName = TYPE_SHORTCUTS.get(cls);
|
||||
return compactName == null ? cls.getName() : compactName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
public static String asStringFor(Method method) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
asStringOn(method, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ? "" : asStringFor((Method)value);
|
||||
}
|
||||
|
||||
private static void serializedTypeIdOn(Class type, StringBuilder sb) {
|
||||
|
||||
Class arrayType = type.getComponentType();
|
||||
if (arrayType == null) {
|
||||
sb.append(shortestNameFor(type));
|
||||
return;
|
||||
}
|
||||
sb.append(shortestNameFor(arrayType)).append("[]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the method signature onto the specified buffer.
|
||||
*
|
||||
* @param method Method
|
||||
* @param sb StringBuilder
|
||||
*/
|
||||
public static void asStringOn(Method method, StringBuilder sb) {
|
||||
|
||||
Class clazz = method.getDeclaringClass();
|
||||
|
||||
sb.append(shortestNameFor(clazz) );
|
||||
sb.append(CLASS_METHOD_DELIMITER);
|
||||
sb.append(method.getName());
|
||||
|
||||
sb.append(METHOD_GROUP_DELIMITERS[0]);
|
||||
|
||||
Class[] argTypes = method.getParameterTypes();
|
||||
if (argTypes.length == 0) {
|
||||
sb.append(METHOD_GROUP_DELIMITERS[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
serializedTypeIdOn(argTypes[0], sb);
|
||||
for (int i=1; i<argTypes.length; i++) {
|
||||
sb.append(METHOD_ARG_DELIMITER);
|
||||
serializedTypeIdOn(argTypes[i], sb);
|
||||
}
|
||||
sb.append(METHOD_GROUP_DELIMITERS[1]);
|
||||
}
|
||||
|
||||
|
||||
private static Class typeFor(String typeName) {
|
||||
|
||||
Class type = null;
|
||||
|
||||
if (typeName.endsWith("[]")) {
|
||||
String arrayTypeName = typeName.substring(0, typeName.length()-2);
|
||||
type = typeFor(arrayTypeName); // recurse
|
||||
return Array.newInstance(type, 0).getClass(); // TODO is there a better way to get an array type?
|
||||
}
|
||||
|
||||
type = ClassUtil.getTypeFor(typeName); // try shortcut first
|
||||
if (type != null) { return type; }
|
||||
|
||||
try {
|
||||
return Class.forName(typeName);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the method specified within the string argument after parsing out its source class and
|
||||
* any optional arguments. Callers need to specify the delimiters expected between the various
|
||||
* elements. I.e.:
|
||||
*
|
||||
* "String#isEmpty()"
|
||||
* "String#indexOf(int)"
|
||||
* "String#substring(int,int)"
|
||||
*
|
||||
* If a method isn't part of the specified class we will walk up any superclasses to Object to try
|
||||
* and find it.
|
||||
*
|
||||
* If the classes are listed in the ClassUtil class within in Typemaps then you likely can avoid
|
||||
* specifying fully-qualified class names per the above example.
|
||||
*
|
||||
* Returns null if a matching method cannot be found.
|
||||
*
|
||||
* @param methodNameAndArgTypes
|
||||
* @param classMethodDelimiter
|
||||
* @param methodArgDelimiter
|
||||
* @return Method
|
||||
*/
|
||||
public static Method methodFrom(String methodNameAndArgTypes, char classMethodDelimiter, char methodArgDelimiter) {
|
||||
|
||||
// classname#methodname(arg1,arg2)
|
||||
// 0 1 2
|
||||
|
||||
int delimPos0 = methodNameAndArgTypes.indexOf(classMethodDelimiter);
|
||||
if (delimPos0 < 0) { return null; }
|
||||
|
||||
String className = methodNameAndArgTypes.substring(0, delimPos0);
|
||||
Class type = ClassUtil.getTypeFor(className);
|
||||
if (type == null) { return null; }
|
||||
|
||||
int delimPos1 = methodNameAndArgTypes.indexOf(METHOD_GROUP_DELIMITERS[0]);
|
||||
if (delimPos1 < 0) {
|
||||
String methodName = methodNameAndArgTypes.substring(delimPos0 + 1);
|
||||
return ClassUtil.methodFor(type, methodName, ClassUtil.EMPTY_CLASS_ARRAY);
|
||||
}
|
||||
|
||||
String methodName = methodNameAndArgTypes.substring(delimPos0 + 1, delimPos1);
|
||||
if (StringUtil.isEmpty(methodName)) { return null; } // missing method name?
|
||||
|
||||
int delimPos2 = methodNameAndArgTypes.indexOf(METHOD_GROUP_DELIMITERS[1]);
|
||||
if (delimPos2 < 0) { return null; } // error!
|
||||
|
||||
String argTypesStr = methodNameAndArgTypes.substring(delimPos1 + 1, delimPos2);
|
||||
if (StringUtil.isEmpty(argTypesStr)) {
|
||||
return ClassUtil.methodFor(type, methodName, ClassUtil.EMPTY_CLASS_ARRAY);
|
||||
} // no arg(s)
|
||||
|
||||
String[] argTypeNames = StringUtil.substringsOf(argTypesStr, methodArgDelimiter);
|
||||
Class[] argTypes = new Class[argTypeNames.length];
|
||||
for (int i=0; i<argTypes.length; i++) {
|
||||
argTypes[i] = typeFor(argTypeNames[i]);
|
||||
}
|
||||
|
||||
return ClassUtil.methodFor(type, methodName, argTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MethodProperty.
|
||||
*
|
||||
* @param theName String
|
||||
* @param theDescription String
|
||||
* @param theDefault Method
|
||||
* @param legalPackageNames String[]
|
||||
* @param theUIOrder float
|
||||
*/
|
||||
public MethodProperty(String theName, String theDescription, Method theDefault, float theUIOrder) {
|
||||
super(theName, theDescription, theDefault, theUIOrder);
|
||||
public MethodProperty(String theName, String theDescription, Method theDefault, String[] legalPackageNames, float theUIOrder) {
|
||||
super(theName, theDescription, theDefault, legalPackageNames, theUIOrder);
|
||||
|
||||
isMultiValue(false);
|
||||
multiValueDelimiter(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,14 +190,27 @@ public class MethodProperty extends AbstractProperty {
|
||||
* @param theName String
|
||||
* @param theDescription String
|
||||
* @param theDefaults Method[]
|
||||
* @param legalPackageNames String[]
|
||||
* @param theUIOrder float
|
||||
*/
|
||||
public MethodProperty(String theName, String theDescription, Method[] theDefaults, float theUIOrder) {
|
||||
super(theName, theDescription, theDefaults, theUIOrder);
|
||||
public MethodProperty(String theName, String theDescription, Method[] theDefaults, String[] legalPackageNames, float theUIOrder) {
|
||||
super(theName, theDescription, theDefaults, legalPackageNames, theUIOrder);
|
||||
|
||||
isMultiValue(true);
|
||||
multiValueDelimiter(' ');
|
||||
}
|
||||
|
||||
protected String packageNameOf(Object item) {
|
||||
|
||||
final Method method = (Method)item;
|
||||
|
||||
return method.getDeclaringClass().getName() + '.' + method.getName();
|
||||
}
|
||||
|
||||
protected String itemTypeName() {
|
||||
return "method";
|
||||
}
|
||||
|
||||
/**
|
||||
* Method type.
|
||||
*
|
||||
@@ -56,39 +229,23 @@ public class MethodProperty extends AbstractProperty {
|
||||
* @throws IllegalArgumentException
|
||||
* @see net.sourceforge.pmd.PropertyDescriptor#valueFrom(String)
|
||||
*/
|
||||
public Object valueFrom(String propertyString) throws IllegalArgumentException {
|
||||
public Object valueFrom(String valueString) throws IllegalArgumentException {
|
||||
|
||||
Class<?> cls = classIn(propertyString);
|
||||
String methodName = methodNameIn(propertyString);
|
||||
Class<?>[] parameterTypes = parameterTypesIn(propertyString);
|
||||
|
||||
try {
|
||||
return cls.getMethod(methodName, parameterTypes);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("invalid method: " + propertyString);
|
||||
if (!isMultiValue()) {
|
||||
return methodFrom(
|
||||
valueString, CLASS_METHOD_DELIMITER, METHOD_ARG_DELIMITER
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String[] values = StringUtil.substringsOf(valueString, multiValueDelimiter());
|
||||
|
||||
private Class<?> classIn(String propertyString) throws IllegalArgumentException {
|
||||
|
||||
int dotPos = propertyString.lastIndexOf('.');
|
||||
String className = propertyString.substring(0, dotPos);
|
||||
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalArgumentException("class not found: " + className);
|
||||
Method[] methods = new Method[values.length];
|
||||
for (int i=0; i<methods.length; i++) {
|
||||
methods[i] = methodFrom(values[i],
|
||||
CLASS_METHOD_DELIMITER,
|
||||
METHOD_ARG_DELIMITER
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private String methodNameIn(String propertyString) throws IllegalArgumentException {
|
||||
|
||||
int dotPos = propertyString.lastIndexOf('.');
|
||||
return propertyString.substring(dotPos);
|
||||
}
|
||||
|
||||
// TODO parse parameter types
|
||||
private Class<?>[] parameterTypesIn(String propertyString) {
|
||||
return null;
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,12 @@ public class StringProperty extends AbstractProperty {
|
||||
checkDefaults(theDefaultValue, aMultiValueDelimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param defaultValue
|
||||
* @param delim
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private static void checkDefaults(Object defaultValue, char delim) {
|
||||
|
||||
if (defaultValue == null) { return; }
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
package net.sourceforge.pmd.lang.rule.properties;
|
||||
|
||||
import net.sourceforge.pmd.util.ClassUtil;
|
||||
import net.sourceforge.pmd.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Defines a property that supports class types, even for primitive values!
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public class TypeProperty extends StringProperty {
|
||||
public class TypeProperty extends AbstractPackagedProperty {
|
||||
|
||||
private static final char DELIMITER = '|';
|
||||
|
||||
@@ -19,10 +20,11 @@ public class TypeProperty extends StringProperty {
|
||||
* @param theName String
|
||||
* @param theDescription String
|
||||
* @param theDefault Class
|
||||
* @param legalPackageNames String[]
|
||||
* @param theUIOrder float
|
||||
*/
|
||||
public TypeProperty(String theName, String theDescription, Class<?> theDefault, float theUIOrder) {
|
||||
super(theName, theDescription, theDefault, theUIOrder, DELIMITER);
|
||||
public TypeProperty(String theName, String theDescription, Class<?> theDefault, String[] legalPackageNames, float theUIOrder) {
|
||||
super(theName, theDescription, theDefault, legalPackageNames, theUIOrder);
|
||||
|
||||
isMultiValue(false);
|
||||
}
|
||||
@@ -32,14 +34,19 @@ public class TypeProperty extends StringProperty {
|
||||
* @param theName String
|
||||
* @param theDescription String
|
||||
* @param theDefaults Class[]
|
||||
* @param legalPackageNames String[]
|
||||
* @param theUIOrder float
|
||||
*/
|
||||
public TypeProperty(String theName, String theDescription, Class<?>[] theDefaults, float theUIOrder) {
|
||||
super(theName, theDescription, theDefaults, theUIOrder, DELIMITER);
|
||||
public TypeProperty(String theName, String theDescription, Class<?>[] theDefaults, String[] legalPackageNames, float theUIOrder) {
|
||||
super(theName, theDescription, theDefaults, legalPackageNames, theUIOrder);
|
||||
|
||||
isMultiValue(true);
|
||||
}
|
||||
|
||||
protected String packageNameOf(Object item) {
|
||||
return ((Class)item).getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method type.
|
||||
* @return Class
|
||||
@@ -49,6 +56,10 @@ public class TypeProperty extends StringProperty {
|
||||
public Class<?> type() {
|
||||
return Class.class;
|
||||
}
|
||||
|
||||
protected String itemTypeName() {
|
||||
return "type";
|
||||
}
|
||||
|
||||
/**
|
||||
* Method asString.
|
||||
@@ -64,6 +75,7 @@ public class TypeProperty extends StringProperty {
|
||||
* Method classFrom.
|
||||
* @param className String
|
||||
* @return Class
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private Class<?> classFrom(String className) {
|
||||
|
||||
@@ -92,24 +104,12 @@ public class TypeProperty extends StringProperty {
|
||||
return classFrom(valueString);
|
||||
}
|
||||
|
||||
String[] values = (String[]) super.valueFrom(valueString);
|
||||
String[] values = StringUtil.substringsOf(valueString, DELIMITER);
|
||||
|
||||
Class<?>[] classes = new Class[values.length];
|
||||
Class<?>[] classes = new Class<?>[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
classes[i] = classFrom(values[i]);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Neutralize unwanted superclass functionality that will result
|
||||
* in a class cast exception.
|
||||
*
|
||||
* @param value Object
|
||||
* @return String
|
||||
*/
|
||||
@Override
|
||||
protected String valueErrorFor(Object value) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +1,112 @@
|
||||
package net.sourceforge.pmd.util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Various class-related utility methods
|
||||
* Various class-related utility methods.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public final class ClassUtil {
|
||||
|
||||
private ClassUtil() {
|
||||
};
|
||||
public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
|
||||
@SuppressWarnings("PMD.AvoidUsingShortType")
|
||||
private static final TypeMap PRIMITIVE_TYPE_NAMES = new TypeMap(new Class[] { int.class, byte.class, long.class,
|
||||
short.class, float.class, double.class, char.class, boolean.class, });
|
||||
private ClassUtil() {
|
||||
};
|
||||
|
||||
private static final TypeMap TYPES_BY_NAME = new TypeMap(new Class[] { Integer.class, Byte.class, Long.class,
|
||||
Short.class, Float.class, Double.class, Character.class, Boolean.class, BigDecimal.class, String.class,
|
||||
Object.class, });
|
||||
@SuppressWarnings("PMD.AvoidUsingShortType")
|
||||
private static final TypeMap PRIMITIVE_TYPE_NAMES = new TypeMap(
|
||||
new Class[] { int.class, byte.class, long.class, short.class,
|
||||
float.class, double.class, char.class, boolean.class, });
|
||||
|
||||
/**
|
||||
* Returns the type(class) for the name specified
|
||||
* or null if not found.
|
||||
*
|
||||
* @param name String
|
||||
* @return Class
|
||||
*/
|
||||
public static Class<?> getPrimitiveTypeFor(String name) {
|
||||
return PRIMITIVE_TYPE_NAMES.typeFor(name);
|
||||
}
|
||||
private static final TypeMap TYPES_BY_NAME = new TypeMap(new Class[] {
|
||||
Integer.class, Byte.class, Long.class, Short.class, Float.class,
|
||||
Double.class, Character.class, Boolean.class, BigDecimal.class,
|
||||
String.class, Object.class, });
|
||||
|
||||
/**
|
||||
* Attempt to determine the actual class given the short name.
|
||||
*
|
||||
* @param shortName String
|
||||
* @return Class
|
||||
*/
|
||||
public static Class<?> getTypeFor(String shortName) {
|
||||
|
||||
Class<?> type = TYPES_BY_NAME.typeFor(shortName);
|
||||
if (type != null) {
|
||||
return type;
|
||||
/**
|
||||
* Returns the type(class) for the name specified or null if not found.
|
||||
*
|
||||
* @param name String
|
||||
* @return Class
|
||||
*/
|
||||
public static Class<?> getPrimitiveTypeFor(String name) {
|
||||
return PRIMITIVE_TYPE_NAMES.typeFor(name);
|
||||
}
|
||||
|
||||
type = PRIMITIVE_TYPE_NAMES.typeFor(shortName);
|
||||
if (type != null) {
|
||||
return type;
|
||||
/**
|
||||
* Return a map of all the short names of classes we maintain mappings for.
|
||||
* The names are keyed by the classes themselves.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Map<Class, String> getClassShortNames() {
|
||||
|
||||
Map<Class, String> map = new HashMap<Class, String>();
|
||||
map.putAll(PRIMITIVE_TYPE_NAMES.asInverseWithShortName());
|
||||
map.putAll(TYPES_BY_NAME.asInverseWithShortName());
|
||||
return map;
|
||||
}
|
||||
|
||||
return CollectionUtil.getCollectionTypeFor(shortName);
|
||||
}
|
||||
/**
|
||||
* Attempt to determine the actual class given the short name.
|
||||
*
|
||||
* @param shortName String
|
||||
* @return Class
|
||||
*/
|
||||
public static Class<?> getTypeFor(String shortName) {
|
||||
|
||||
/**
|
||||
* Returns the abbreviated name of the type,
|
||||
* without the package name
|
||||
*
|
||||
* @param fullTypeName
|
||||
* @return String
|
||||
*/
|
||||
Class<?> type = TYPES_BY_NAME.typeFor(shortName);
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
|
||||
public static String withoutPackageName(String fullTypeName) {
|
||||
type = PRIMITIVE_TYPE_NAMES.typeFor(shortName);
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
|
||||
int dotPos = fullTypeName.lastIndexOf('.');
|
||||
return CollectionUtil.getCollectionTypeFor(shortName);
|
||||
}
|
||||
|
||||
return dotPos > 0 ? fullTypeName.substring(dotPos + 1) : fullTypeName;
|
||||
}
|
||||
/**
|
||||
* Returns the abbreviated name of the type, without the package name
|
||||
*
|
||||
* @param fullTypeName
|
||||
* @return String
|
||||
*/
|
||||
|
||||
public static String withoutPackageName(String fullTypeName) {
|
||||
|
||||
int dotPos = fullTypeName.lastIndexOf('.');
|
||||
return dotPos > 0 ? fullTypeName.substring(dotPos + 1) : fullTypeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to return the specified method from the class provided but will
|
||||
* walk up its superclasses until it finds a match. Returns null if it
|
||||
* doesn't.
|
||||
*
|
||||
* @param clasz Class
|
||||
* @param methodName String
|
||||
* @param paramTypes Class[]
|
||||
* @return Method
|
||||
*/
|
||||
public static Method methodFor(Class clasz, String methodName, Class[] paramTypes) {
|
||||
|
||||
Method method = null;
|
||||
Class current = clasz;
|
||||
while (current != Object.class) {
|
||||
try {
|
||||
method = current.getDeclaredMethod(methodName, paramTypes);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
if (method != null) { return method; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +1,158 @@
|
||||
package net.sourceforge.pmd.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A specialized map that stores classes by both their full and short names.
|
||||
* A specialized map that stores types by both their full and short (without package prefixes) names.
|
||||
* If an incoming type shares the same name (but different package/prefix) with a type already in the
|
||||
* map then an IllegalArgumentException will be thrown since any subsequent retrievals by said short
|
||||
* name could be in error.
|
||||
*
|
||||
* @author Brian Remedios
|
||||
*/
|
||||
public class TypeMap {
|
||||
|
||||
private Map<String, Class<?>> typesByName;
|
||||
private Map<String, Class<?>> typesByName;
|
||||
|
||||
/**
|
||||
* Constructor for TypeMap.
|
||||
* @param initialSize int
|
||||
*/
|
||||
public TypeMap(int initialSize) {
|
||||
typesByName = new HashMap<String, Class<?>>(initialSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for TypeMap that takes in an initial set of types.
|
||||
*
|
||||
* @param types Class[]
|
||||
*/
|
||||
public TypeMap(Class<?>... types) {
|
||||
this(types.length);
|
||||
add(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a type to the receiver and stores it keyed by both its full
|
||||
* and short names.
|
||||
*
|
||||
* @param type Class
|
||||
*/
|
||||
public void add(Class<?> type) {
|
||||
typesByName.put(type.getName(), type);
|
||||
typesByName.put(ClassUtil.withoutPackageName(type.getName()), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the type is known to the receiver.
|
||||
*
|
||||
* @param type Class
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean contains(Class<?> type) {
|
||||
return typesByName.containsValue(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the typeName is known to the receiver.
|
||||
*
|
||||
* @param typeName String
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean contains(String typeName) {
|
||||
return typesByName.containsKey(typeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type for the typeName specified.
|
||||
*
|
||||
* @param typeName String
|
||||
* @return Class
|
||||
*/
|
||||
public Class<?> typeFor(String typeName) {
|
||||
return typesByName.get(typeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an array of types to the receiver at once.
|
||||
*
|
||||
* @param types Class[]
|
||||
*/
|
||||
public void add(Class<?>... types) {
|
||||
for (Class<?> element : types) {
|
||||
add(element);
|
||||
/**
|
||||
* Constructor for TypeMap.
|
||||
*
|
||||
* @param initialSize int
|
||||
*/
|
||||
public TypeMap(int initialSize) {
|
||||
typesByName = new HashMap<String, Class<?>>(initialSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for TypeMap that takes in an initial set of types.
|
||||
*
|
||||
* @param types Class[]
|
||||
*/
|
||||
public TypeMap(Class<?>... types) {
|
||||
this(types.length);
|
||||
add(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a type to the receiver and stores it keyed by both its full and
|
||||
* short names. Throws an exception if the short name of the argument
|
||||
* matches an existing one already in the map for a different class.
|
||||
*
|
||||
* @param type Class
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
||||
public void add(Class<?> type) {
|
||||
|
||||
final String shortName = ClassUtil.withoutPackageName(type.getName());
|
||||
Class<?> existingType = typesByName.get(shortName);
|
||||
if (existingType == null) {
|
||||
typesByName.put(type.getName(), type);
|
||||
typesByName.put(shortName, type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingType != type) {
|
||||
throw new IllegalArgumentException(
|
||||
"Short name collision between existing " +
|
||||
existingType + " and new " + type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the type is known to the receiver.
|
||||
*
|
||||
* @param type Class
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean contains(Class<?> type) {
|
||||
return typesByName.containsValue(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the typeName is known to the receiver.
|
||||
*
|
||||
* @param typeName String
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean contains(String typeName) {
|
||||
return typesByName.containsKey(typeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type for the typeName specified.
|
||||
*
|
||||
* @param typeName String
|
||||
* @return Class
|
||||
*/
|
||||
public Class<?> typeFor(String typeName) {
|
||||
return typesByName.get(typeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an array of types to the receiver at once.
|
||||
*
|
||||
* @param types Class[]
|
||||
*/
|
||||
public void add(Class<?>... types) {
|
||||
for (Class<?> element : types) {
|
||||
add(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a map of short type names (without the package
|
||||
* prefixes) keyed by the classes themselves.
|
||||
*
|
||||
* @return Map
|
||||
*/
|
||||
public Map<Class<?>, String> asInverseWithShortName() {
|
||||
|
||||
Map<Class<?>, String> inverseMap = new HashMap<Class<?>, String>(typesByName.size() / 2);
|
||||
|
||||
Iterator iter = typesByName.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) iter.next();
|
||||
storeShortest(inverseMap, entry.getValue(), (String) entry.getKey());
|
||||
}
|
||||
|
||||
return inverseMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of entries in the receiver. This will be exactly
|
||||
* twice the number of types added.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int size() {
|
||||
return typesByName.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the shorter of the incoming value or the existing value in the map
|
||||
* at the key specified.
|
||||
*
|
||||
* @param map
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
private void storeShortest(Map map, Object key, String value) {
|
||||
|
||||
String existingValue = (String) map.get(key);
|
||||
|
||||
if (existingValue == null) {
|
||||
map.put(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingValue.length() < value.length()) {
|
||||
return;
|
||||
}
|
||||
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user