JAL-3032 adds Java 8 functionality (1/2)
[jalview.git] / srcjar2 / org / apache / log4j / config / PropertySetter.java
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  * 
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  * 
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 // Contributors:  Georg Lundesgaard
19
20 package org.apache.log4j.config;
21
22 import org.apache.log4j.Appender;
23 import org.apache.log4j.Level;
24 import org.apache.log4j.Priority;
25 import org.apache.log4j.helpers.LogLog;
26 import org.apache.log4j.helpers.OptionConverter;
27 import org.apache.log4j.spi.OptionHandler;
28 import org.apache.log4j.spi.ErrorHandler;
29
30 import java.beans.BeanInfo;
31 import java.beans.IntrospectionException;
32 import java.beans.Introspector;
33 import java.beans.PropertyDescriptor;
34 import java.io.InterruptedIOException;
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.Method;
37 import java.util.Enumeration;
38 import java.util.Properties;
39
40 /**
41    General purpose Object property setter. Clients repeatedly invokes
42    {@link #setProperty setProperty(name,value)} in order to invoke setters
43    on the Object specified in the constructor. This class relies on the
44    JavaBeans {@link Introspector} to analyze the given Object Class using
45    reflection.
46    
47    <p>Usage:
48    <pre>
49      PropertySetter ps = new PropertySetter(anObject);
50      ps.set("name", "Joe");
51      ps.set("age", "32");
52      ps.set("isMale", "true");
53    </pre>
54    will cause the invocations anObject.setName("Joe"), anObject.setAge(32),
55    and setMale(true) if such methods exist with those signatures.
56    Otherwise an {@link IntrospectionException} are thrown.
57   
58    @author Anders Kristensen
59    @since 1.1
60  */
61 public class PropertySetter {
62   protected Object obj;
63   protected PropertyDescriptor[] props;
64   
65   /**
66     Create a new PropertySetter for the specified Object. This is done
67     in prepartion for invoking {@link #setProperty} one or more times.
68     
69     @param obj  the object for which to set properties
70    */
71   public
72   PropertySetter(Object obj) {
73     this.obj = obj;
74   }
75   
76   /**
77      Uses JavaBeans {@link Introspector} to computer setters of object to be
78      configured.
79    */
80   protected
81   void introspect() {
82     try {
83       BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
84       props = bi.getPropertyDescriptors();
85     } catch (IntrospectionException ex) {
86       LogLog.error("Failed to introspect "+obj+": " + ex.getMessage());
87       props = new PropertyDescriptor[0];
88     }
89   }
90   
91
92   /**
93      Set the properties of an object passed as a parameter in one
94      go. The <code>properties</code> are parsed relative to a
95      <code>prefix</code>.
96
97      @param obj The object to configure.
98      @param properties A java.util.Properties containing keys and values.
99      @param prefix Only keys having the specified prefix will be set.
100   */
101   public
102   static
103   void setProperties(Object obj, Properties properties, String prefix) {
104     new PropertySetter(obj).setProperties(properties, prefix);
105   }
106   
107
108   /**
109      Set the properites for the object that match the
110      <code>prefix</code> passed as parameter.
111
112      
113    */
114   public
115   void setProperties(Properties properties, String prefix) {
116     int len = prefix.length();
117     
118     for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) {
119       String key = (String) e.nextElement();
120       
121       // handle only properties that start with the desired frefix.
122       if (key.startsWith(prefix)) {
123
124         
125         // ignore key if it contains dots after the prefix
126         if (key.indexOf('.', len + 1) > 0) {
127           //System.err.println("----------Ignoring---["+key
128           //         +"], prefix=["+prefix+"].");
129           continue;
130         }
131         
132         String value = OptionConverter.findAndSubst(key, properties);
133         key = key.substring(len);
134         if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) {
135           continue;
136         }
137         //
138         //   if the property type is an OptionHandler
139         //     (for example, triggeringPolicy of org.apache.log4j.rolling.RollingFileAppender)
140         PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key));
141         if (prop != null
142                 && OptionHandler.class.isAssignableFrom(prop.getPropertyType())
143                 && prop.getWriteMethod() != null) {
144             OptionHandler opt = (OptionHandler)
145                     OptionConverter.instantiateByKey(properties, prefix + key,
146                                   prop.getPropertyType(),
147                                   null);
148             PropertySetter setter = new PropertySetter(opt);
149             setter.setProperties(properties, prefix + key + ".");
150             try {
151                 prop.getWriteMethod().invoke(this.obj, new Object[] { opt });
152             } catch(IllegalAccessException ex) {
153                 LogLog.warn("Failed to set property [" + key +
154                             "] to value \"" + value + "\". ", ex);
155             } catch(InvocationTargetException ex) {
156                 if (ex.getTargetException() instanceof InterruptedException
157                         || ex.getTargetException() instanceof InterruptedIOException) {
158                     Thread.currentThread().interrupt();
159                 }
160                 LogLog.warn("Failed to set property [" + key +
161                             "] to value \"" + value + "\". ", ex);
162             } catch(RuntimeException ex) {
163                 LogLog.warn("Failed to set property [" + key +
164                             "] to value \"" + value + "\". ", ex);
165             }
166             continue;
167         }
168
169         setProperty(key, value);
170       }
171     }
172     activate();
173   }
174   
175   /**
176      Set a property on this PropertySetter's Object. If successful, this
177      method will invoke a setter method on the underlying Object. The
178      setter is the one for the specified property name and the value is
179      determined partly from the setter argument type and partly from the
180      value specified in the call to this method.
181      
182      <p>If the setter expects a String no conversion is necessary.
183      If it expects an int, then an attempt is made to convert 'value'
184      to an int using new Integer(value). If the setter expects a boolean,
185      the conversion is by new Boolean(value).
186      
187      @param name    name of the property
188      @param value   String value of the property
189    */
190   public
191   void setProperty(String name, String value) {
192     if (value == null) {
193         return;
194     }
195     
196     name = Introspector.decapitalize(name);
197     PropertyDescriptor prop = getPropertyDescriptor(name);
198     
199     //LogLog.debug("---------Key: "+name+", type="+prop.getPropertyType());
200
201     if (prop == null) {
202       LogLog.warn("No such property [" + name + "] in "+
203                   obj.getClass().getName()+"." );
204     } else {
205       try {
206         setProperty(prop, name, value);
207       } catch (PropertySetterException ex) {
208         LogLog.warn("Failed to set property [" + name +
209                     "] to value \"" + value + "\". ", ex.rootCause);
210       }
211     }
212   }
213   
214   /** 
215       Set the named property given a {@link PropertyDescriptor}.
216
217       @param prop A PropertyDescriptor describing the characteristics
218       of the property to set.
219       @param name The named of the property to set.
220       @param value The value of the property.      
221    */
222   public
223   void setProperty(PropertyDescriptor prop, String name, String value)
224     throws PropertySetterException {
225     Method setter = prop.getWriteMethod();
226     if (setter == null) {
227       throw new PropertySetterException("No setter for property ["+name+"].");
228     }
229     Class[] paramTypes = setter.getParameterTypes();
230     if (paramTypes.length != 1) {
231       throw new PropertySetterException("#params for setter != 1");
232     }
233     
234     Object arg;
235     try {
236       arg = convertArg(value, paramTypes[0]);
237     } catch (Throwable t) {
238       throw new PropertySetterException("Conversion to type ["+paramTypes[0]+
239                                         "] failed. Reason: "+t);
240     }
241     if (arg == null) {
242       throw new PropertySetterException(
243           "Conversion to type ["+paramTypes[0]+"] failed.");
244     }
245     LogLog.debug("Setting property [" + name + "] to [" +arg+"].");
246     try {
247       setter.invoke(obj, new Object[]  { arg });
248     } catch (IllegalAccessException ex) {
249       throw new PropertySetterException(ex);
250     } catch (InvocationTargetException ex) {
251         if (ex.getTargetException() instanceof InterruptedException
252                 || ex.getTargetException() instanceof InterruptedIOException) {
253             Thread.currentThread().interrupt();
254         }        
255         throw new PropertySetterException(ex);
256     } catch (RuntimeException ex) {
257       throw new PropertySetterException(ex);
258     }
259   }
260   
261
262   /**
263      Convert <code>val</code> a String parameter to an object of a
264      given type.
265   */
266   protected
267   Object convertArg(String val, Class type) {
268     if(val == null) {
269         return null;
270     }
271
272     String v = val.trim();
273     if (String.class.isAssignableFrom(type)) {
274       return val;
275     } else if (Integer.TYPE.isAssignableFrom(type)) {
276       return new Integer(v);
277     } else if (Long.TYPE.isAssignableFrom(type)) {
278       return new Long(v);
279     } else if (Boolean.TYPE.isAssignableFrom(type)) {
280       if ("true".equalsIgnoreCase(v)) {
281         return Boolean.TRUE;
282       } else if ("false".equalsIgnoreCase(v)) {
283         return Boolean.FALSE;
284       }
285     } else if (Priority.class.isAssignableFrom(type)) {
286       return OptionConverter.toLevel(v, Level.DEBUG);
287     } else if (ErrorHandler.class.isAssignableFrom(type)) {
288       return OptionConverter.instantiateByClassName(v, 
289           ErrorHandler.class, null);
290     }
291     return null;
292   }
293   
294   
295   protected
296   PropertyDescriptor getPropertyDescriptor(String name) {
297     if (props == null) {
298         introspect();
299     }
300     
301     for (int i = 0; i < props.length; i++) {
302       if (name.equals(props[i].getName())) {
303         return props[i];
304       }
305     }
306     return null;
307   }
308   
309   public
310   void activate() {
311     if (obj instanceof OptionHandler) {
312       ((OptionHandler) obj).activateOptions();
313     }
314   }
315 }