001package org.apache.commons.jcs3.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.jcs3.log.Log; 025import org.apache.commons.jcs3.log.LogManager; 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 = LogManager.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 } 053 054 /** 055 * Combines two arrays. 056 * @param l 057 * @param r 058 * @return String[] 059 */ 060 public static String[] concatanateArrays( final String[] l, final String[] r ) 061 { 062 final int len = l.length + r.length; 063 final String[] a = new String[len]; 064 065 System.arraycopy( l, 0, a, 0, l.length ); 066 System.arraycopy( r, 0, a, l.length, r.length ); 067 068 return a; 069 } 070 071 /** 072 * Escapes special characters. 073 * 074 * @param s 075 * @return String 076 */ 077 public static String convertSpecialChars( final String s ) 078 { 079 char c; 080 final int len = s.length(); 081 final StringBuilder sb = new StringBuilder( len ); 082 083 int i = 0; 084 while ( i < len ) 085 { 086 c = s.charAt( i++ ); 087 if ( c == '\\' ) 088 { 089 c = s.charAt( i++ ); 090 if ( c == 'n' ) 091 { 092 c = '\n'; 093 } 094 else if ( c == 'r' ) 095 { 096 c = '\r'; 097 } 098 else if ( c == 't' ) 099 { 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}