View Javadoc
1   package org.apache.commons.jcs3.utils.config;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Properties;
23  
24  import org.apache.commons.jcs3.log.Log;
25  import org.apache.commons.jcs3.log.LogManager;
26  
27  /**
28   * This class is based on the log4j class org.apache.log4j.helpers.OptionConverter that was made by
29   * Ceki Gülcü Simon Kitching; Avy Sharell (sharell@online.fr) Anders Kristensen Matthieu
30   * Verbert (mve@zurich.ibm.com) A convenience class to convert property values to specific types.
31   */
32  public class OptionConverter
33  {
34      /** The logger */
35      private static final Log log = LogManager.getLog( OptionConverter.class );
36  
37      /** System property delimter */
38      private static final String DELIM_START = "${";
39  
40      /** System property delimter */
41      private static final char DELIM_STOP = '}';
42  
43      /** System property delimter start length */
44      private static final int DELIM_START_LEN = 2;
45  
46      /** System property delimter end length */
47      private static final int DELIM_STOP_LEN = 1;
48  
49      /** No instances please. */
50      private OptionConverter()
51      {
52      }
53  
54      /**
55       * Combines two arrays.
56       * @param l
57       * @param r
58       * @return String[]
59       */
60      public static String[] concatanateArrays( final String[] l, final String[] r )
61      {
62          final int len = l.length + r.length;
63          final String[] a = new String[len];
64  
65          System.arraycopy( l, 0, a, 0, l.length );
66          System.arraycopy( r, 0, a, l.length, r.length );
67  
68          return a;
69      }
70  
71      /**
72       * Escapes special characters.
73       *
74       * @param s
75       * @return String
76       */
77      public static String convertSpecialChars( final String s )
78      {
79          char c;
80          final int len = s.length();
81          final StringBuilder sb = new StringBuilder( len );
82  
83          int i = 0;
84          while ( i < len )
85          {
86              c = s.charAt( i++ );
87              if ( c == '\\' )
88              {
89                  c = s.charAt( i++ );
90                  if ( c == 'n' )
91                  {
92                      c = '\n';
93                  }
94                  else if ( c == 'r' )
95                  {
96                      c = '\r';
97                  }
98                  else if ( c == 't' )
99                  {
100                     c = '\t';
101                 }
102                 else if ( c == 'f' )
103                 {
104                     c = '\f';
105                 }
106                 else if ( c == '\b' )
107                 {
108                     c = '\b';
109                 }
110                 else if ( c == '\"' )
111                 {
112                     c = '\"';
113                 }
114                 else if ( c == '\'' )
115                 {
116                     c = '\'';
117                 }
118                 else if ( c == '\\' )
119                 {
120                     c = '\\';
121                 }
122             }
123             sb.append( c );
124         }
125         return sb.toString();
126     }
127 
128     /**
129      * Very similar to <code>System.getProperty</code> except that the {@link SecurityException} is
130      * hidden.
131      * @param key The key to search for.
132      * @param def The default value to return.
133      * @return the string value of the system property, or the default value if there is no property
134      *         with that key.
135      * @since 1.1
136      */
137 
138     public static String getSystemProperty( final String key, final String def )
139     {
140         try
141         {
142             return System.getProperty( key, def );
143         }
144         catch ( final Throwable e )
145         {
146             // MS-Java throws com.ms.security.SecurityExceptionEx
147             log.debug( "Was not allowed to read system property \"{0}\".", key );
148             return def;
149         }
150     }
151 
152     /**
153      * Creates an object for the className value of the key.
154      *
155      * @param props
156      * @param key
157      * @param defaultValue
158      * @return Object that was created
159      */
160     public static <T> T instantiateByKey( final Properties props, final String key, final T defaultValue )
161     {
162 
163         // Get the value of the property in string form
164         final String className = findAndSubst( key, props );
165         if ( className == null )
166         {
167             log.trace( "Could not find value for key {0}", key );
168             return defaultValue;
169         }
170         // Trim className to avoid trailing spaces that cause problems.
171         return OptionConverter.instantiateByClassName( className.trim(), defaultValue );
172     }
173 
174     /**
175      * If <code>value</code> is "true", then {@code true} is returned. If <code>value</code> is
176      * "false", then {@code true} is returned. Otherwise, <code>default</code> is returned.
177      *
178      * Case of value is unimportant.
179      * @param value
180      * @param defaultValue
181      * @return Object
182      */
183     public static boolean toBoolean( final String value, final boolean defaultValue )
184     {
185         if ( value == null )
186         {
187             return defaultValue;
188         }
189         final String trimmedVal = value.trim();
190         if ( "true".equalsIgnoreCase( trimmedVal ) )
191         {
192             return true;
193         }
194         if ( "false".equalsIgnoreCase( trimmedVal ) )
195         {
196             return false;
197         }
198         return defaultValue;
199     }
200 
201     /**
202      * Converts to int.
203      *
204      * @param value
205      * @param defaultValue
206      * @return int
207      */
208     public static int toInt( final String value, final int defaultValue )
209     {
210         if ( value != null )
211         {
212             final String s = value.trim();
213             try
214             {
215                 return Integer.parseInt(s);
216             }
217             catch ( final NumberFormatException e )
218             {
219                 log.error( "[{0}] is not in proper int form.", s, e );
220             }
221         }
222         return defaultValue;
223     }
224 
225     /**
226      * @param value
227      * @param defaultValue
228      * @return long
229      */
230     public static long toFileSize( final String value, final long defaultValue )
231     {
232         if ( value == null )
233         {
234             return defaultValue;
235         }
236 
237         String s = value.trim().toUpperCase();
238         long multiplier = 1;
239         int index;
240 
241         if ( ( index = s.indexOf( "KB" ) ) != -1 )
242         {
243             multiplier = 1024;
244             s = s.substring( 0, index );
245         }
246         else if ( ( index = s.indexOf( "MB" ) ) != -1 )
247         {
248             multiplier = 1024 * 1024;
249             s = s.substring( 0, index );
250         }
251         else if ( ( index = s.indexOf( "GB" ) ) != -1 )
252         {
253             multiplier = 1024 * 1024 * 1024;
254             s = s.substring( 0, index );
255         }
256         try
257         {
258             return Long.parseLong(s) * multiplier;
259         }
260         catch ( final NumberFormatException e )
261         {
262             log.error( "[{0}] is not in proper int form.", s);
263             log.error( "[{0}] not in expected format", value, e );
264         }
265         return defaultValue;
266     }
267 
268     /**
269      * Find the value corresponding to <code>key</code> in <code>props</code>. Then perform variable
270      * substitution on the found value.
271      *
272      * @param key
273      * @param props
274      * @return substituted string
275      */
276 
277     public static String findAndSubst( final String key, final Properties props )
278     {
279         final String value = props.getProperty( key );
280         if ( value == null )
281         {
282             return null;
283         }
284 
285         try
286         {
287             return substVars( value, props );
288         }
289         catch ( final IllegalArgumentException e )
290         {
291             log.error( "Bad option value [{0}]", value, e );
292             return value;
293         }
294     }
295 
296     /**
297      * Instantiate an object given a class name. Check that the <code>className</code> is a subclass
298      * of <code>superClass</code>. If that test fails or the object could not be instantiated, then
299      * <code>defaultValue</code> is returned.
300      *
301      * @param className The fully qualified class name of the object to instantiate.
302      * @param defaultValue The object to return in case of non-fulfillment
303      * @return instantiated object
304      */
305 
306     public static <T> T instantiateByClassName( final String className, final T defaultValue )
307     {
308         if ( className != null )
309         {
310             try
311             {
312                 final Class<?> classObj = Class.forName( className );
313                 final Object o = classObj.getDeclaredConstructor().newInstance();
314 
315                 try
316                 {
317                     @SuppressWarnings("unchecked") // CCE catched
318                     final
319                     T t = (T) o;
320                     return t;
321                 }
322                 catch (final ClassCastException e)
323                 {
324                     log.error( "A \"{0}\" object is not assignable to the "
325                             + "generic variable.", className );
326                     return defaultValue;
327                 }
328             }
329             catch (final Exception e )
330             {
331                 log.error( "Could not instantiate class [{0}]", className, e );
332             }
333         }
334         return defaultValue;
335     }
336 
337     /**
338      * Perform variable substitution in string <code>val</code> from the values of keys found in the
339      * system properties.
340      *
341      * The variable substitution delimiters are <b>${ </b> and <b>} </b>.
342      *
343      * For example, if the System properties contains "key=value", then the call
344      *
345      * <pre>
346      * String s = OptionConverter.substituteVars( &quot;Value of key is ${key}.&quot; );
347      * </pre>
348      *
349      * will set the variable <code>s</code> to "Value of key is value.".
350      *
351      * If no value could be found for the specified key, then the <code>props</code> parameter is
352      * searched, if the value could not be found there, then substitution defaults to the empty
353      * string.
354      *
355      * For example, if system properties contains no value for the key "inexistentKey", then the call
356      *
357      * <pre>
358      * String s = OptionConverter.subsVars( &quot;Value of inexistentKey is [${inexistentKey}]&quot; );
359      * </pre>
360      *
361      * will set <code>s</code> to "Value of inexistentKey is []"
362      * <p>
363      * An {@link java.lang.IllegalArgumentException}is thrown if <code>val</code> contains a start
364      * delimiter "${" which is not balanced by a stop delimiter "}".
365      * </p>
366      * <p>
367      * <b>Author </b> Avy Sharell
368      * </p>
369      * @param val The string on which variable substitution is performed.
370      * @param props
371      * @return String
372      * @throws IllegalArgumentException if <code>val</code> is malformed.
373      */
374 
375     public static String substVars( final String val, final Properties props )
376         throws IllegalArgumentException
377     {
378         final StringBuilder sbuf = new StringBuilder();
379 
380         int i = 0;
381         int j;
382         int k;
383 
384         while ( true )
385         {
386             j = val.indexOf( DELIM_START, i );
387             if ( j == -1 )
388             {
389                 if ( i == 0 )
390                 {
391                     return val;
392                 }
393                 sbuf.append( val.substring( i ) );
394                 return sbuf.toString();
395             }
396             sbuf.append(val, i, j);
397             k = val.indexOf( DELIM_STOP, j );
398             if ( k == -1 )
399             {
400                 throw new IllegalArgumentException( '"' + val + "\" has no closing brace. Opening brace at position "
401                     + j + '.' );
402             }
403             j += DELIM_START_LEN;
404             final String key = val.substring( j, k );
405             // first try in System properties
406             String replacement = getSystemProperty( key, null );
407             // then try props parameter
408             if ( replacement == null && props != null )
409             {
410                 replacement = props.getProperty( key );
411             }
412 
413             if ( replacement != null )
414             {
415                 sbuf.append( replacement );
416             }
417             i = k + DELIM_STOP_LEN;
418         }
419     }
420 }