1 package org.apache.commons.jcs.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.logging.Log;
25 import org.apache.commons.logging.LogFactory;
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 = LogFactory.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 super();
53 }
54
55 /**
56 * Combines two arrays.
57 * @param l
58 * @param r
59 * @return String[]
60 */
61 public static String[] concatanateArrays( String[] l, String[] r )
62 {
63 int len = l.length + r.length;
64 String[] a = new String[len];
65
66 System.arraycopy( l, 0, a, 0, l.length );
67 System.arraycopy( r, 0, a, l.length, r.length );
68
69 return a;
70 }
71
72 /**
73 * Escapes special characters.
74 *
75 * @param s
76 * @return String
77 */
78 public static String convertSpecialChars( String s )
79 {
80 char c;
81 int len = s.length();
82 StringBuilder sb = new StringBuilder( len );
83
84 int i = 0;
85 while ( i < len )
86 {
87 c = s.charAt( i++ );
88 if ( c == '\\' )
89 {
90 c = s.charAt( i++ );
91 if ( c == 'n' )
92 {
93 c = '\n';
94 }
95 else if ( c == 'r' )
96 {
97 c = '\r';
98 }
99 else if ( c == 't' )
100 {
101 c = '\t';
102 }
103 else if ( c == 'f' )
104 {
105 c = '\f';
106 }
107 else if ( c == '\b' )
108 {
109 c = '\b';
110 }
111 else if ( c == '\"' )
112 {
113 c = '\"';
114 }
115 else if ( c == '\'' )
116 {
117 c = '\'';
118 }
119 else if ( c == '\\' )
120 {
121 c = '\\';
122 }
123 }
124 sb.append( c );
125 }
126 return sb.toString();
127 }
128
129 /**
130 * Very similar to <code>System.getProperty</code> except that the {@link SecurityException} is
131 * hidden.
132 * @param key The key to search for.
133 * @param def The default value to return.
134 * @return the string value of the system property, or the default value if there is no property
135 * with that key.
136 * @since 1.1
137 */
138
139 public static String getSystemProperty( String key, String def )
140 {
141 try
142 {
143 return System.getProperty( key, def );
144 }
145 catch ( Throwable e )
146 {
147 // MS-Java throws com.ms.security.SecurityExceptionEx
148 log.debug( "Was not allowed to read system property \"" + key + "\"." );
149 return def;
150 }
151 }
152
153 /**
154 * Creates an object for the className value of the key.
155 *
156 * @param props
157 * @param key
158 * @param defaultValue
159 * @return Object that was created
160 */
161 public static <T> T instantiateByKey( Properties props, String key, T defaultValue )
162 {
163
164 // Get the value of the property in string form
165 String className = findAndSubst( key, props );
166 if ( className == null )
167 {
168 if ( log.isTraceEnabled() )
169 {
170 log.info( "Could not find value for key " + key );
171 }
172 return defaultValue;
173 }
174 // Trim className to avoid trailing spaces that cause problems.
175 return OptionConverter.instantiateByClassName( className.trim(), defaultValue );
176 }
177
178 /**
179 * If <code>value</code> is "true", then <code>true</code> is returned. If <code>value</code> is
180 * "false", then <code>true</code> is returned. Otherwise, <code>default</code> is returned.
181 *
182 * Case of value is unimportant.
183 * @param value
184 * @param defaultValue
185 * @return Object
186 */
187 public static boolean toBoolean( String value, boolean defaultValue )
188 {
189 if ( value == null )
190 {
191 return defaultValue;
192 }
193 String trimmedVal = value.trim();
194 if ( "true".equalsIgnoreCase( trimmedVal ) )
195 {
196 return true;
197 }
198 if ( "false".equalsIgnoreCase( trimmedVal ) )
199 {
200 return false;
201 }
202 return defaultValue;
203 }
204
205 /**
206 * Converts to int.
207 *
208 * @param value
209 * @param defaultValue
210 * @return int
211 */
212 public static int toInt( String value, int defaultValue )
213 {
214 if ( value != null )
215 {
216 String s = value.trim();
217 try
218 {
219 return Integer.parseInt(s);
220 }
221 catch ( NumberFormatException e )
222 {
223 log.error( "[" + s + "] is not in proper int form." );
224 e.printStackTrace();
225 }
226 }
227 return defaultValue;
228 }
229
230 /**
231 * @param value
232 * @param defaultValue
233 * @return long
234 */
235 public static long toFileSize( String value, long defaultValue )
236 {
237 if ( value == null )
238 {
239 return defaultValue;
240 }
241
242 String s = value.trim().toUpperCase();
243 long multiplier = 1;
244 int index;
245
246 if ( ( index = s.indexOf( "KB" ) ) != -1 )
247 {
248 multiplier = 1024;
249 s = s.substring( 0, index );
250 }
251 else if ( ( index = s.indexOf( "MB" ) ) != -1 )
252 {
253 multiplier = 1024 * 1024;
254 s = s.substring( 0, index );
255 }
256 else if ( ( index = s.indexOf( "GB" ) ) != -1 )
257 {
258 multiplier = 1024 * 1024 * 1024;
259 s = s.substring( 0, index );
260 }
261 if ( s != null )
262 {
263 try
264 {
265 return Long.parseLong(s) * multiplier;
266 }
267 catch ( NumberFormatException e )
268 {
269 log.error( "[" + s + "] is not in proper int form" );
270 log.error( "[" + value + "] not in expected format", e );
271 }
272 }
273 return defaultValue;
274 }
275
276 /**
277 * Find the value corresponding to <code>key</code> in <code>props</code>. Then perform variable
278 * substitution on the found value.
279 *
280 * @param key
281 * @param props
282 * @return substituted string
283 */
284
285 public static String findAndSubst( String key, Properties props )
286 {
287 String value = props.getProperty( key );
288 if ( value == null )
289 {
290 return null;
291 }
292
293 try
294 {
295 return substVars( value, props );
296 }
297 catch ( IllegalArgumentException e )
298 {
299 log.error( "Bad option value [" + value + "]", e );
300 return value;
301 }
302 }
303
304 /**
305 * Instantiate an object given a class name. Check that the <code>className</code> is a subclass
306 * of <code>superClass</code>. If that test fails or the object could not be instantiated, then
307 * <code>defaultValue</code> is returned.
308 *
309 * @param className The fully qualified class name of the object to instantiate.
310 * @param defaultValue The object to return in case of non-fulfillment
311 * @return instantiated object
312 */
313
314 public static <T> T instantiateByClassName( String className, T defaultValue )
315 {
316 if ( className != null )
317 {
318 try
319 {
320 Class<?> classObj = Class.forName( className );
321 Object o = classObj.newInstance();
322
323 try
324 {
325 @SuppressWarnings("unchecked") // CCE catched
326 T t = (T) o;
327 return t;
328 }
329 catch (ClassCastException e)
330 {
331 log.error( "A \"" + className + "\" object is not assignable to the generic variable." );
332 return defaultValue;
333 }
334 }
335 catch ( Exception e )
336 {
337 log.error( "Could not instantiate class [" + className + "]", e );
338 }
339 }
340 return defaultValue;
341 }
342
343 /**
344 * Perform variable substitution in string <code>val</code> from the values of keys found in the
345 * system properties.
346 *
347 * The variable substitution delimiters are <b>${ </b> and <b>} </b>.
348 *
349 * For example, if the System properties contains "key=value", then the call
350 *
351 * <pre>
352 * String s = OptionConverter.substituteVars( "Value of key is ${key}." );
353 * </pre>
354 *
355 * will set the variable <code>s</code> to "Value of key is value.".
356 *
357 * If no value could be found for the specified key, then the <code>props</code> parameter is
358 * searched, if the value could not be found there, then substitution defaults to the empty
359 * string.
360 *
361 * For example, if system properties contains no value for the key "inexistentKey", then the call
362 *
363 * <pre>
364 * String s = OptionConverter.subsVars( "Value of inexistentKey is [${inexistentKey}]" );
365 * </pre>
366 *
367 * will set <code>s</code> to "Value of inexistentKey is []"
368 * <p>
369 * An {@link java.lang.IllegalArgumentException}is thrown if <code>val</code> contains a start
370 * delimiter "${" which is not balanced by a stop delimiter "}".
371 * </p>
372 * <p>
373 * <b>Author </b> Avy Sharell
374 * </p>
375 * @param val The string on which variable substitution is performed.
376 * @param props
377 * @return String
378 * @throws IllegalArgumentException if <code>val</code> is malformed.
379 */
380
381 public static String substVars( String val, Properties props )
382 throws IllegalArgumentException
383 {
384 StringBuilder sbuf = new StringBuilder();
385
386 int i = 0;
387 int j;
388 int k;
389
390 while ( true )
391 {
392 j = val.indexOf( DELIM_START, i );
393 if ( j == -1 )
394 {
395 if ( i == 0 )
396 {
397 return val;
398 }
399 sbuf.append( val.substring( i, val.length() ) );
400 return sbuf.toString();
401 }
402 sbuf.append( val.substring( i, j ) );
403 k = val.indexOf( DELIM_STOP, j );
404 if ( k == -1 )
405 {
406 throw new IllegalArgumentException( '"' + val + "\" has no closing brace. Opening brace at position "
407 + j + '.' );
408 }
409 j += DELIM_START_LEN;
410 String key = val.substring( j, k );
411 // first try in System properties
412 String replacement = getSystemProperty( key, null );
413 // then try props parameter
414 if ( replacement == null && props != null )
415 {
416 replacement = props.getProperty( key );
417 }
418
419 if ( replacement != null )
420 {
421 sbuf.append( replacement );
422 }
423 i = k + DELIM_STOP_LEN;
424 }
425 }
426 }