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 java.beans.Introspector;
019    import java.beans.PropertyChangeListener;
020    import java.beans.PropertyChangeSupport;
021    import java.beans.PropertyVetoException;
022    import java.beans.VetoableChangeListener;
023    import java.beans.VetoableChangeSupport;
024    
025    import java.lang.reflect.Array;
026    import java.lang.reflect.InvocationHandler;
027    import java.lang.reflect.Method;
028    
029    public 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    }