001    /*
002     * Copyright 2005,2009 Ivan SZKIBA
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.ini4j.spi;
017    
018    import org.ini4j.Ini4jCase;
019    
020    import org.ini4j.sample.Dwarf;
021    
022    import org.ini4j.test.Helper;
023    
024    import static org.junit.Assert.assertEquals;
025    import static org.junit.Assert.assertFalse;
026    import static org.junit.Assert.assertNotNull;
027    import static org.junit.Assert.assertNull;
028    import static org.junit.Assert.assertSame;
029    import static org.junit.Assert.assertTrue;
030    import static org.junit.Assert.fail;
031    
032    import org.junit.Test;
033    
034    import java.beans.PropertyChangeEvent;
035    import java.beans.PropertyChangeListener;
036    import java.beans.PropertyVetoException;
037    import java.beans.VetoableChangeListener;
038    
039    import java.lang.reflect.Proxy;
040    
041    import java.util.HashMap;
042    import java.util.Map;
043    
044    public class AbstractBeanInvocationHandlerTest extends Ini4jCase
045    {
046        private static final String PROP_AGE = Dwarf.PROP_AGE;
047        private static final String PROP_HEIGHT = Dwarf.PROP_HEIGHT;
048    
049        @Test public void testGetProperty() throws Exception
050        {
051            Map<String, String> map = new HashMap<String, String>();
052            MapBeanHandler handler = new MapBeanHandler(map);
053            Integer i = new Integer(23);
054    
055            map.put(PROP_AGE, "23");
056            assertEquals(i, (Integer) handler.getProperty(PROP_AGE, Integer.class));
057            assertTrue(handler.hasProperty(PROP_AGE));
058            assertFalse(handler.hasProperty(null));
059            map.put(PROP_AGE, "?.");
060            assertEquals(null, handler.getProperty(PROP_AGE, Integer.class));
061            assertEquals("?.", (String) handler.getProperty(PROP_AGE, String.class));
062            handler = new MapBeanHandler(map)
063            {
064                @Override protected boolean hasPropertySpi(String property)
065                {
066                    throw new UnsupportedOperationException();
067                }
068            };
069            assertFalse(handler.hasProperty(PROP_AGE));
070        }
071    
072        @Test public void testGetSetHas() throws Exception
073        {
074            Dwarf dwarf = MapBeanHandler.newBean(Dwarf.class);
075    
076            assertFalse(dwarf.hasAge());
077            dwarf.setAge(23);
078            assertEquals(23, dwarf.getAge());
079            assertNull(dwarf.getHomeDir());
080            dwarf.setHomeDir("dummy");
081        }
082    
083        @Test public void testMisc() throws Exception
084        {
085            Map<String, String> map = new HashMap<String, String>();
086            MapBeanHandler handler = new MapBeanHandler(map);
087            Dummy dummy = (Dummy) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Dummy.class }, handler);
088    
089            assertNull(handler.getProxy());
090    
091            // non existend method calls
092            dummy.dummy();
093            dummy.addDummy();
094            dummy.removeDummy();
095    
096            // boolean invoke
097            map.put("dummy", "true");
098            assertTrue(dummy.isDummy());
099            assertSame(dummy, handler.getProxy());
100    
101            // subclass should call fire methods any time
102            // so null support reference should be not a problem
103            handler.firePropertyChange(PROP_AGE, Integer.valueOf(1), Integer.valueOf(2));
104            handler.fireVetoableChange(PROP_AGE, Integer.valueOf(1), Integer.valueOf(2));
105        }
106    
107        @Test public void testPropertyChangeListener() throws Exception
108        {
109            class Listener implements PropertyChangeListener
110            {
111                int _counter;
112                String _property;
113                PropertyChangeEvent _event;
114    
115                Listener(String property)
116                {
117                    _property = property;
118                }
119    
120                @Override public void propertyChange(PropertyChangeEvent event)
121                {
122                    if (_property.equals(event.getPropertyName()))
123                    {
124                        _counter++;
125                        _event = event;
126                    }
127                }
128            }
129    
130            Dwarf d = MapBeanHandler.newBean(Dwarf.class);
131            Listener l = new Listener(PROP_AGE);
132    
133            // test add and remove: invalid state should be OK
134            d.removePropertyChangeListener(PROP_AGE, l);
135            d.addPropertyChangeListener(PROP_AGE, l);
136            d.addPropertyChangeListener(PROP_AGE, l);
137            d.removePropertyChangeListener(PROP_AGE, l);
138            d.removePropertyChangeListener(PROP_AGE, l);
139    
140            // check listener call
141            d.setAge(23);
142            d.addPropertyChangeListener(PROP_AGE, l);
143            d.setAge(45);
144            assertNotNull(l._event);
145            assertEquals(23, ((Integer) l._event.getOldValue()).intValue());
146            assertEquals(45, ((Integer) l._event.getNewValue()).intValue());
147    
148            // check listener call again
149            d.setAge(2);
150            d.setWeight(23.4);
151            assertEquals(2, l._counter);
152            d.removePropertyChangeListener(PROP_AGE, l);
153    
154            // should not run listener
155            d.setAge(44);
156            assertEquals(2, l._counter);
157    
158            // test remove listener> invalid state should be OK
159            d.removePropertyChangeListener(PROP_AGE, l);
160        }
161    
162        @Test public void testSetProperty() throws Exception
163        {
164            Map<String, String> map = new HashMap<String, String>();
165            MapBeanHandler handler = new MapBeanHandler(map);
166    
167            // very special case: set string property to non stirng value implies string conversion
168            handler.setProperty(PROP_AGE, new Integer(23), String.class);
169            assertEquals("23", handler.getProperty(PROP_AGE, String.class));
170        }
171    
172        @Test public void testVetoableChangeListener() throws Exception
173        {
174            class HeightCheck implements VetoableChangeListener
175            {
176                @Override public void vetoableChange(PropertyChangeEvent event) throws PropertyVetoException
177                {
178                    if (PROP_HEIGHT.equals(event.getPropertyName()))
179                    {
180                        if (((Double) event.getNewValue()) < 0.0)
181                        {
182                            throw new PropertyVetoException("invalid value", event);
183                        }
184                    }
185                }
186            }
187    
188            Dwarf d = MapBeanHandler.newBean(Dwarf.class);
189            HeightCheck l = new HeightCheck();
190    
191            // test add and remove: invalid state should be OK
192            d.removeVetoableChangeListener(PROP_HEIGHT, l);
193            d.addVetoableChangeListener(PROP_HEIGHT, l);
194            d.addVetoableChangeListener(PROP_HEIGHT, l);
195            d.removeVetoableChangeListener(PROP_HEIGHT, l);
196            d.removeVetoableChangeListener(PROP_HEIGHT, l);
197    
198            // set invalid value without lsitener
199            d.setHeight(-2.0);
200            d.setHeight(33.0);
201            d.addVetoableChangeListener(PROP_HEIGHT, l);
202    
203            // set invalid value with listener
204            try
205            {
206                d.setHeight(-3.4);
207                fail();
208            }
209            catch (PropertyVetoException x)
210            {
211                assertEquals(33.0, d.getHeight(), Helper.DELTA);
212            }
213    
214            // set valid value
215            d.setHeight(44.0);
216            assertEquals(44.0, d.getHeight(), Helper.DELTA);
217            d.removeVetoableChangeListener(PROP_HEIGHT, l);
218    
219            // set invalid value without lsitener
220            d.setHeight(-4.0);
221            assertEquals(-4.0, d.getHeight(), Helper.DELTA);
222    
223            // test remove: invalid state should be OK
224            d.removeVetoableChangeListener(PROP_HEIGHT, l);
225        }
226    
227        static interface Dummy
228        {
229            boolean isDummy();
230    
231            void addDummy();
232    
233            void dummy();
234    
235            void removeDummy();
236        }
237    
238        static class MapBeanHandler extends AbstractBeanInvocationHandler
239        {
240            private Map<String, String> _map;
241    
242            MapBeanHandler(Map<String, String> map)
243            {
244                super();
245                _map = map;
246            }
247    
248            protected static <T> T newBean(Class<T> clazz)
249            {
250                return newBean(clazz, new HashMap<String, String>());
251            }
252    
253            protected static <T> T newBean(Class<T> clazz, Map<String, String> map)
254            {
255                return clazz.cast(Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new MapBeanHandler(map)));
256            }
257    
258            @Override protected Object getPropertySpi(String property, Class clazz)
259            {
260                return _map.get(property);
261            }
262    
263            @Override protected void setPropertySpi(String property, Object value, Class clazz)
264            {
265                _map.put(property, value.toString());
266            }
267    
268            @Override protected boolean hasPropertySpi(String property)
269            {
270                return _map.containsKey(property);
271            }
272        }
273    }