Author: tchemit Date: 2013-03-12 17:07:04 +0100 (Tue, 12 Mar 2013) New Revision: 2527 Url: http://nuiton.org/projects/nuiton-utils/repository/revisions/2527 Log: fixes #2587: [BeanUtil] Add method to filter descriptors Modified: trunk/nuiton-utils/pom.xml trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BeanUtil.java trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanUtilTest.java Modified: trunk/nuiton-utils/pom.xml =================================================================== --- trunk/nuiton-utils/pom.xml 2013-03-12 10:28:51 UTC (rev 2526) +++ trunk/nuiton-utils/pom.xml 2013-03-12 16:07:04 UTC (rev 2527) @@ -85,6 +85,11 @@ </dependency> <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + + <dependency> <groupId>org.nuiton.i18n</groupId> <artifactId>nuiton-i18n</artifactId> </dependency> Modified: trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BeanUtil.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BeanUtil.java 2013-03-12 10:28:51 UTC (rev 2526) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BeanUtil.java 2013-03-12 16:07:04 UTC (rev 2527) @@ -24,6 +24,10 @@ */ package org.nuiton.util.beans; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.apache.commons.beanutils.MethodUtils; import org.apache.commons.beanutils.PropertyUtils; @@ -31,6 +35,7 @@ import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.HashSet; +import java.util.Map; import java.util.Set; /** @@ -52,6 +57,40 @@ } /** + * Is a property is readable ? + * + * @since 2.5.11 + */ + public static final Predicate<PropertyDescriptor> IS_READ_DESCRIPTOR = new Predicate<PropertyDescriptor>() { + @Override + public boolean apply(PropertyDescriptor input) { + return input.getReadMethod() != null; + } + }; + + /** + * Is a property is writable ? + * + * @since 2.5.11 + */ + public static final Predicate<PropertyDescriptor> IS_WRITE_DESCRIPTOR = new Predicate<PropertyDescriptor>() { + @Override + public boolean apply(PropertyDescriptor input) { + return input.getReadMethod() != null; + } + }; + + /** + * Is a property is readable and writable ? + * + * @since 2.5.11 + */ + public static final Predicate<PropertyDescriptor> IS_READ_AND_WRITE_DESCRIPTOR = Predicates.and( + IS_READ_DESCRIPTOR, + IS_WRITE_DESCRIPTOR + ); + + /** * Test if the given type is JavaBean compiliant, says that it has two * public methods : * <ul> @@ -189,7 +228,7 @@ result = false; } else { - String rest = propertyName.substring(dotIndex+1); + String rest = propertyName.substring(dotIndex + 1); result = isNestedReadableProperty(nestedType, rest); } @@ -245,6 +284,72 @@ return result; } + /** + * Scan the given type and obtain {@link PropertyDescriptor} + * given the (optional) predicate (except the {@code class} property of + * any java object). + * <p/> + * <strong>Note:</strong> If no predicate is given, then all descriptors + * are returned. + * + * @param beanType the bean type to scan + * @param predicate the optional predicate to keep descriptor + * @return set of all matching descriptors + * @since 2.6.11 + */ + public static Set<PropertyDescriptor> getDescriptors(Class<?> beanType, + Predicate<PropertyDescriptor> predicate) { + + if (predicate == null) { + predicate = Predicates.alwaysTrue(); + } + + Set<Class<?>> exploredTypes = Sets.newHashSet(); + Map<String, PropertyDescriptor> result = Maps.newTreeMap(); + + getDescriptors(beanType, predicate, result, exploredTypes); + + // the special getClass will never be a JavaBean property... + result.remove("class"); + + return Sets.newHashSet(result.values()); + } + + protected static void getDescriptors(Class<?> beanType, + Predicate<PropertyDescriptor> predicate, + Map<String, PropertyDescriptor> result, + Set<Class<?>> exploredTypes) { + + if (exploredTypes.contains(beanType)) { + + // already explored + return; + } + exploredTypes.add(beanType); + + PropertyDescriptor[] descriptors = + PropertyUtils.getPropertyDescriptors(beanType); + + for (PropertyDescriptor descriptor : descriptors) { + String name = descriptor.getName(); + if (!result.containsKey(name) && predicate.apply(descriptor)) { + result.put(name, descriptor); + } + } + + if (beanType.getSuperclass() != null) { + + // get properties fro super-class + getDescriptors(beanType.getSuperclass(), predicate, result, exploredTypes); + } + Class<?>[] interfaces = beanType.getInterfaces(); + for (Class<?> anInterface : interfaces) { + + // get properties fro super-class + getDescriptors(anInterface, predicate, result, exploredTypes); + } + } + protected static void getReadableProperties(Class<?> beanType, Set<String> result, Set<Class<?>> exploredTypes) { Modified: trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanUtilTest.java =================================================================== --- trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanUtilTest.java 2013-03-12 10:28:51 UTC (rev 2526) +++ trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanUtilTest.java 2013-03-12 16:07:04 UTC (rev 2527) @@ -24,10 +24,15 @@ */ package org.nuiton.util.beans; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import org.junit.Assert; import org.junit.Test; import java.beans.PropertyChangeListener; +import java.beans.PropertyDescriptor; import java.beans.beancontext.BeanContextSupport; import java.lang.reflect.InvocationTargetException; import java.util.Set; @@ -86,6 +91,66 @@ } @Test + public void getDescriptors() { + + assertFoundDescriptors(BeanA.class, + BeanUtil.IS_READ_DESCRIPTOR, + BeanA.PROPERTY_A, + BeanA.PROPERTY_B, + BeanA.PROPERTY_C, + BeanA.PROPERTY_D, + BeanA.PROPERTY_E, + BeanA.PROPERTY_F); + + assertFoundDescriptors(BeanB.class, + BeanUtil.IS_READ_DESCRIPTOR, + BeanA.PROPERTY_A, + BeanA.PROPERTY_B, + BeanA.PROPERTY_C, + BeanA.PROPERTY_D, + BeanA.PROPERTY_E, + BeanA.PROPERTY_F, + BeanB.PROPERTY_BB, + BeanB.PROPERTY_A2, + BeanB.PROPERTY_B2, + BeanB.PROPERTY_C2, + BeanB.PROPERTY_D2, + BeanB.PROPERTY_E2, + BeanB.PROPERTY_F2 + ); + + assertFoundDescriptors(getClass(), BeanUtil.IS_READ_DESCRIPTOR); + + assertFoundDescriptors(BeanA.class, + BeanUtil.IS_WRITE_DESCRIPTOR, + BeanA.PROPERTY_A, + BeanA.PROPERTY_B, + BeanA.PROPERTY_C, + BeanA.PROPERTY_D, + BeanA.PROPERTY_E, + BeanA.PROPERTY_F); + + assertFoundDescriptors(BeanB.class, + BeanUtil.IS_WRITE_DESCRIPTOR, + BeanA.PROPERTY_A, + BeanA.PROPERTY_B, + BeanA.PROPERTY_C, + BeanA.PROPERTY_D, + BeanA.PROPERTY_E, + BeanA.PROPERTY_F, + BeanB.PROPERTY_BB, + BeanB.PROPERTY_A2, + BeanB.PROPERTY_B2, + BeanB.PROPERTY_C2, + BeanB.PROPERTY_D2, + BeanB.PROPERTY_E2, + BeanB.PROPERTY_F2 + ); + + assertFoundDescriptors(getClass(), BeanUtil.IS_WRITE_DESCRIPTOR); + } + + @Test public void getWriteableProperties() { assertFoundWriteableProperties(BeanA.class, @@ -233,4 +298,22 @@ Assert.assertTrue("Did not found nested property " + expectedproperty, actual); } } + + protected void assertFoundDescriptors(Class<?> type, + Predicate<PropertyDescriptor> predicate, + String... expectedproperties) { + Set<PropertyDescriptor> actual = BeanUtil.getDescriptors(type, predicate); + Assert.assertEquals(expectedproperties.length, actual.size()); + + ImmutableMap<String, PropertyDescriptor> map = Maps.uniqueIndex(actual, new Function<PropertyDescriptor, String>() { + @Override + public String apply(PropertyDescriptor input) { + return input.getName(); + } + }); + for (String expectedproperty : expectedproperties) { + Assert.assertTrue("Did not found property " + expectedproperty, + map.containsKey(expectedproperty)); + } + } }