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 */
016package org.ini4j.spi;
017
018import org.ini4j.Ini4jCase;
019
020import org.ini4j.sample.Dwarf;
021
022import org.ini4j.test.Helper;
023
024import static org.junit.Assert.assertEquals;
025import static org.junit.Assert.assertFalse;
026import static org.junit.Assert.assertNotNull;
027import static org.junit.Assert.assertNull;
028import static org.junit.Assert.assertSame;
029import static org.junit.Assert.assertTrue;
030import static org.junit.Assert.fail;
031
032import org.junit.Test;
033
034import java.beans.PropertyChangeEvent;
035import java.beans.PropertyChangeListener;
036import java.beans.PropertyVetoException;
037import java.beans.VetoableChangeListener;
038
039import java.lang.reflect.Proxy;
040
041import java.util.HashMap;
042import java.util.Map;
043
044public 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}