001    package org.apache.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    
022    import java.util.Properties;
023    
024    import org.apache.commons.logging.Log;
025    import 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     */
032    public class OptionConverter
033    {
034        /** The logger */
035        private final static Log log = LogFactory.getLog( OptionConverter.class );
036    
037        /** System property delimter */
038        static String DELIM_START = "${";
039    
040        /** System property delimter */
041        static char DELIM_STOP = '}';
042    
043        /** System property delimter start length */
044        static int DELIM_START_LEN = 2;
045    
046        /** System property delimter end length */
047        static int DELIM_STOP_LEN = 1;
048    
049        /** working buffer */
050        static StringBuffer sbuf = new StringBuffer();
051    
052        /** No instances please. */
053        private OptionConverter()
054        {
055            super();
056        }
057    
058        /**
059         * Combines two arrays.
060         * @param l
061         * @param r
062         * @return String[]
063         */
064        public static String[] concatanateArrays( String[] l, String[] r )
065        {
066            int len = l.length + r.length;
067            String[] a = new String[len];
068    
069            System.arraycopy( l, 0, a, 0, l.length );
070            System.arraycopy( r, 0, a, l.length, r.length );
071    
072            return a;
073        }
074    
075        /**
076         * Escapes special characters.
077         * <p>
078         * @param s
079         * @return String
080         */
081        public static String convertSpecialChars( String s )
082        {
083            char c;
084            int len = s.length();
085            StringBuffer sbuf = new StringBuffer( len );
086    
087            int i = 0;
088            while ( i < len )
089            {
090                c = s.charAt( i++ );
091                if ( c == '\\' )
092                {
093                    c = s.charAt( i++ );
094                    if ( c == 'n' )
095                    {
096                        c = '\n';
097                    }
098                    else if ( c == 'r' )
099                    {
100                        c = '\r';
101                    }
102                    else if ( c == 't' )
103                    {
104                        c = '\t';
105                    }
106                    else if ( c == 'f' )
107                    {
108                        c = '\f';
109                    }
110                    else if ( c == '\b' )
111                    {
112                        c = '\b';
113                    }
114                    else if ( c == '\"' )
115                    {
116                        c = '\"';
117                    }
118                    else if ( c == '\'' )
119                    {
120                        c = '\'';
121                    }
122                    else if ( c == '\\' )
123                    {
124                        c = '\\';
125                    }
126                }
127                sbuf.append( c );
128            }
129            return sbuf.toString();
130        }
131    
132        /**
133         * Very similar to <code>System.getProperty</code> except that the {@link SecurityException} is
134         * hidden.
135         * @param key The key to search for.
136         * @param def The default value to return.
137         * @return the string value of the system property, or the default value if there is no property
138         *         with that key.
139         * @since 1.1
140         */
141    
142        public static String getSystemProperty( String key, String def )
143        {
144            try
145            {
146                return System.getProperty( key, def );
147            }
148            catch ( Throwable e )
149            {
150                // MS-Java throws com.ms.security.SecurityExceptionEx
151                log.debug( "Was not allowed to read system property \"" + key + "\"." );
152                return def;
153            }
154        }
155    
156        /**
157         * Creates an object for the className value of the key.
158         * <p>
159         * @param props
160         * @param key
161         * @param defaultValue
162         * @return Object that was created
163         */
164        public static <T> T instantiateByKey( Properties props, String key, T defaultValue )
165        {
166    
167            // Get the value of the property in string form
168            String className = findAndSubst( key, props );
169            if ( className == null )
170            {
171                if ( log.isTraceEnabled() )
172                {
173                    log.info( "Could not find value for key " + key );
174                }
175                return defaultValue;
176            }
177            // Trim className to avoid trailing spaces that cause problems.
178            return OptionConverter.instantiateByClassName( className.trim(), defaultValue );
179        }
180    
181        /**
182         * If <code>value</code> is "true", then <code>true</code> is returned. If <code>value</code> is
183         * "false", then <code>true</code> is returned. Otherwise, <code>default</code> is returned.
184         * <p>
185         * Case of value is unimportant.
186         * @param value
187         * @param dEfault
188         * @return Object
189         */
190    
191        public static boolean toBoolean( String value, boolean dEfault )
192        {
193            if ( value == null )
194            {
195                return dEfault;
196            }
197            String trimmedVal = value.trim();
198            if ( "true".equalsIgnoreCase( trimmedVal ) )
199            {
200                return true;
201            }
202            if ( "false".equalsIgnoreCase( trimmedVal ) )
203            {
204                return false;
205            }
206            return dEfault;
207        }
208    
209        /**
210         * Converts to int.
211         * <p>
212         * @param value
213         * @param dEfault
214         * @return int
215         */
216        public static int toInt( String value, int dEfault )
217        {
218            if ( value != null )
219            {
220                String s = value.trim();
221                try
222                {
223                    return Integer.valueOf( s ).intValue();
224                }
225                catch ( NumberFormatException e )
226                {
227                    log.error( "[" + s + "] is not in proper int form." );
228                    e.printStackTrace();
229                }
230            }
231            return dEfault;
232        }
233    
234        /**
235         * @param value
236         * @param dEfault
237         * @return long
238         */
239        public static long toFileSize( String value, long dEfault )
240        {
241            if ( value == null )
242            {
243                return dEfault;
244            }
245    
246            String s = value.trim().toUpperCase();
247            long multiplier = 1;
248            int index;
249    
250            if ( ( index = s.indexOf( "KB" ) ) != -1 )
251            {
252                multiplier = 1024;
253                s = s.substring( 0, index );
254            }
255            else if ( ( index = s.indexOf( "MB" ) ) != -1 )
256            {
257                multiplier = 1024 * 1024;
258                s = s.substring( 0, index );
259            }
260            else if ( ( index = s.indexOf( "GB" ) ) != -1 )
261            {
262                multiplier = 1024 * 1024 * 1024;
263                s = s.substring( 0, index );
264            }
265            if ( s != null )
266            {
267                try
268                {
269                    return Long.valueOf( s ).longValue() * multiplier;
270                }
271                catch ( NumberFormatException e )
272                {
273                    log.error( "[" + s + "] is not in proper int form" );
274                    log.error( "[" + value + "] not in expected format", e );
275                }
276            }
277            return dEfault;
278        }
279    
280        /**
281         * Find the value corresponding to <code>key</code> in <code>props</code>. Then perform variable
282         * substitution on the found value.
283         * <p>
284         * @param key
285         * @param props
286         * @return substituted string
287         */
288    
289        public static String findAndSubst( String key, Properties props )
290        {
291            String value = props.getProperty( key );
292            if ( value == null )
293            {
294                return null;
295            }
296    
297            try
298            {
299                return substVars( value, props );
300            }
301            catch ( IllegalArgumentException e )
302            {
303                log.error( "Bad option value [" + value + "]", e );
304                return value;
305            }
306        }
307    
308        /**
309         * Instantiate an object given a class name. Check that the <code>className</code> is a subclass
310         * of <code>superClass</code>. If that test fails or the object could not be instantiated, then
311         * <code>defaultValue</code> is returned.
312         * <p>
313         * @param className The fully qualified class name of the object to instantiate.
314         * @param defaultValue The object to return in case of non-fulfillment
315         * @return instantiated object
316         */
317    
318        public static <T> T instantiateByClassName( String className, T defaultValue )
319        {
320            if ( className != null )
321            {
322                try
323                {
324                    Class<?> classObj = Class.forName( className );
325                    Object o = classObj.newInstance();
326    
327                    try
328                    {
329                        @SuppressWarnings("unchecked") // CCE catched
330                        T t = (T) o;
331                        return t;
332                    }
333                    catch (ClassCastException e)
334                    {
335                        log.error( "A \"" + className + "\" object is not assignable to the generic variable." );
336                        return defaultValue;
337                    }
338                }
339                catch ( Exception e )
340                {
341                    log.error( "Could not instantiate class [" + className + "]", e );
342                }
343            }
344            return defaultValue;
345        }
346    
347        /**
348         * Perform variable substitution in string <code>val</code> from the values of keys found in the
349         * system properties.
350         * <p>
351         * The variable substitution delimeters are <b>${ </b> and <b>} </b>.
352         * <p>
353         * For example, if the System properties contains "key=value", then the call
354         *
355         * <pre>
356         * String s = OptionConverter.substituteVars( &quot;Value of key is ${key}.&quot; );
357         * </pre>
358         *
359         * will set the variable <code>s</code> to "Value of key is value.".
360         * <p>
361         * If no value could be found for the specified key, then the <code>props</code> parameter is
362         * searched, if the value could not be found there, then substitution defaults to the empty
363         * string.
364         * <p>
365         * For example, if system propeties contains no value for the key "inexistentKey", then the call
366         *
367         * <pre>
368         * String s = OptionConverter.subsVars( &quot;Value of inexistentKey is [${inexistentKey}]&quot; );
369         * </pre>
370         *
371         * will set <code>s</code> to "Value of inexistentKey is []"
372         * <p>
373         * An {@link java.lang.IllegalArgumentException}is thrown if <code>val</code> contains a start
374         * delimeter "${" which is not balanced by a stop delimeter "}".
375         * </p>
376         * <p>
377         * <b>Author </b> Avy Sharell </a>
378         * </p>
379         * @param val The string on which variable substitution is performed.
380         * @param props
381         * @return String
382         * @throws IllegalArgumentException if <code>val</code> is malformed.
383         */
384    
385        public static String substVars( String val, Properties props )
386            throws IllegalArgumentException
387        {
388            sbuf.setLength( 0 );
389    
390            int i = 0;
391            int j;
392            int k;
393    
394            while ( true )
395            {
396                j = val.indexOf( DELIM_START, i );
397                if ( j == -1 )
398                {
399                    if ( i == 0 )
400                    {
401                        return val;
402                    }
403                    sbuf.append( val.substring( i, val.length() ) );
404                    return sbuf.toString();
405                }
406                sbuf.append( val.substring( i, j ) );
407                k = val.indexOf( DELIM_STOP, j );
408                if ( k == -1 )
409                {
410                    throw new IllegalArgumentException( '"' + val + "\" has no closing brace. Opening brace at position "
411                        + j + '.' );
412                }
413                j += DELIM_START_LEN;
414                String key = val.substring( j, k );
415                // first try in System properties
416                String replacement = getSystemProperty( key, null );
417                // then try props parameter
418                if ( replacement == null && props != null )
419                {
420                    replacement = props.getProperty( key );
421                }
422    
423                if ( replacement != null )
424                {
425                    sbuf.append( replacement );
426                }
427                i = k + DELIM_STOP_LEN;
428            }
429        }
430    }