Author: tchemit Date: 2012-01-19 12:45:45 +0100 (Thu, 19 Jan 2012) New Revision: 2291 Url: http://nuiton.org/repositories/revision/nuiton-utils/2291 Log: Evolution #1913: Default binder should not take account of properties with different types Added: trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanAA.java Modified: trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BinderModelBuilder.java trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BinderModelBuilderTest.java Modified: trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BinderModelBuilder.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BinderModelBuilder.java 2012-01-17 12:21:21 UTC (rev 2290) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/beans/BinderModelBuilder.java 2012-01-19 11:45:45 UTC (rev 2291) @@ -161,6 +161,24 @@ */ public static <S, T> BinderModelBuilder<S, T> newDefaultBuilder(Class<S> sourceType, Class<T> targetType) { + return newDefaultBuilder(sourceType, targetType, true); + } + + /** + * Creates a new model builder and fill the model with all matching + * and available properties from the source type to the target type. + * + * @param sourceType the source type of the model to create + * @param targetType the target type of the model to create + * @param <S> the source type of the binder model to create + * @param <T> the target type of the binder model to create + * @param checkType flag to check if properties has same types, otherwise skip them + * @return the new instanciated model builder fully filled + * @since 2.4.5 + */ + public static <S, T> BinderModelBuilder<S, T> newDefaultBuilder(Class<S> sourceType, + Class<T> targetType, + boolean checkType) { BinderModelBuilder<S, T> builder = newEmptyBuilder(sourceType, targetType); Map<String, PropertyDescriptor> source = builder.sourceDescriptors; @@ -173,18 +191,32 @@ continue; } PropertyDescriptor sourceDescriptor = source.get(propertyName); - if (sourceDescriptor.getReadMethod() == null) { + Method readMethod = sourceDescriptor.getReadMethod(); + if (readMethod == null) { // no getter on source, do not use this property continue; } PropertyDescriptor targetDescriptor = target.get(propertyName); - if (targetDescriptor.getWriteMethod() == null) { + Method writeMethod = targetDescriptor.getWriteMethod(); + if (writeMethod == null) { // no setter on target, do not use this property continue; } + if (checkType) { + + // check types are compatible + + Class<?> writerType = writeMethod.getParameterTypes()[0]; + Class<?> readerType = readMethod.getReturnType(); + if (!writerType.equals(readerType)) { + + // types are not compatible + continue; + } + } // can safely use this property properties.add(propertyName); } @@ -308,7 +340,7 @@ if (sourceAndTargetProperties.length % 2 != 0) { throw new IllegalArgumentException( "must have couple(s) of sourceProperty,targetProperty) " + - "but had " + Arrays.toString(sourceAndTargetProperties)); + "but had " + Arrays.toString(sourceAndTargetProperties)); } for (int i = 0, max = sourceAndTargetProperties.length / 2; i < max; i++) { @@ -317,12 +349,12 @@ if (sourceProperty == null) { throw new NullPointerException( "parameter 'sourceAndTargetProperties' can not " + - "contains a null value"); + "contains a null value"); } if (targetProperty == null) { throw new NullPointerException( "parameter 'sourceAndTargetProperties' can not " + - "contains a null value"); + "contains a null value"); } addProperty0(sourceProperty, targetProperty); } @@ -335,7 +367,7 @@ if (!model.containsSourceProperty(propertyName)) { throw new IllegalArgumentException( "source property '" + propertyName + "' " + - " is NOT registred."); + " is NOT registred."); } // check property is the same type of given binder @@ -343,11 +375,11 @@ Class<?> type = descriptor.getPropertyType(); if (!Collection.class.isAssignableFrom(type) && - !binder.model.getSourceType().isAssignableFrom(type)) { + !binder.model.getSourceType().isAssignableFrom(type)) { throw new IllegalStateException( "source property '" + propertyName + - "' has not the same type [" + type + - "] of the binder [" + binder.model.getSourceType() + "]."); + "' has not the same type [" + type + + "] of the binder [" + binder.model.getSourceType() + "]."); } // can safely add the strategy @@ -365,7 +397,7 @@ if (!model.containsSourceProperty(propertyName)) { throw new IllegalArgumentException( "source property '" + propertyName + "' " + - " is NOT registred."); + " is NOT registred."); } // check property is collection type @@ -374,7 +406,7 @@ if (!Collection.class.isAssignableFrom(type)) { throw new IllegalStateException( "source property '" + propertyName + - "' is not a collection type [" + type + "]"); + "' is not a collection type [" + type + "]"); } // can safely add the strategy @@ -400,7 +432,7 @@ if (model != null) { throw new IllegalStateException( "there is already a binderModel in construction, release " + - "it with the method createBinder before using this method." + "it with the method createBinder before using this method." ); } @@ -425,14 +457,14 @@ sourceDescriptors.get(sourceProperty); if (sourceDescriptor == null) { throw new IllegalArgumentException("no property '" + - sourceProperty + "' " + "found on type " + - model.getSourceType()); + sourceProperty + "' " + "found on type " + + model.getSourceType()); } // check srcProperty is readable Method readMethod = sourceDescriptor.getReadMethod(); if (readMethod == null) { throw new IllegalArgumentException("property '" + sourceProperty + - "' " + "is not readable on type " + model.getSourceType()); + "' " + "is not readable on type " + model.getSourceType()); } // obtain dst descriptor @@ -440,14 +472,14 @@ targetDescriptors.get(targetProperty); if (targetDescriptor == null) { throw new IllegalArgumentException("no property '" + - targetProperty + "' " + "found on type " + - model.getTargetType()); + targetProperty + "' " + "found on type " + + model.getTargetType()); } // check dstProperty is writable Method writeMethod = targetDescriptor.getWriteMethod(); if (writeMethod == null) { throw new IllegalArgumentException("property '" + targetProperty + - "' " + "is not writable on type " + model.getTargetType()); + "' " + "is not writable on type " + model.getTargetType()); } // check types are ok @@ -456,9 +488,9 @@ //TODO-TC20100221 : should check if primitive and boxed it in such case if (!sourceType.equals(targetType)) { throw new IllegalArgumentException("source property '" + - sourceProperty + "' and target property '" + - targetProperty + "' are not compatible ( sourceType : " + - sourceType + " vs targetType :" + targetType + ')'); + sourceProperty + "' and target property '" + + targetProperty + "' are not compatible ( sourceType : " + + sourceType + " vs targetType :" + targetType + ')'); } // check srcProperty does not exist @@ -474,7 +506,7 @@ // property for the entry and this is a bit unatural if (model.containsTargetProperty(targetProperty)) { throw new IllegalArgumentException("destination property '" + - targetProperty + "' " + " was already registred."); + targetProperty + "' " + " was already registred."); } // safe to add the binding model.addBinding(sourceDescriptor, targetDescriptor); @@ -505,7 +537,7 @@ } } catch (IntrospectionException e) { throw new RuntimeException("Could not obtain bean properties " + - "descriptors for source type " + type, e); + "descriptors for source type " + type, e); } Class<?>[] interfaces = type.getInterfaces(); for (Class<?> i : interfaces) { Copied: trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanAA.java (from rev 2290, trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanA.java) =================================================================== --- trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanAA.java (rev 0) +++ trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BeanAA.java 2012-01-19 11:45:45 UTC (rev 2291) @@ -0,0 +1,143 @@ +/* + * #%L + * Nuiton Utils + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.util.beans; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +public class BeanAA { + + public static final String PROPERTY_AA = "aa"; + + public static final String PROPERTY_A = "a"; + + public static final String PROPERTY_B = "b"; + + public static final String PROPERTY_C = "c"; + + public static final String PROPERTY_D = "d"; + + public static final String PROPERTY_E = "e"; + + public static final String PROPERTY_F = "f"; + + protected String aa, b, c, d; + + protected int a, e, f; + + protected PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + public void setAa(String aa) { + this.aa = aa; + } + + public int getA() { + return a; + } + + public void setA(int a) { + Object oldValue = this.a; + this.a = a; + firePropertyChange(PROPERTY_A, oldValue, a); + } + + public String getB() { + return b; + } + + public void setB(String b) { + Object oldValue = this.b; + this.b = b; + firePropertyChange(PROPERTY_B, oldValue, b); + } + + public String getC() { + return c; + } + + public void setC(String c) { + Object oldValue = this.c; + this.c = c; + firePropertyChange(PROPERTY_C, oldValue, c); + } + + public String getD() { + return d; + } + + public void setD(String d) { + Object oldValue = this.d; + this.d = d; + firePropertyChange(PROPERTY_D, oldValue, d); + } + + public int getE() { + return e; + } + + public void setE(int e) { + Object oldValue = this.e; + this.e = e; + firePropertyChange(PROPERTY_E, oldValue, e); + } + + public int getF() { + return f; + } + + public void setF(int f) { + Object oldValue = this.f; + this.f = f; + firePropertyChange(PROPERTY_F, oldValue, f); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener) { + pcs.addPropertyChangeListener(propertyName, listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener) { + pcs.removePropertyChangeListener(propertyName, listener); + } + + protected void firePropertyChange(String propertyName, Object oldValue, + Object newValue) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + + protected PropertyChangeListener[] getPropertyChangeListeners() { + return pcs.getPropertyChangeListeners(); + } +} Modified: trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BinderModelBuilderTest.java =================================================================== --- trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BinderModelBuilderTest.java 2012-01-17 12:21:21 UTC (rev 2290) +++ trunk/nuiton-utils/src/test/java/org/nuiton/util/beans/BinderModelBuilderTest.java 2012-01-19 11:45:45 UTC (rev 2291) @@ -560,4 +560,32 @@ Assert.assertEquals(salutationCode, destination.getGender()); } + @Test + public void testEvol1913() throws Exception { + + // This is test for http://nuiton.org/issues/1913 + + // A --> AA + + BinderModelBuilder<BeanA, BeanAA> builderAAA; + + builderAAA = BinderModelBuilder.newDefaultBuilder(BeanA.class, BeanAA.class); + assertBuilder(builderAAA, BeanA.class, BeanAA.class); + assertDescriptor(builderAAA.getModel().propertiesMapping, + BeanA.PROPERTY_B, + BeanA.PROPERTY_C, + BeanA.PROPERTY_D, + BeanA.PROPERTY_E, + BeanA.PROPERTY_F + ); + + try { + // property a is not the same, will fail + builderAAA = BinderModelBuilder.newDefaultBuilder(BeanA.class, BeanAA.class, false); + Assert.fail(); + } catch (IllegalArgumentException e) { + Assert.assertTrue(true); + } + } + }