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.Map.Entry;
033    import java.util.Set;
034    
035    import org.apache.camel.TypeConverter;
036    import org.apache.commons.logging.Log;
037    import org.apache.commons.logging.LogFactory;
038    
039    public final class IntrospectionSupport {
040    
041        private static final Log LOG = LogFactory.getLog(IntrospectionSupport.class);
042    
043    
044        /**
045         * Utility classes should not have a public constructor.
046         */
047        private IntrospectionSupport() {
048        }
049    
050        public static boolean getProperties(Object target, Map props, String optionPrefix) {
051            boolean rc = false;
052            if (target == null) {
053                throw new IllegalArgumentException("target was null.");
054            }
055            if (props == null) {
056                throw new IllegalArgumentException("props was null.");
057            }
058            if (optionPrefix == null) {
059                optionPrefix = "";
060            }
061    
062            Class clazz = target.getClass();
063            Method[] methods = clazz.getMethods();
064            for (int i = 0; i < methods.length; i++) {
065                Method method = methods[i];
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, new Object[] {});
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, new Object[] {});
105        }
106    
107        public static Method getPropertyGetter(Class type, String propertyName) throws NoSuchMethodException {
108            Method method = type.getMethod("get" + ObjectHelper.capitalize(propertyName), new Class[] {});
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                Entry entry = (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, new Object[] {value});
192                } else {
193                    // We need to convert it
194                    Object convertedValue = convert(typeConverter, setter.getParameterTypes()[0], value);
195                    setter.invoke(target, new Object[] {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    
211        public static boolean setProperty(Object target, String name, Object value) throws Exception {
212            return setProperty(null, target, name, value);
213        }
214    
215        private static Object convert(TypeConverter typeConverter, Class type, Object value) throws URISyntaxException {
216            if (typeConverter != null) {
217                Object answer = typeConverter.convertTo(type, value);
218                if (answer == null) {
219                    throw new IllegalArgumentException("Could not convert \"" + value + "\" to " + type.getName());
220                }
221                return answer;
222            }
223            PropertyEditor editor = PropertyEditorManager.findEditor(type);
224            if (editor != null) {
225                editor.setAsText(value.toString());
226                return editor.getValue();
227            }
228            if (type == URI.class) {
229                return new URI(value.toString());
230            }
231            return null;
232        }
233    
234        private static String convertToString(Object value, Class type) throws URISyntaxException {
235            PropertyEditor editor = PropertyEditorManager.findEditor(type);
236            if (editor != null) {
237                editor.setValue(value);
238                return editor.getAsText();
239            }
240            if (type == URI.class) {
241                return ((URI)value).toString();
242            }
243            return null;
244        }
245    
246        private static Method findSetterMethod(TypeConverter typeConverter, Class clazz, String name, Object value) {
247            // Build the method name.
248            name = "set" + ObjectHelper.capitalize(name);
249            while (clazz != Object.class) {
250                Method[] methods = clazz.getMethods();
251                for (int i = 0; i < methods.length; i++) {
252                    Method method = methods[i];
253                    Class params[] = method.getParameterTypes();
254                    if (method.getName().equals(name) && params.length == 1) {
255                        Class paramType = params[0];
256                        if (typeConverter != null || isSettableType(paramType) || paramType.isInstance(value)) {
257                            return method;
258                        }
259                    }
260                }
261                clazz = clazz.getSuperclass();
262            }
263            return null;
264        }
265    
266        private static boolean isSettableType(Class clazz) {
267            if (PropertyEditorManager.findEditor(clazz) != null) {
268                return true;
269            }
270            if (clazz == URI.class) {
271                return true;
272            }
273            if (clazz == Boolean.class) {
274                return true;
275            }
276            return false;
277        }
278    
279        public static String toString(Object target) {
280            return toString(target, Object.class);
281        }
282    
283        public static String toString(Object target, Class stopClass) {
284            LinkedHashMap map = new LinkedHashMap();
285            addFields(target, target.getClass(), stopClass, map);
286            StringBuffer buffer = new StringBuffer(simpleName(target.getClass()));
287            buffer.append(" {");
288            Set entrySet = map.entrySet();
289            boolean first = true;
290            for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
291                Map.Entry entry = (Map.Entry)iter.next();
292                if (first) {
293                    first = false;
294                } else {
295                    buffer.append(", ");
296                }
297                buffer.append(entry.getKey());
298                buffer.append(" = ");
299                appendToString(buffer, entry.getValue());
300            }
301            buffer.append("}");
302            return buffer.toString();
303        }
304    
305        protected static void appendToString(StringBuffer buffer, Object value) {
306            // if (value instanceof ActiveMQDestination) {
307            // ActiveMQDestination destination = (ActiveMQDestination) value;
308            // buffer.append(destination.getQualifiedName());
309            // }
310            // else {
311            buffer.append(value);
312            // }
313        }
314    
315        public static String simpleName(Class clazz) {
316            String name = clazz.getName();
317            int p = name.lastIndexOf(".");
318            if (p >= 0) {
319                name = name.substring(p + 1);
320            }
321            return name;
322        }
323    
324        private static void addFields(Object target, Class startClass, Class stopClass, LinkedHashMap map) {
325            if (startClass != stopClass) {
326                addFields(target, startClass.getSuperclass(), stopClass, map);
327            }
328    
329            Field[] fields = startClass.getDeclaredFields();
330            for (int i = 0; i < fields.length; i++) {
331                Field field = fields[i];
332                if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) || Modifier.isPrivate(field.getModifiers())) {
333                    continue;
334                }
335    
336                try {
337                    field.setAccessible(true);
338                    Object o = field.get(target);
339                    if (o != null && o.getClass().isArray()) {
340                        try {
341                            o = Arrays.asList((Object[])o);
342                        } catch (Throwable e) {
343                            // ignore
344                        }
345                    }
346                    map.put(field.getName(), o);
347                } catch (Throwable e) {
348                    LOG.debug("Error adding fields", e);
349                }
350            }
351        }
352    
353    }