001/*
002 * Copyright (c) 2004-2012, Willem Cazander
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
006 * that the following conditions are met:
007 * 
008 * * Redistributions of source code must retain the above copyright notice, this list of conditions and the
009 *   following disclaimer.
010 * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
011 *   the following disclaimer in the documentation and/or other materials provided with the distribution.
012 * 
013 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
014 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
015 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
016 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
018 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
019 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
020 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
021 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
022 */
023
024package org.x4o.xml.impl;
025
026import java.io.Serializable;
027import java.lang.reflect.Method;
028import java.util.Iterator;
029import java.util.Locale;
030import java.util.Map;
031import java.util.logging.Logger;
032
033import org.x4o.xml.conv.DefaultObjectConverterProvider;
034import org.x4o.xml.conv.ObjectConverter;
035import org.x4o.xml.conv.ObjectConverterException;
036import org.x4o.xml.element.ElementObjectPropertyValue;
037import org.x4o.xml.element.ElementObjectPropertyValueException;
038
039/**
040 * An DefaultElementObjectPropertyValue which does does get/set operations on pojo beans.
041 * 
042 * @author Willem Cazander
043 * @version 1.0 Feb 16, 2007
044 */
045public class DefaultElementObjectPropertyValue implements ElementObjectPropertyValue,Serializable {
046
047        private static final long serialVersionUID = 1L;
048        
049        private Logger logger = Logger.getLogger(DefaultElementObjectPropertyValue.class.getName());
050        
051        private Method findMethod(Object object,String parameterName,Object parameter) {
052                
053                // Get class but can be null.
054                Class<?> parameterClass = null;
055                if(parameter!=null) {
056                        parameterClass=parameter.getClass();
057                }
058                logger.finer("Trying value: pn="+parameterName+" o="+object+" p="+parameter+"("+parameterClass+")");
059                String parameterNameSet = "set"+parameterName;
060                Method[] methodes = object.getClass().getMethods();
061                Method lastMethodFall = null;
062                for (int i=0;i<methodes.length;i++) {
063                        Method method = methodes[i];
064                        Class<?>[] types = method.getParameterTypes();
065                        if (types.length == 0) {
066                                continue;
067                        }
068                        if (types.length > 1) {
069                                continue;
070                        }
071                        if (method.getName().equalsIgnoreCase(parameterNameSet)) {
072                                lastMethodFall = method;
073                                if (parameterClass!=null) {
074                                        // Check for class based parameters.
075                                        if (types[0].isAssignableFrom(parameterClass)) {
076                                                logger.finest("Found method type: "+method.getParameterTypes()[0]+" for parameter: "+parameterName);
077                                                return method;
078                                        }
079                                        // Check the native parameter types.
080                                        if (parameterClass.isAssignableFrom(Boolean.class) && types[0].isAssignableFrom(Boolean.TYPE) ) {
081                                                return method;
082                                        }
083                                        if (parameterClass.isAssignableFrom(Integer.class) && types[0].isAssignableFrom(Integer.TYPE) ) {
084                                                return method;
085                                        }
086                                        if (parameterClass.isAssignableFrom(Long.class) && types[0].isAssignableFrom(Long.TYPE) ) {
087                                                return method;
088                                        }
089                                        if (parameterClass.isAssignableFrom(Double.class) && types[0].isAssignableFrom(Double.TYPE) ) {
090                                                return method;
091                                        }
092                                        if (parameterClass.isAssignableFrom(Float.class) && types[0].isAssignableFrom(Float.TYPE) ) {
093                                                return method;
094                                        }
095                                        if (parameterClass.isAssignableFrom(Byte.class) && types[0].isAssignableFrom(Byte.TYPE) ) {
096                                                return method;
097                                        }
098                                        if (parameterClass.isAssignableFrom(Character.class) && types[0].isAssignableFrom(Character.TYPE) ) {
099                                                return method;
100                                        }
101                                }
102                        }
103                }
104                return lastMethodFall;
105        }
106        
107        /**
108         * TODO: this function is not completed !!
109         * 
110         *
111         * 
112         * @param object
113         * @param parameterName
114         * @param parameter
115         * @throws ElementParameterException
116         * @throws ElementObjectPropertyValueException
117         */
118        public void setProperty(Object object,String parameterName,Object parameter) throws ElementObjectPropertyValueException {
119                
120                // find the method for the parameter
121                Method lastMethod = findMethod(object,parameterName,parameter);
122                if (lastMethod==null) {
123                        logger.finest("No method found, aborting parameter: "+parameterName);
124                        return;
125                }
126                
127                // Special case for null value.
128                if (parameter==null) {
129                        logger.finest("Found parameter is null Setting method: "+lastMethod.getParameterTypes()[0]+" for parameter: "+parameterName);
130                        try {
131                                lastMethod.invoke(object,new Object[]{parameter});
132                                return;
133                        } catch (Exception e) {
134                                throw new ElementObjectPropertyValueException(e.getMessage(),e);
135                        }
136                }
137                
138                // Invoke for class based parameters
139                if (lastMethod.getParameterTypes()[0].isAssignableFrom(parameter.getClass())) {
140                        logger.finest("Found parameter type: "+lastMethod.getParameterTypes()[0]+" for parameter: "+parameterName+" setting value: "+parameter);
141                        try {
142                                lastMethod.invoke(object,new Object[]{parameter});
143                                return;
144                        } catch (Exception e) {
145                                throw new ElementObjectPropertyValueException(e.getMessage(),e);
146                        }
147                }
148                
149                // Invoke for native based types
150                
151                
152                // not found 2sec try
153                logger.finest("No corresoning class is found, trying convert manualy");
154                
155                // special case for object.
156                if (lastMethod.getParameterTypes()[0].equals(Object.class) ) {
157                        logger.finest("Set Special object value: "+parameterName+" parm2: "+parameter+" on2="+lastMethod.getName()+" with="+object);
158                        try {
159                                lastMethod.invoke(object,new Object[]{parameter});
160                        } catch (Exception e) {
161                                throw new ElementObjectPropertyValueException(e.getMessage(),e);
162                        }
163                        return;
164                }
165                
166                // all below creates from string
167                if (parameter.toString().length()==0) {
168                        return; // can't set value from string with empty size.
169                }
170                
171                //
172                // JAVA OBJECT TYPES:
173                //
174                Object parameter2 = null;
175                
176                try {
177                        DefaultObjectConverterProvider convProvider = new DefaultObjectConverterProvider();
178                        convProvider.addDefaults();
179                        ObjectConverter conv = convProvider.getObjectConverterForClass(lastMethod.getParameterTypes()[0]);
180                        if (conv!=null) {
181                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
182                        }
183        
184                        /*
185                         * JAVA NATIVE TYPES:
186                         * 
187                         * TYPE:        Size in bits:
188                         * boolean      8, unsigned
189                         * byte         8
190                         * char         16, unsigned
191                         * short        16
192                         * int          32
193                         * long         64
194                         * float        32
195                         * double       64
196                         * void         n/a
197                         */
198                        if (lastMethod.getParameterTypes()[0].isAssignableFrom(Boolean.TYPE) ) {
199                                conv = convProvider.getObjectConverterForClass(Boolean.class);
200                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
201                        }
202                        if (lastMethod.getParameterTypes()[0].isAssignableFrom(Integer.TYPE) ) {
203                                conv = convProvider.getObjectConverterForClass(Integer.class);
204                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
205                        }
206                        if (lastMethod.getParameterTypes()[0].isAssignableFrom(Long.TYPE) ) {
207                                conv = convProvider.getObjectConverterForClass(Long.class);
208                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
209                        }
210                        if (lastMethod.getParameterTypes()[0].isAssignableFrom(Double.TYPE) ) {
211                                conv = convProvider.getObjectConverterForClass(Double.class);
212                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
213                        }
214                        if (lastMethod.getParameterTypes()[0].isAssignableFrom(Float.TYPE) ) {
215                                conv = convProvider.getObjectConverterForClass(Float.class);
216                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
217                        }
218                        if (lastMethod.getParameterTypes()[0].isAssignableFrom(Byte.TYPE) ) {
219                                conv = convProvider.getObjectConverterForClass(Byte.class);
220                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
221                        }
222                        if (lastMethod.getParameterTypes()[0].isAssignableFrom(Character.TYPE) ) {
223                                conv = convProvider.getObjectConverterForClass(Character.class);
224                                parameter2 = conv.convertTo(parameter.toString(), Locale.getDefault());
225                        }
226                        
227                } catch (ObjectConverterException oce) {
228                        throw new ElementObjectPropertyValueException(oce.getMessage(),oce);
229                }
230                
231                if (parameter2==null) {
232                        throw new ElementObjectPropertyValueException("Could not convert to type for parameter: '"+parameterName+"' value: '"+parameter+"'");
233                }
234                
235                logger.finest("Set value: "+parameterName+" parm2: "+parameter2+" on2="+lastMethod.getName()+" with="+object);
236                try {
237                        lastMethod.invoke(object,new Object[]{parameter2});
238                } catch (Exception e) {
239                        throw new ElementObjectPropertyValueException(e.getMessage(),e);
240                }
241        }
242        
243        /**
244         * Gets the property of an bean.
245         * 
246         * @param object        The object to get from.
247         * @param parameterName The parameter name of the property to get.
248         * @throws ElementParameterException
249         * @throws ElementObjectPropertyValueException
250         */
251        public Object getProperty(Object object,String parameterName) throws ElementObjectPropertyValueException {
252
253                if (object==null) {
254                        throw new NullPointerException("Can't get property of null object.");
255                }
256                if (parameterName==null) {
257                        throw new NullPointerException("Can't get property is the name is null.");
258                }
259                
260                Logger logger = Logger.getLogger(DefaultElementObjectPropertyValue.class.getName());
261                
262                String propRest = null;
263                int index = parameterName.indexOf(".");
264                if(index>0) {
265                        propRest = parameterName.substring(index+1);
266                        parameterName = parameterName.substring(0,index);
267                        logger.finest("slit property into: '"+propRest+"' and '"+parameterName+"'");
268                }
269
270                logger.finer("Trying value: pn="+parameterName+" o="+object);
271                String parameterNameSet = "get"+parameterName;
272                Method[] methodes = object.getClass().getMethods();
273                Method lastMethod = null;
274                
275                // a bit hackie
276                for(int i=0;i<methodes.length;i++) {
277                        Method method = methodes[i];                            
278                        if(method.getName().equalsIgnoreCase(parameterNameSet)) {
279                                logger.finest("Found method: "+method.getName());
280                                lastMethod = method;
281                                break;
282                        }
283                }
284                
285                if (lastMethod==null) {
286                        for(int i=0;i<methodes.length;i++) {
287                                Method method = methodes[i];                            
288                                if(method.getName().equalsIgnoreCase("is"+parameterName)) {
289                                        logger.finest("Found is method: "+method.getName());
290                                        lastMethod = method;
291                                        break;
292                                }
293                                if(method.getName().equalsIgnoreCase("has"+parameterName)) {
294                                        logger.finest("Found has method: "+method.getName());
295                                        lastMethod = method;
296                                        break;
297                                }
298                        }
299                }
300                
301                if (lastMethod==null) {
302                        throw new ElementObjectPropertyValueException("Could not find method for parameter: '"+parameterName+"' object: '"+object+"'");
303                }
304
305                Object result = null;
306                try {
307                        result = lastMethod.invoke(object);
308                } catch (Exception e) {
309                        throw new ElementObjectPropertyValueException(e.getMessage(),e);
310                }
311                        
312                if (propRest!=null) {
313                        if (result==null) {
314                                return null; // no need to go deeper into a null value.
315                        }
316                        // recursif function:
317                        return getProperty(result,propRest);
318                }
319                
320                return result;
321        }
322        
323        /**
324         * @see org.x4o.xml.element.ElementObjectPropertyValue#setPropertyMap(java.lang.Object, java.util.Map)
325         */
326        public void setPropertyMap(Object object, Map<String, Object> attributes) throws ElementObjectPropertyValueException {
327                Iterator<String> keyIterator = attributes.keySet().iterator();
328                while(keyIterator.hasNext()) {
329                        String key = keyIterator.next();
330                        setProperty(object,key,attributes.get(key));
331                }
332        }
333}