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}