001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.util;
018    
019    import java.beans.PropertyEditor;
020    import java.beans.PropertyEditorManager;
021    import java.lang.reflect.Field;
022    import java.lang.reflect.InvocationTargetException;
023    import java.lang.reflect.Method;
024    import java.lang.reflect.Modifier;
025    import java.net.URI;
026    import java.net.URISyntaxException;
027    import java.util.Arrays;
028    import java.util.HashMap;
029    import java.util.Iterator;
030    import java.util.LinkedHashMap;
031    import java.util.Map;
032    import java.util.Set;
033    
034    import org.apache.camel.TypeConverter;
035    import org.apache.commons.logging.Log;
036    import org.apache.commons.logging.LogFactory;
037    
038    /**
039     * Helper for introspections of beans.
040     */
041    public final class IntrospectionSupport {
042    
043        private static final transient Log LOG = LogFactory.getLog(IntrospectionSupport.class);
044    
045        /**
046         * Utility classes should not have a public constructor.
047         */
048        private IntrospectionSupport() {
049        }
050    
051        public static boolean getProperties(Object target, Map props, String optionPrefix) {
052            boolean rc = false;
053            if (target == null) {
054                throw new IllegalArgumentException("target was null.");
055            }
056            if (props == null) {
057                throw new IllegalArgumentException("props was null.");
058            }
059            if (optionPrefix == null) {
060                optionPrefix = "";
061            }
062    
063            Class clazz = target.getClass();
064            Method[] methods = clazz.getMethods();
065            for (Method method : methods) {
066                String name = method.getName();
067                Class type = method.getReturnType();
068                Class params[] = method.getParameterTypes();
069                if (name.startsWith("get") && params.length == 0 && type != null && isSettableType(type)) {
070                    try {
071                        Object value = method.invoke(target);
072                        if (value == null) {
073                            continue;
074                        }
075    
076                        String strValue = convertToString(value, type);
077                        if (strValue == null) {
078                            continue;
079                        }
080    
081                        name = name.substring(3, 4).toLowerCase() + name.substring(4);
082                        props.put(optionPrefix + name, strValue);
083                        rc = true;
084                    } catch (Throwable ignore) {
085                        // ignore
086                    }
087                }
088            }
089    
090            return rc;
091        }
092    
093        public static Object getProperty(Object target, String prop) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
094            if (target == null) {
095                throw new IllegalArgumentException("target was null.");
096            }
097            if (prop == null) {
098                throw new IllegalArgumentException("prop was null.");
099            }
100            prop = prop.substring(0, 1).toUpperCase() + prop.substring(1);
101    
102            Class clazz = target.getClass();
103            Method method = getPropertyGetter(clazz, prop);
104            return method.invoke(target);
105        }
106    
107        public static Method getPropertyGetter(Class type, String propertyName) throws NoSuchMethodException {
108            Method method = type.getMethod("get" + ObjectHelper.capitalize(propertyName));
109            return method;
110        }
111    
112        public static boolean setProperties(Object target, Map props, String optionPrefix) throws Exception {
113            boolean rc = false;
114            if (target == null) {
115                throw new IllegalArgumentException("target was null.");
116            }
117            if (props == null) {
118                throw new IllegalArgumentException("props was null.");
119            }
120    
121            for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
122                String name = (String)iter.next();
123                if (name.startsWith(optionPrefix)) {
124                    Object value = props.get(name);
125                    name = name.substring(optionPrefix.length());
126                    if (setProperty(target, name, value)) {
127                        iter.remove();
128                        rc = true;
129                    }
130                }
131            }
132            return rc;
133        }
134    
135        public static Map extractProperties(Map props, String optionPrefix) {
136            if (props == null) {
137                throw new IllegalArgumentException("props was null.");
138            }
139    
140            HashMap rc = new HashMap(props.size());
141    
142            for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
143                String name = (String)iter.next();
144                if (name.startsWith(optionPrefix)) {
145                    Object value = props.get(name);
146                    name = name.substring(optionPrefix.length());
147                    rc.put(name, value);
148                    iter.remove();
149                }
150            }
151    
152            return rc;
153        }
154    
155        public static boolean setProperties(TypeConverter typeConverter, Object target, Map props) throws Exception {
156            boolean rc = false;
157    
158            if (target == null) {
159                throw new IllegalArgumentException("target was null.");
160            }
161            if (props == null) {
162                throw new IllegalArgumentException("props was null.");
163            }
164    
165            for (Iterator iter = props.entrySet().iterator(); iter.hasNext();) {
166                Map.Entry entry = (Map.Entry)iter.next();
167                if (setProperty(typeConverter, target, (String)entry.getKey(), entry.getValue())) {
168                    iter.remove();
169                    rc = true;
170                }
171            }
172    
173            return rc;
174        }
175    
176        public static boolean setProperties(Object target, Map props) throws Exception {
177            return setProperties(null, target, props);
178        }
179    
180        public static boolean setProperty(TypeConverter typeConverter, Object target, String name, Object value) throws Exception {
181            try {
182                Class clazz = target.getClass();
183                Method setter = findSetterMethod(typeConverter, clazz, name, value);
184                if (setter == null) {
185                    return false;
186                }
187    
188                // If the type is null or it matches the needed type, just use the
189                // value directly
190                if (value == null || value.getClass() == setter.getParameterTypes()[0]) {
191                    setter.invoke(target, value);
192                } else {
193                    // We need to convert it
194                    Object convertedValue = convert(typeConverter, setter.getParameterTypes()[0], value);
195                    setter.invoke(target, convertedValue);
196                }
197                return true;
198            } catch (InvocationTargetException e) {
199                Throwable throwable = e.getTargetException();
200                if (throwable instanceof Exception) {
201                    Exception exception = (Exception)throwable;
202                    throw exception;
203                } else {
204                    Error error = (Error)throwable;
205                    throw error;
206                }
207            }
208        }
209    
210        public static boolean setProperty(Object target, String name, Object value) throws Exception {
211            return setProperty(null, target, name, value);
212        }
213    
214        private static Object convert(TypeConverter typeConverter, Class type, Object value) throws URISyntaxException {
215            if (typeConverter != null) {
216                Object answer = typeConverter.convertTo(type, value);
217                if (answer == null) {
218                    throw new IllegalArgumentException("Could not convert \"" + value + "\" to " + type.getName());
219                }
220                return answer;
221            }
222            PropertyEditor editor = PropertyEditorManager.findEditor(type);
223            if (editor != null) {
224                editor.setAsText(value.toString());
225                return editor.getValue();
226            }
227            if (type == URI.class) {
228                return new URI(value.toString());
229            }
230            return null;
231        }
232    
233        private static String convertToString(Object value, Class type) throws URISyntaxException {
234            PropertyEditor editor = PropertyEditorManager.findEditor(type);
235            if (editor != null) {
236                editor.setValue(value);
237                return editor.getAsText();
238            }
239            if (type == URI.class) {
240                return value.toString();
241            }
242            return null;
243        }
244    
245        private static Method findSetterMethod(TypeConverter typeConverter, Class clazz, String name, Object value) {
246            // Build the method name.
247            name = "set" + ObjectHelper.capitalize(name);
248            while (clazz != Object.class) {
249                Method[] methods = clazz.getMethods();
250                for (Method method : methods) {
251                    Class params[] = method.getParameterTypes();
252                    if (method.getName().equals(name) && params.length == 1) {
253                        Class paramType = params[0];
254                        if (typeConverter != null || isSettableType(paramType) || paramType.isInstance(value)) {
255                            return method;
256                        }
257                    }
258                }
259                clazz = clazz.getSuperclass();
260            }
261            return null;
262        }
263    
264        private static boolean isSettableType(Class clazz) {
265            if (PropertyEditorManager.findEditor(clazz) != null) {
266                return true;
267            }
268            if (clazz == URI.class) {
269                return true;
270            }
271            if (clazz == Boolean.class) {
272                return true;
273            }
274            return false;
275        }
276    
277        public static String toString(Object target) {
278            return toString(target, Object.class);
279        }
280    
281        public static String toString(Object target, Class stopClass) {
282            LinkedHashMap map = new LinkedHashMap();
283            addFields(target, target.getClass(), stopClass, map);
284            StringBuffer buffer = new StringBuffer(simpleName(target.getClass()));
285            buffer.append(" {");
286            Set entrySet = map.entrySet();
287            boolean first = true;
288            for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
289                Map.Entry entry = (Map.Entry)iter.next();
290                if (first) {
291                    first = false;
292                } else {
293                    buffer.append(", ");
294                }
295                buffer.append(entry.getKey());
296                buffer.append(" = ");
297                appendToString(buffer, entry.getValue());
298            }
299            buffer.append("}");
300            return buffer.toString();
301        }
302    
303        protected static void appendToString(StringBuffer buffer, Object value) {
304            // if (value instanceof ActiveMQDestination) {
305            // ActiveMQDestination destination = (ActiveMQDestination) value;
306            // buffer.append(destination.getQualifiedName());
307            // }
308            // else {
309            buffer.append(value);
310            // }
311        }
312    
313        public static String simpleName(Class clazz) {
314            String name = clazz.getName();
315            int p = name.lastIndexOf(".");
316            if (p >= 0) {
317                name = name.substring(p + 1);
318            }
319            return name;
320        }
321    
322        private static void addFields(Object target, Class startClass, Class stopClass, LinkedHashMap map) {
323            if (startClass != stopClass) {
324                addFields(target, startClass.getSuperclass(), stopClass, map);
325            }
326    
327            Field[] fields = startClass.getDeclaredFields();
328            for (Field field : fields) {
329                if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())
330                    || Modifier.isPrivate(field.getModifiers())) {
331                    continue;
332                }
333    
334                try {
335                    field.setAccessible(true);
336                    Object o = field.get(target);
337                    if (o != null && o.getClass().isArray()) {
338                        try {
339                            o = Arrays.asList((Object[])o);
340                        } catch (Throwable e) {
341                            // ignore
342                        }
343                    }
344                    map.put(field.getName(), o);
345                } catch (Throwable e) {
346                    LOG.debug("Error adding fields", e);
347                }
348            }
349        }
350    
351    }