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