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 java.beans.Introspector;
019import java.beans.PropertyChangeListener;
020import java.beans.PropertyChangeSupport;
021import java.beans.PropertyVetoException;
022import java.beans.VetoableChangeListener;
023import java.beans.VetoableChangeSupport;
024
025import java.lang.reflect.Array;
026import java.lang.reflect.InvocationHandler;
027import java.lang.reflect.Method;
028
029public abstract class AbstractBeanInvocationHandler implements InvocationHandler
030{
031    private static final String PROPERTY_CHANGE_LISTENER = "PropertyChangeListener";
032    private static final String VETOABLE_CHANGE_LISTENER = "VetoableChangeListener";
033    private static final String ADD_PREFIX = "add";
034    private static final String READ_PREFIX = "get";
035    private static final String REMOVE_PREFIX = "remove";
036    private static final String READ_BOOLEAN_PREFIX = "is";
037    private static final String WRITE_PREFIX = "set";
038    private static final String HAS_PREFIX = "has";
039
040    private static enum Prefix
041    {
042        READ(READ_PREFIX),
043        READ_BOOLEAN(READ_BOOLEAN_PREFIX),
044        WRITE(WRITE_PREFIX),
045        ADD_CHANGE(ADD_PREFIX + PROPERTY_CHANGE_LISTENER),
046        ADD_VETO(ADD_PREFIX + VETOABLE_CHANGE_LISTENER),
047        REMOVE_CHANGE(REMOVE_PREFIX + PROPERTY_CHANGE_LISTENER),
048        REMOVE_VETO(REMOVE_PREFIX + VETOABLE_CHANGE_LISTENER),
049        HAS(HAS_PREFIX);
050        private int _len;
051        private String _value;
052
053        private Prefix(String value)
054        {
055            _value = value;
056            _len = value.length();
057        }
058
059        public static Prefix parse(String str)
060        {
061            Prefix ret = null;
062
063            for (Prefix p : values())
064            {
065                if (str.startsWith(p.getValue()))
066                {
067                    ret = p;
068
069                    break;
070                }
071            }
072
073            return ret;
074        }
075
076        public String getTail(String input)
077        {
078            return Introspector.decapitalize(input.substring(_len));
079        }
080
081        public String getValue()
082        {
083            return _value;
084        }
085    }
086
087    private PropertyChangeSupport _pcSupport;
088    private Object _proxy;
089    private VetoableChangeSupport _vcSupport;
090
091    @Override public Object invoke(Object proxy, Method method, Object[] args) throws PropertyVetoException
092    {
093        Object ret = null;
094        Prefix prefix = Prefix.parse(method.getName());
095
096        if (prefix != null)
097        {
098            String tail = prefix.getTail(method.getName());
099
100            updateProxy(proxy);
101            switch (prefix)
102            {
103
104                case READ:
105                    ret = getProperty(prefix.getTail(method.getName()), method.getReturnType());
106                    break;
107
108                case READ_BOOLEAN:
109                    ret = getProperty(prefix.getTail(method.getName()), method.getReturnType());
110                    break;
111
112                case WRITE:
113                    setProperty(tail, args[0], method.getParameterTypes()[0]);
114                    break;
115
116                case HAS:
117                    ret = Boolean.valueOf(hasProperty(prefix.getTail(method.getName())));
118                    break;
119
120                case ADD_CHANGE:
121                    addPropertyChangeListener((String) args[0], (PropertyChangeListener) args[1]);
122                    break;
123
124                case ADD_VETO:
125                    addVetoableChangeListener((String) args[0], (VetoableChangeListener) args[1]);
126                    break;
127
128                case REMOVE_CHANGE:
129                    removePropertyChangeListener((String) args[0], (PropertyChangeListener) args[1]);
130                    break;
131
132                case REMOVE_VETO:
133                    removeVetoableChangeListener((String) args[0], (VetoableChangeListener) args[1]);
134                    break;
135
136                default:
137                    break;
138            }
139        }
140
141        return ret;
142    }
143
144    protected abstract Object getPropertySpi(String property, Class<?> clazz);
145
146    protected abstract void setPropertySpi(String property, Object value, Class<?> clazz);
147
148    protected abstract boolean hasPropertySpi(String property);
149
150    protected synchronized Object getProperty(String property, Class<?> clazz)
151    {
152        Object o;
153
154        try
155        {
156            o = getPropertySpi(property, clazz);
157            if (o == null)
158            {
159                o = zero(clazz);
160            }
161            else if (clazz.isArray() && (o instanceof String[]) && !clazz.equals(String[].class))
162            {
163                String[] str = (String[]) o;
164
165                o = Array.newInstance(clazz.getComponentType(), str.length);
166                for (int i = 0; i < str.length; i++)
167                {
168                    Array.set(o, i, parse(str[i], clazz.getComponentType()));
169                }
170            }
171            else if ((o instanceof String) && !clazz.equals(String.class))
172            {
173                o = parse((String) o, clazz);
174            }
175        }
176        catch (Exception x)
177        {
178            o = zero(clazz);
179        }
180
181        return o;
182    }
183
184    protected synchronized void setProperty(String property, Object value, Class<?> clazz) throws PropertyVetoException
185    {
186        boolean pc = (_pcSupport != null) && _pcSupport.hasListeners(property);
187        boolean vc = (_vcSupport != null) && _vcSupport.hasListeners(property);
188        Object oldVal = null;
189        Object newVal = ((value != null) && clazz.equals(String.class) && !(value instanceof String)) ? value.toString() : value;
190
191        if (pc || vc)
192        {
193            oldVal = getProperty(property, clazz);
194        }
195
196        if (vc)
197        {
198            fireVetoableChange(property, oldVal, value);
199        }
200
201        setPropertySpi(property, newVal, clazz);
202        if (pc)
203        {
204            firePropertyChange(property, oldVal, value);
205        }
206    }
207
208    protected synchronized Object getProxy()
209    {
210        return _proxy;
211    }
212
213    protected synchronized void addPropertyChangeListener(String property, PropertyChangeListener listener)
214    {
215        if (_pcSupport == null)
216        {
217            _pcSupport = new PropertyChangeSupport(_proxy);
218        }
219
220        _pcSupport.addPropertyChangeListener(property, listener);
221    }
222
223    protected synchronized void addVetoableChangeListener(String property, VetoableChangeListener listener)
224    {
225        if (_vcSupport == null)
226        {
227            _vcSupport = new VetoableChangeSupport(_proxy);
228        }
229
230        _vcSupport.addVetoableChangeListener(property, listener);
231    }
232
233    protected synchronized void firePropertyChange(String property, Object oldValue, Object newValue)
234    {
235        if (_pcSupport != null)
236        {
237            _pcSupport.firePropertyChange(property, oldValue, newValue);
238        }
239    }
240
241    protected synchronized void fireVetoableChange(String property, Object oldValue, Object newValue) throws PropertyVetoException
242    {
243        if (_vcSupport != null)
244        {
245            _vcSupport.fireVetoableChange(property, oldValue, newValue);
246        }
247    }
248
249    protected synchronized boolean hasProperty(String property)
250    {
251        boolean ret;
252
253        try
254        {
255            ret = hasPropertySpi(property);
256        }
257        catch (Exception x)
258        {
259            ret = false;
260        }
261
262        return ret;
263    }
264
265    protected Object parse(String value, Class<?> clazz) throws IllegalArgumentException
266    {
267        return BeanTool.getInstance().parse(value, clazz);
268    }
269
270    protected synchronized void removePropertyChangeListener(String property, PropertyChangeListener listener)
271    {
272        if (_pcSupport != null)
273        {
274            _pcSupport.removePropertyChangeListener(property, listener);
275        }
276    }
277
278    protected synchronized void removeVetoableChangeListener(String property, VetoableChangeListener listener)
279    {
280        if (_vcSupport != null)
281        {
282            _vcSupport.removeVetoableChangeListener(property, listener);
283        }
284    }
285
286    protected Object zero(Class<?> clazz)
287    {
288        return BeanTool.getInstance().zero(clazz);
289    }
290
291    private synchronized void updateProxy(Object value)
292    {
293        if (_proxy == null)
294        {
295            _proxy = value;
296        }
297    }
298}