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( "Value of key is ${key}." );
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( "Value of inexistentKey is [${inexistentKey}]" );
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 }