/******************************************************************************* * Copyright (c) 2010-present Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stuart McCulloch (Sonatype, Inc.) - initial API and implementation *******************************************************************************/ package org.eclipse.sisu.bean; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import javax.inject.Named; import javax.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.util.Types; import junit.framework.TestCase; @SuppressWarnings( "unused" ) public class BeanPropertiesTest extends TestCase { @Retention( RetentionPolicy.RUNTIME ) @interface Metadata { String value(); } @Target( ElementType.METHOD ) @Retention( RetentionPolicy.RUNTIME ) @interface MethodMetadata { String value(); } @Target( ElementType.FIELD ) @Retention( RetentionPolicy.RUNTIME ) @interface FieldMetadata { String value(); } @Target( { ElementType.FIELD, ElementType.METHOD } ) @Retention( RetentionPolicy.RUNTIME ) @interface MultiMetadata { String value(); } static interface A { String name = ""; void setName( String name ); } static class B { static void setName( final String name ) { } } static class C { final String name = ""; } static class D { public D() { } private void setName( final String name ) { } } static class E { void setName() { } void setName( final String firstName, final String lastName ) { } void name( final String _name ) { } } static class F { void setName( final String name ) { } void setName() { } String name; void setName( final String firstName, final String lastName ) { } void name( final String _name ) { } } static class G { List<String> names; void setMap( final Map<BigDecimal, Float> map ) { } } static abstract class IBase<T> { public abstract void setId( T id ); } static class H extends IBase<String> { private volatile String vid = "test"; private static Internal internal = new Internal(); static class Internal { String m_id; } @Override public void setId( final String _id ) { internal.m_id = _id; } @Override public String toString() { return vid + "@" + internal.m_id; } } static class I { @Singleton @Named( "bar" ) String bar; @Singleton @Named( "foo" ) void setFoo( final String foo ) { } } static class J { String a; String b; String c; } static class K { void setName( final String name ) { throw new RuntimeException(); } } static class L { void setter( final String value ) { } } static class M { void set( final String value ) { } } static class N { @Metadata( "Field1" ) @MultiMetadata( "A" ) @FieldMetadata( "1" ) int value; @MethodMetadata( "1" ) void init() { } @MethodMetadata( "2" ) void setValue( final int value ) { this.value = value; } } static class O1 { private String a1; void setA( final String a ) { } } @IgnoreSetters static class O2 extends O1 { private String b2; void setB( final String b ) { } } static class O3 extends O2 { void setC( final String c ) { } } public void testInterface() { for ( final BeanProperty<?> bp : new BeanProperties( A.class ) ) { fail( "Expected no bean properties" ); } } public void testEmptyClass() { for ( final BeanProperty<?> bp : new BeanProperties( B.class ) ) { fail( "Expected no bean properties" ); } } public void testPropertyField() { final Iterator<BeanProperty<Object>> i = new BeanProperties( C.class ).iterator(); assertEquals( "name", i.next().getName() ); assertFalse( i.hasNext() ); } public void testPropertySetter() { final Iterator<BeanProperty<Object>> i = new BeanProperties( D.class ).iterator(); assertEquals( "name", i.next().getName() ); assertFalse( i.hasNext() ); } public void testHashCodeAndEquals() throws Exception { final BeanProperty<Object> propertyField = new BeanProperties( C.class ).iterator().next(); final BeanProperty<Object> propertySetter = new BeanProperties( D.class ).iterator().next(); assertEquals( propertyField, propertyField ); assertEquals( propertySetter, propertySetter ); assertFalse( propertyField.equals( propertySetter ) ); assertFalse( propertySetter.equals( propertyField ) ); final Field field = C.class.getDeclaredField( "name" ); final Method setter = D.class.getDeclaredMethod( "setName", String.class ); assertEquals( propertyField, new BeanPropertyField<Object>( field ) ); assertEquals( propertySetter, new BeanPropertySetter<Object>( setter ) ); assertFalse( propertyField.equals( new BeanPropertyField<Object>( F.class.getDeclaredField( "name" ) ) ) ); assertFalse( propertySetter.equals( new BeanPropertySetter<Object>( F.class.getDeclaredMethod( "setName", String.class ) ) ) ); assertEquals( field.hashCode(), propertyField.hashCode() ); assertEquals( setter.hashCode(), propertySetter.hashCode() ); assertEquals( field.toString(), propertyField.toString() ); assertEquals( setter.toString(), propertySetter.toString() ); } public void testSkipInvalidSetters() { for ( final BeanProperty<?> bp : new BeanProperties( E.class ) ) { fail( "Expected no bean properties" ); } } public void testPropertyCombination() { final Iterator<BeanProperty<Object>> i = new BeanProperties( F.class ).iterator(); BeanProperty<Object> bp; bp = i.next(); assertEquals( "name", bp.getName() ); assertTrue( bp instanceof BeanPropertySetter<?> ); bp = i.next(); assertEquals( "name", bp.getName() ); assertTrue( bp instanceof BeanPropertyField<?> ); assertFalse( i.hasNext() ); try { i.next(); fail( "Expected NoSuchElementException" ); } catch ( final NoSuchElementException e ) { } try { i.remove(); fail( "Expected UnsupportedOperationException" ); } catch ( final UnsupportedOperationException e ) { } } public void testConstructor() throws NoSuchMethodException { final Iterable<Member> members = Collections.singleton( (Member) String.class.getConstructor() ); final Iterator<BeanProperty<Object>> i = new BeanProperties( members ).iterator(); assertFalse( i.hasNext() ); } public void testPropertyType() { final Iterator<BeanProperty<Object>> i = new BeanProperties( G.class ).iterator(); assertEquals( TypeLiteral.get( Types.mapOf( BigDecimal.class, Float.class ) ), i.next().getType() ); assertEquals( TypeLiteral.get( Types.listOf( String.class ) ), i.next().getType() ); } public void testPropertyUpdate() { final Iterator<BeanProperty<Object>> i = new BeanProperties( H.class ).iterator(); final BeanProperty<Object> a = i.next(); final BeanProperty<Object> b = i.next(); assertFalse( i.hasNext() ); final H component = new H(); a.set( component, "bar" ); b.set( component, "foo" ); assertEquals( "foo@bar", component.toString() ); b.set( component, "abc" ); a.set( component, "xyz" ); assertEquals( "abc@xyz", component.toString() ); } public void testIllegalAccess() { try { final BeanProperty<Object> p = new BeanPropertyField<Object>( A.class.getDeclaredField( "name" ) ); p.set( new Object(), "test" ); fail( "Expected RuntimeException" ); } catch ( final NoSuchFieldException e ) { fail( e.toString() ); } catch ( final RuntimeException e ) { e.printStackTrace(); } try { final BeanProperty<Object> p = new BeanPropertySetter<Object>( A.class.getDeclaredMethod( "setName", String.class ) ); p.set( new Object(), "test" ); fail( "Expected RuntimeException" ); } catch ( final NoSuchMethodException e ) { fail( e.toString() ); } catch ( final RuntimeException e ) { e.printStackTrace(); } } public void testPropertyAnnotations() { final Iterator<BeanProperty<Object>> i = new BeanProperties( I.class ).iterator(); assertEquals( "foo", i.next().getAnnotation( Named.class ).value() ); assertEquals( "bar", i.next().getAnnotation( Named.class ).value() ); assertFalse( i.hasNext() ); } public void testPropertyIteration() { final Iterator<BeanProperty<Object>> i = new BeanProperties( J.class ).iterator(); assertTrue( i.hasNext() ); assertTrue( i.hasNext() ); assertEquals( "c", i.next().getName() ); assertTrue( i.hasNext() ); assertTrue( i.hasNext() ); assertEquals( "b", i.next().getName() ); assertTrue( i.hasNext() ); assertTrue( i.hasNext() ); assertEquals( "a", i.next().getName() ); assertFalse( i.hasNext() ); assertFalse( i.hasNext() ); } public void testBadPropertySetter() { try { final Iterator<BeanProperty<Object>> i = new BeanProperties( K.class ).iterator(); i.next().set( new K(), "TEST" ); fail( "Expected RuntimeException" ); } catch ( final RuntimeException e ) { e.printStackTrace(); } } public void testSetterNames() { assertFalse( new BeanProperties( L.class ).iterator().hasNext() ); assertFalse( new BeanProperties( M.class ).iterator().hasNext() ); } public void testIgnoreSetters() { final Iterator<BeanProperty<Object>> i = new BeanProperties( O3.class ).iterator(); assertTrue( i.hasNext() ); assertEquals( "b2", i.next().getName() ); assertTrue( i.hasNext() ); assertEquals( "a1", i.next().getName() ); assertFalse( i.hasNext() ); } }