001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements. See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache license, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License. You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the license for the specific language governing permissions and
015     * limitations under the license.
016     */
017    package org.apache.logging.log4j.core.helpers;
018    
019    import org.apache.logging.log4j.Logger;
020    import org.apache.logging.log4j.status.StatusLogger;
021    
022    import java.util.Locale;
023    import java.util.Properties;
024    
025    /**
026     * A convenience class to convert property values to specific types.
027     */
028    public final class OptionConverter {
029    
030        private static final Logger LOGGER = StatusLogger.getLogger();
031    
032        private static final String DELIM_START = "${";
033        private static final char DELIM_STOP = '}';
034        private static final int DELIM_START_LEN = 2;
035        private static final int DELIM_STOP_LEN = 1;
036        private static final int ONE_K = 1024;
037    
038        /**
039         * OptionConverter is a static class.
040         */
041        private OptionConverter() {
042        }
043    
044        public static String[] concatanateArrays(String[] l, String[] r) {
045            int len = l.length + r.length;
046            String[] a = new String[len];
047    
048            System.arraycopy(l, 0, a, 0, l.length);
049            System.arraycopy(r, 0, a, l.length, r.length);
050    
051            return a;
052        }
053    
054        public static String convertSpecialChars(String s) {
055            char c;
056            int len = s.length();
057            StringBuffer sbuf = new StringBuffer(len);
058    
059            int i = 0;
060            while (i < len) {
061                c = s.charAt(i++);
062                if (c == '\\') {
063                    c = s.charAt(i++);
064                    if (c == 'n') {
065                        c = '\n';
066                    } else if (c == 'r') {
067                        c = '\r';
068                    } else if (c == 't') {
069                        c = '\t';
070                    } else if (c == 'f') {
071                        c = '\f';
072                    } else if (c == '\b') {
073                        c = '\b';
074                    } else if (c == '\"') {
075                        c = '\"';
076                    } else if (c == '\'') {
077                        c = '\'';
078                    } else if (c == '\\') {
079                        c = '\\';
080                    }
081                }
082                sbuf.append(c);
083            }
084            return sbuf.toString();
085        }
086    
087    
088        /**
089         * Very similar to <code>System.getProperty</code> except
090         * that the {@link SecurityException} is hidden.
091         *
092         * @param key The key to search for.
093         * @param def The default value to return.
094         * @return the string value of the system property, or the default
095         *         value if there is no property with that key.
096         */
097        public static String getSystemProperty(String key, String def) {
098            try {
099                return System.getProperty(key, def);
100            } catch (Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
101                LOGGER.debug("Was not allowed to read system property \"" + key + "\".");
102                return def;
103            }
104        }
105    
106    
107        public static Object instantiateByKey(Properties props, String key, Class<?> superClass,
108                                       Object defaultValue) {
109    
110            // Get the value of the property in string form
111            String className = findAndSubst(key, props);
112            if (className == null) {
113                LOGGER.error("Could not find value for key " + key);
114                return defaultValue;
115            }
116            // Trim className to avoid trailing spaces that cause problems.
117            return OptionConverter.instantiateByClassName(className.trim(), superClass,
118                defaultValue);
119        }
120    
121        /**
122         * If <code>value</code> is "true", then {@code true} is
123         * returned. If <code>value</code> is "false", then
124         * {@code true} is returned. Otherwise, <code>default</code> is
125         * returned.
126         * <p/>
127         * <p>Case of value is unimportant.
128         * @param value The value to convert.
129         * @param dEfault The default value.
130         * @return true or false, depending on the value and/or default.
131         */
132        public static boolean toBoolean(String value, boolean dEfault) {
133            if (value == null) {
134                return dEfault;
135            }
136            String trimmedVal = value.trim();
137            if ("true".equalsIgnoreCase(trimmedVal)) {
138                return true;
139            }
140            if ("false".equalsIgnoreCase(trimmedVal)) {
141                return false;
142            }
143            return dEfault;
144        }
145    
146        /**
147         * Convert the String value to an int.
148         * @param value The value as a String.
149         * @param dEfault The default value.
150         * @return The value as an int.
151         */
152        public static int toInt(String value, int dEfault) {
153            if (value != null) {
154                String s = value.trim();
155                try {
156                    return Integer.valueOf(s);
157                } catch (NumberFormatException e) {
158                    LOGGER.error("[" + s + "] is not in proper int form.");
159                    e.printStackTrace();
160                }
161            }
162            return dEfault;
163        }
164    
165        /**
166         *
167         * @param value The size of the file as a String.
168         * @param defaultValue The default value.
169         * @return The size of the file as a long.
170         */
171        public static long toFileSize(String value, long defaultValue) {
172            if (value == null) {
173                return defaultValue;
174            }
175    
176            String s = value.trim().toUpperCase(Locale.ENGLISH);
177            long multiplier = 1;
178            int index;
179    
180            if ((index = s.indexOf("KB")) != -1) {
181                multiplier = ONE_K;
182                s = s.substring(0, index);
183            } else if ((index = s.indexOf("MB")) != -1) {
184                multiplier = ONE_K * ONE_K;
185                s = s.substring(0, index);
186            } else if ((index = s.indexOf("GB")) != -1) {
187                multiplier = ONE_K * ONE_K * ONE_K;
188                s = s.substring(0, index);
189            }
190            if (s != null) {
191                try {
192                    return Long.valueOf(s) * multiplier;
193                } catch (NumberFormatException e) {
194                    LOGGER.error("[" + s + "] is not in proper int form.");
195                    LOGGER.error("[" + value + "] not in expected format.", e);
196                }
197            }
198            return defaultValue;
199        }
200    
201        /**
202         * Find the value corresponding to <code>key</code> in
203         * <code>props</code>. Then perform variable substitution on the
204         * found value.
205         * @param key The key to locate.
206         * @param props The properties.
207         * @return The String after substitution.
208         */
209        public static String findAndSubst(String key, Properties props) {
210            String value = props.getProperty(key);
211            if (value == null) {
212                return null;
213            }
214    
215            try {
216                return substVars(value, props);
217            } catch (IllegalArgumentException e) {
218                LOGGER.error("Bad option value [" + value + "].", e);
219                return value;
220            }
221        }
222    
223        /**
224         * Instantiate an object given a class name. Check that the
225         * <code>className</code> is a subclass of
226         * <code>superClass</code>. If that test fails or the object could
227         * not be instantiated, then <code>defaultValue</code> is returned.
228         *
229         * @param className    The fully qualified class name of the object to instantiate.
230         * @param superClass   The class to which the new object should belong.
231         * @param defaultValue The object to return in case of non-fulfillment
232         * @return The created object.
233         */
234        public static Object instantiateByClassName(String className, Class<?> superClass,
235                                             Object defaultValue) {
236            if (className != null) {
237                try {
238                    Class<?> classObj = Loader.loadClass(className);
239                    if (!superClass.isAssignableFrom(classObj)) {
240                        LOGGER.error("A \"" + className + "\" object is not assignable to a \"" +
241                            superClass.getName() + "\" variable.");
242                        LOGGER.error("The class \"" + superClass.getName() + "\" was loaded by ");
243                        LOGGER.error("[" + superClass.getClassLoader() + "] whereas object of type ");
244                        LOGGER.error("\"" + classObj.getName() + "\" was loaded by ["
245                            + classObj.getClassLoader() + "].");
246                        return defaultValue;
247                    }
248                    return classObj.newInstance();
249                } catch (ClassNotFoundException e) {
250                    LOGGER.error("Could not instantiate class [" + className + "].", e);
251                } catch (IllegalAccessException e) {
252                    LOGGER.error("Could not instantiate class [" + className + "].", e);
253                } catch (InstantiationException e) {
254                    LOGGER.error("Could not instantiate class [" + className + "].", e);
255                } catch (RuntimeException e) {
256                    LOGGER.error("Could not instantiate class [" + className + "].", e);
257                }
258            }
259            return defaultValue;
260        }
261    
262    
263        /**
264         * Perform variable substitution in string <code>val</code> from the
265         * values of keys found in the system propeties.
266         * <p/>
267         * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
268         * <p/>
269         * <p>For example, if the System properties contains "key=value", then
270         * the call
271         * <pre>
272         * String s = OptionConverter.substituteVars("Value of key is ${key}.");
273         * </pre>
274         * <p/>
275         * will set the variable <code>s</code> to "Value of key is value.".
276         * <p/>
277         * <p>If no value could be found for the specified key, then the
278         * <code>props</code> parameter is searched, if the value could not
279         * be found there, then substitution defaults to the empty string.
280         * <p/>
281         * <p>For example, if system propeties contains no value for the key
282         * "inexistentKey", then the call
283         * <p/>
284         * <pre>
285         * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
286         * </pre>
287         * will set <code>s</code> to "Value of inexistentKey is []"
288         * <p/>
289         * <p>An {@link java.lang.IllegalArgumentException} is thrown if
290         * <code>val</code> contains a start delimeter "${" which is not
291         * balanced by a stop delimeter "}". </p>
292         * <p/>
293         * <p><b>Author</b> Avy Sharell</a></p>
294         *
295         * @param val The string on which variable substitution is performed.
296         * @param props The properties to use for substitution.
297         * @return The String after substitution.
298         * @throws IllegalArgumentException if <code>val</code> is malformed.
299         */
300        public static String substVars(String val, Properties props) throws
301            IllegalArgumentException {
302    
303            StringBuilder sbuf = new StringBuilder();
304    
305            int i = 0;
306            int j;
307            int k;
308    
309            while (true) {
310                j = val.indexOf(DELIM_START, i);
311                if (j == -1) {
312                    // no more variables
313                    if (i == 0) { // this is a simple string
314                        return val;
315                    } else { // add the tail string which contails no variables and return the result.
316                        sbuf.append(val.substring(i, val.length()));
317                        return sbuf.toString();
318                    }
319                } else {
320                    sbuf.append(val.substring(i, j));
321                    k = val.indexOf(DELIM_STOP, j);
322                    if (k == -1) {
323                        throw new IllegalArgumentException('"' + val +
324                            "\" has no closing brace. Opening brace at position " + j
325                            + '.');
326                    } else {
327                        j += DELIM_START_LEN;
328                        String key = val.substring(j, k);
329                        // first try in System properties
330                        String replacement = getSystemProperty(key, null);
331                        // then try props parameter
332                        if (replacement == null && props != null) {
333                            replacement = props.getProperty(key);
334                        }
335    
336                        if (replacement != null) {
337                            // Do variable substitution on the replacement string
338                            // such that we can solve "Hello ${x2}" as "Hello p1"
339                            // the where the properties are
340                            // x1=p1
341                            // x2=${x1}
342                            String recursiveReplacement = substVars(replacement, props);
343                            sbuf.append(recursiveReplacement);
344                        }
345                        i = k + DELIM_STOP_LEN;
346                    }
347                }
348            }
349        }
350    }