JAL-3048 test updated for AlignExportSettings changes
[jalview.git] / srcjar / org / apache / log4j / helpers / OptionConverter.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 package org.apache.log4j.helpers;
19
20 import java.io.InputStream;
21 import java.io.InterruptedIOException;
22 import java.net.URL;
23 import java.util.Properties;
24
25 import org.apache.log4j.Level;
26 import org.apache.log4j.PropertyConfigurator;
27 import org.apache.log4j.spi.Configurator;
28 import org.apache.log4j.spi.LoggerRepository;
29
30 // Contributors:   Avy Sharell (sharell@online.fr)
31 //                 Matthieu Verbert (mve@zurich.ibm.com)
32 //                 Colin Sampaleanu
33
34 /**
35    A convenience class to convert property values to specific types.
36
37    @author Ceki Gülcü
38    @author Simon Kitching;
39    @author Anders Kristensen
40 */
41 public class OptionConverter {
42
43   static String DELIM_START = "${";
44   static char   DELIM_STOP  = '}';
45   static int DELIM_START_LEN = 2;
46   static int DELIM_STOP_LEN  = 1;
47
48   /** OptionConverter is a static class. */
49   private OptionConverter() {}
50
51   public
52   static
53   String[] concatanateArrays(String[] l, String[] r) {
54     int len = l.length + r.length;
55     String[] a = new String[len];
56
57     System.arraycopy(l, 0, a, 0, l.length);
58     System.arraycopy(r, 0, a, l.length, r.length);
59
60     return a;
61   }
62
63   public
64   static
65   String convertSpecialChars(String s) {
66     char c;
67     int len = s.length();
68     StringBuffer sbuf = new StringBuffer(len);
69
70     int i = 0;
71     while(i < len) {
72       c = s.charAt(i++);
73       if (c == '\\') {
74         c =  s.charAt(i++);
75         if(c == 'n') {
76         c = '\n';
77     } else if(c == 'r') {
78         c = '\r';
79     } else if(c == 't') {
80         c = '\t';
81     } else if(c == 'f') {
82         c = '\f';
83     } else if(c == '\b') {
84         c = '\b';
85     } else if(c == '\"') {
86         c = '\"';
87     } else if(c == '\'') {
88         c = '\'';
89     } else if(c == '\\') {
90         c = '\\';
91     }
92       }
93       sbuf.append(c);
94     }
95     return sbuf.toString();
96   }
97
98
99   /**
100      Very similar to <code>System.getProperty</code> except
101      that the {@link SecurityException} is hidden.
102
103      @param key The key to search for.
104      @param def The default value to return.
105      @return the string value of the system property, or the default
106      value if there is no property with that key.
107
108      @since 1.1 */
109   public
110   static
111   String getSystemProperty(String key, String def) {
112     try {
113       return System.getProperty(key, def);
114     } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
115       LogLog.debug("Was not allowed to read system property \""+key+"\".");
116       return def;
117     }
118   }
119
120
121   public
122   static
123   Object instantiateByKey(Properties props, String key, Class superClass,
124                                 Object defaultValue) {
125
126     // Get the value of the property in string form
127     String className = findAndSubst(key, props);
128     if(className == null) {
129       LogLog.error("Could not find value for key " + key);
130       return defaultValue;
131     }
132     // Trim className to avoid trailing spaces that cause problems.
133     return OptionConverter.instantiateByClassName(className.trim(), superClass,
134                                                   defaultValue);
135   }
136
137   /**
138      If <code>value</code> is "true", then <code>true</code> is
139      returned. If <code>value</code> is "false", then
140      <code>true</code> is returned. Otherwise, <code>default</code> is
141      returned.
142
143      <p>Case of value is unimportant.  */
144   public
145   static
146   boolean toBoolean(String value, boolean dEfault) {
147     if(value == null) {
148         return dEfault;
149     }
150     String trimmedVal = value.trim();
151     if("true".equalsIgnoreCase(trimmedVal)) {
152         return true;
153     }
154     if("false".equalsIgnoreCase(trimmedVal)) {
155         return false;
156     }
157     return dEfault;
158   }
159
160   public
161   static
162   int toInt(String value, int dEfault) {
163     if(value != null) {
164       String s = value.trim();
165       try {
166         return Integer.valueOf(s).intValue();
167       }
168       catch (NumberFormatException e) {
169          LogLog.error("[" + s + "] is not in proper int form.");
170         e.printStackTrace();
171       }
172     }
173     return dEfault;
174   }
175
176   /**
177      Converts a standard or custom priority level to a Level
178      object.  <p> If <code>value</code> is of form
179      "level#classname", then the specified class' toLevel method
180      is called to process the specified level string; if no '#'
181      character is present, then the default {@link org.apache.log4j.Level}
182      class is used to process the level value.
183
184      <p>As a special case, if the <code>value</code> parameter is
185      equal to the string "NULL", then the value <code>null</code> will
186      be returned.
187
188      <p> If any error occurs while converting the value to a level,
189      the <code>defaultValue</code> parameter, which may be
190      <code>null</code>, is returned.
191
192      <p> Case of <code>value</code> is insignificant for the level level, but is
193      significant for the class name part, if present.
194
195      @since 1.1 */
196   public
197   static
198   Level toLevel(String value, Level defaultValue) {
199     if(value == null) {
200         return defaultValue;
201     }
202       
203     value = value.trim();
204
205     int hashIndex = value.indexOf('#');
206     if (hashIndex == -1) {
207       if("NULL".equalsIgnoreCase(value)) {
208         return null;
209       } else {
210         // no class name specified : use standard Level class
211         return Level.toLevel(value, defaultValue);
212       }
213     }
214
215     Level result = defaultValue;
216
217     String clazz = value.substring(hashIndex+1);
218     String levelName = value.substring(0, hashIndex);
219
220     // This is degenerate case but you never know.
221     if("NULL".equalsIgnoreCase(levelName)) {
222         return null;
223     }
224
225     LogLog.debug("toLevel" + ":class=[" + clazz + "]"
226                  + ":pri=[" + levelName + "]");
227
228     try {
229       Class customLevel = Loader.loadClass(clazz);
230
231       // get a ref to the specified class' static method
232       // toLevel(String, org.apache.log4j.Level)
233       Class[] paramTypes = new Class[] { String.class,
234                                          org.apache.log4j.Level.class
235                                        };
236       java.lang.reflect.Method toLevelMethod =
237                       customLevel.getMethod("toLevel", paramTypes);
238
239       // now call the toLevel method, passing level string + default
240       Object[] params = new Object[] {levelName, defaultValue};
241       Object o = toLevelMethod.invoke(null, params);
242
243       result = (Level) o;
244     } catch(ClassNotFoundException e) {
245       LogLog.warn("custom level class [" + clazz + "] not found.");
246     } catch(NoSuchMethodException e) {
247       LogLog.warn("custom level class [" + clazz + "]"
248         + " does not have a class function toLevel(String, Level)", e);
249     } catch(java.lang.reflect.InvocationTargetException e) {
250         if (e.getTargetException() instanceof InterruptedException
251                 || e.getTargetException() instanceof InterruptedIOException) {
252             Thread.currentThread().interrupt();
253         }
254       LogLog.warn("custom level class [" + clazz + "]"
255                    + " could not be instantiated", e);
256     } catch(ClassCastException e) {
257       LogLog.warn("class [" + clazz
258         + "] is not a subclass of org.apache.log4j.Level", e);
259     } catch(IllegalAccessException e) {
260       LogLog.warn("class ["+clazz+
261                    "] cannot be instantiated due to access restrictions", e);
262     } catch(RuntimeException e) {
263       LogLog.warn("class ["+clazz+"], level ["+levelName+
264                    "] conversion failed.", e);
265     }
266     return result;
267    }
268
269   public
270   static
271   long toFileSize(String value, long dEfault) {
272     if(value == null) {
273         return dEfault;
274     }
275
276     String s = value.trim().toUpperCase();
277     long multiplier = 1;
278     int index;
279
280     if((index = s.indexOf("KB")) != -1) {
281       multiplier = 1024;
282       s = s.substring(0, index);
283     }
284     else if((index = s.indexOf("MB")) != -1) {
285       multiplier = 1024*1024;
286       s = s.substring(0, index);
287     }
288     else if((index = s.indexOf("GB")) != -1) {
289       multiplier = 1024*1024*1024;
290       s = s.substring(0, index);
291     }
292     if(s != null) {
293       try {
294         return Long.valueOf(s).longValue() * multiplier;
295       }
296       catch (NumberFormatException e) {
297         LogLog.error("[" + s + "] is not in proper int form.");
298         LogLog.error("[" + value + "] not in expected format.", e);
299       }
300     }
301     return dEfault;
302   }
303
304   /**
305      Find the value corresponding to <code>key</code> in
306      <code>props</code>. Then perform variable substitution on the
307      found value.
308
309  */
310   public
311   static
312   String findAndSubst(String key, Properties props) {
313     String value = props.getProperty(key);
314     if(value == null) {
315         return null;
316     }
317
318     try {
319       return substVars(value, props);
320     } catch(IllegalArgumentException e) {
321       LogLog.error("Bad option value ["+value+"].", e);
322       return value;
323     }
324   }
325
326   /**
327      Instantiate an object given a class name. Check that the
328      <code>className</code> is a subclass of
329      <code>superClass</code>. If that test fails or the object could
330      not be instantiated, then <code>defaultValue</code> is returned.
331
332      @param className The fully qualified class name of the object to instantiate.
333      @param superClass The class to which the new object should belong.
334      @param defaultValue The object to return in case of non-fulfillment
335    */
336   public
337   static
338   Object instantiateByClassName(String className, Class superClass,
339                                 Object defaultValue) {
340     if(className != null) {
341       try {
342         Class classObj = Loader.loadClass(className);
343         if(!superClass.isAssignableFrom(classObj)) {
344           LogLog.error("A \""+className+"\" object is not assignable to a \""+
345                        superClass.getName() + "\" variable.");
346           LogLog.error("The class \""+ superClass.getName()+"\" was loaded by ");
347           LogLog.error("["+superClass.getClassLoader()+"] whereas object of type ");
348           LogLog.error("\"" +classObj.getName()+"\" was loaded by ["
349                        +classObj.getClassLoader()+"].");
350           return defaultValue;
351         }
352         return classObj.newInstance();
353       } catch (ClassNotFoundException e) {
354             LogLog.error("Could not instantiate class [" + className + "].", e);
355       } catch (IllegalAccessException e) {
356             LogLog.error("Could not instantiate class [" + className + "].", e);
357       } catch (InstantiationException e) {
358         LogLog.error("Could not instantiate class [" + className + "].", e);
359       } catch (RuntimeException e) {
360             LogLog.error("Could not instantiate class [" + className + "].", e);
361       }
362     }
363     return defaultValue;
364   }
365
366
367   /**
368      Perform variable substitution in string <code>val</code> from the
369      values of keys found in the system propeties.
370
371      <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
372
373      <p>For example, if the System properties contains "key=value", then
374      the call
375      <pre>
376      String s = OptionConverter.substituteVars("Value of key is ${key}.");
377      </pre>
378
379      will set the variable <code>s</code> to "Value of key is value.".
380
381      <p>If no value could be found for the specified key, then the
382      <code>props</code> parameter is searched, if the value could not
383      be found there, then substitution defaults to the empty string.
384
385      <p>For example, if system propeties contains no value for the key
386      "inexistentKey", then the call
387
388      <pre>
389      String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
390      </pre>
391      will set <code>s</code> to "Value of inexistentKey is []"
392
393      <p>An {@link java.lang.IllegalArgumentException} is thrown if
394      <code>val</code> contains a start delimeter "${" which is not
395      balanced by a stop delimeter "}". </p>
396
397      <p><b>Author</b> Avy Sharell</a></p>
398
399      @param val The string on which variable substitution is performed.
400      @throws IllegalArgumentException if <code>val</code> is malformed.
401
402   */
403   public static
404   String substVars(String val, Properties props) throws
405                         IllegalArgumentException {
406
407     StringBuffer sbuf = new StringBuffer();
408
409     int i = 0;
410     int j, k;
411
412     while(true) {
413       j=val.indexOf(DELIM_START, i);
414       if(j == -1) {
415         // no more variables
416         if(i==0) { // this is a simple string
417           return val;
418         } else { // add the tail string which contails no variables and return the result.
419           sbuf.append(val.substring(i, val.length()));
420           return sbuf.toString();
421         }
422       } else {
423         sbuf.append(val.substring(i, j));
424         k = val.indexOf(DELIM_STOP, j);
425         if(k == -1) {
426           throw new IllegalArgumentException('"'+val+
427                       "\" has no closing brace. Opening brace at position " + j
428                                              + '.');
429         } else {
430           j += DELIM_START_LEN;
431           String key = val.substring(j, k);
432           // first try in System properties
433           String replacement = getSystemProperty(key, null);
434           // then try props parameter
435           if(replacement == null && props != null) {
436             replacement =  props.getProperty(key);
437           }
438
439           if(replacement != null) {
440             // Do variable substitution on the replacement string
441             // such that we can solve "Hello ${x2}" as "Hello p1" 
442             // the where the properties are
443             // x1=p1
444             // x2=${x1}
445             String recursiveReplacement = substVars(replacement, props);
446             sbuf.append(recursiveReplacement);
447           }
448           i = k + DELIM_STOP_LEN;
449         }
450       }
451     }
452   }
453
454     /**
455      * Configure log4j given an {@link InputStream}.
456      * 
457      * <p>
458      * The InputStream will be interpreted by a new instance of a log4j configurator.
459      * </p>
460      * <p>
461      * All configurations steps are taken on the <code>hierarchy</code> passed as a parameter.
462      * </p>
463      * 
464      * @param inputStream
465      *            The configuration input stream.
466      * @param clazz
467      *            The class name, of the log4j configurator which will parse the <code>inputStream</code>. This must be a
468      *            subclass of {@link Configurator}, or null. If this value is null then a default configurator of
469      *            {@link PropertyConfigurator} is used.
470      * @param hierarchy
471      *            The {@link org.apache.log4j.Hierarchy} to act on.
472      * @since 1.2.17
473      */
474
475 static
476 public
477 void selectAndConfigure(InputStream inputStream, String clazz, LoggerRepository hierarchy) {
478 Configurator configurator = null;
479
480 if(clazz != null) {
481   LogLog.debug("Preferred configurator class: " + clazz);
482   configurator = (Configurator) instantiateByClassName(clazz,
483                            Configurator.class,
484                            null);
485   if(configurator == null) {
486    LogLog.error("Could not instantiate configurator ["+clazz+"].");
487    return;
488   }
489 } else {
490   configurator = new PropertyConfigurator();
491 }
492
493 configurator.doConfigure(inputStream, hierarchy);
494 }
495
496
497   /**
498      Configure log4j given a URL.
499
500      <p>The url must point to a file or resource which will be interpreted by
501      a new instance of a log4j configurator.
502
503      <p>All configurations steps are taken on the
504      <code>hierarchy</code> passed as a parameter.
505
506      <p>
507      @param url The location of the configuration file or resource.
508      @param clazz The classname, of the log4j configurator which will parse
509      the file or resource at <code>url</code>. This must be a subclass of
510      {@link Configurator}, or null. If this value is null then a default
511      configurator of {@link PropertyConfigurator} is used, unless the
512      filename pointed to by <code>url</code> ends in '.xml', in which case
513      {@link org.apache.log4j.xml.DOMConfigurator} is used.
514      @param hierarchy The {@link org.apache.log4j.Hierarchy} to act on.
515
516      @since 1.1.4 */
517
518   static
519   public
520   void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
521    Configurator configurator = null;
522    String filename = url.getFile();
523
524    if(clazz == null && filename != null && filename.endsWith(".xml")) {
525      clazz = "org.apache.log4j.xml.DOMConfigurator";
526    }
527
528    if(clazz != null) {
529      LogLog.debug("Preferred configurator class: " + clazz);
530      configurator = (Configurator) instantiateByClassName(clazz,
531                                                           Configurator.class,
532                                                           null);
533      if(configurator == null) {
534           LogLog.error("Could not instantiate configurator ["+clazz+"].");
535           return;
536      }
537    } else {
538      configurator = new PropertyConfigurator();
539    }
540
541    configurator.doConfigure(url, hierarchy);
542   }
543 }