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 }