001package org.apache.commons.jcs.utils.config; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.Properties; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026 027/** 028 * This class is based on the log4j class org.apache.log4j.helpers.OptionConverter that was made by 029 * Ceki Gülcü Simon Kitching; Avy Sharell (sharell@online.fr) Anders Kristensen Matthieu 030 * Verbert (mve@zurich.ibm.com) A convenience class to convert property values to specific types. 031 */ 032public class OptionConverter 033{ 034 /** The logger */ 035 private static final Log log = LogFactory.getLog( OptionConverter.class ); 036 037 /** System property delimter */ 038 private static final String DELIM_START = "${"; 039 040 /** System property delimter */ 041 private static final char DELIM_STOP = '}'; 042 043 /** System property delimter start length */ 044 private static final int DELIM_START_LEN = 2; 045 046 /** System property delimter end length */ 047 private static final int DELIM_STOP_LEN = 1; 048 049 /** No instances please. */ 050 private OptionConverter() 051 { 052 super(); 053 } 054 055 /** 056 * Combines two arrays. 057 * @param l 058 * @param r 059 * @return String[] 060 */ 061 public static String[] concatanateArrays( String[] l, String[] r ) 062 { 063 int len = l.length + r.length; 064 String[] a = new String[len]; 065 066 System.arraycopy( l, 0, a, 0, l.length ); 067 System.arraycopy( r, 0, a, l.length, r.length ); 068 069 return a; 070 } 071 072 /** 073 * Escapes special characters. 074 * 075 * @param s 076 * @return String 077 */ 078 public static String convertSpecialChars( String s ) 079 { 080 char c; 081 int len = s.length(); 082 StringBuilder sb = new StringBuilder( len ); 083 084 int i = 0; 085 while ( i < len ) 086 { 087 c = s.charAt( i++ ); 088 if ( c == '\\' ) 089 { 090 c = s.charAt( i++ ); 091 if ( c == 'n' ) 092 { 093 c = '\n'; 094 } 095 else if ( c == 'r' ) 096 { 097 c = '\r'; 098 } 099 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}