1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.util;
15  
16  import java.lang.reflect.Constructor;
17  import java.lang.reflect.InvocationTargetException;
18  import java.util.Properties;
19  
20  import ch.qos.logback.core.Context;
21  import ch.qos.logback.core.spi.ContextAware;
22  import ch.qos.logback.core.spi.PropertyContainer;
23  import ch.qos.logback.core.spi.ScanException;
24  import ch.qos.logback.core.subst.NodeToStringTransformer;
25  
26  /**
27   * @author Ceki Gulcu
28   */
29  public class OptionHelper {
30  
31      public static Object instantiateByClassName(String className, Class<?> superClass, Context context)
32              throws IncompatibleClassException, DynamicClassLoadingException {
33          ClassLoader classLoader = Loader.getClassLoaderOfObject(context);
34          return instantiateByClassName(className, superClass, classLoader);
35      }
36  
37      public static Object instantiateByClassNameAndParameter(String className, Class<?> superClass, Context context,
38              Class<?> type, Object param) throws IncompatibleClassException, DynamicClassLoadingException {
39          ClassLoader classLoader = Loader.getClassLoaderOfObject(context);
40          return instantiateByClassNameAndParameter(className, superClass, classLoader, type, param);
41      }
42  
43      public static Object instantiateByClassName(String className, Class<?> superClass, ClassLoader classLoader)
44              throws IncompatibleClassException, DynamicClassLoadingException {
45          return instantiateByClassNameAndParameter(className, superClass, classLoader, null, null);
46      }
47  
48  
49      public static Object instantiateClassWithSuperclassRestriction(Class<?> classObj, Class<?> superClass)
50              throws IncompatibleClassException, DynamicClassLoadingException {
51          if (!superClass.isAssignableFrom(classObj)) {
52              throw new IncompatibleClassException(superClass, classObj);
53          }
54  
55          try {
56              return classObj.getConstructor().newInstance();
57          } catch (NoSuchMethodException|InstantiationException|IllegalAccessException|InvocationTargetException e) {
58              throw new DynamicClassLoadingException("Failed to instantiate type " + classObj.getName(), e);
59          }
60      }
61  
62      public static Object instantiateByClassNameAndParameter(String className, Class<?> superClass,
63              ClassLoader classLoader, Class<?> type, Object parameter)
64              throws IncompatibleClassException, DynamicClassLoadingException {
65  
66          if (className == null) {
67              throw new NullPointerException();
68          }
69          try {
70              Class<?> classObj = null;
71              classObj = classLoader.loadClass(className);
72              if (!superClass.isAssignableFrom(classObj)) {
73                  throw new IncompatibleClassException(superClass, classObj);
74              }
75              if (type == null) {
76                  return classObj.getConstructor().newInstance();
77              } else {
78                  Constructor<?> constructor = classObj.getConstructor(type);
79                  return constructor.newInstance(parameter);
80              }
81          } catch (IncompatibleClassException ice) {
82              throw ice;
83          } catch (Throwable t) {
84              throw new DynamicClassLoadingException("Failed to instantiate type " + className, t);
85          }
86      }
87  
88      /**
89       * Find the value corresponding to <code>key</code> in <code>props</code>. Then
90       * perform variable substitution on the found value.
91       */
92      // public static String findAndSubst(String key, Properties props) {
93      // String value = props.getProperty(key);
94      //
95      // if (value == null) {
96      // return null;
97      // }
98      //
99      // try {
100     // return substVars(value, props);
101     // } catch (IllegalArgumentException e) {
102     // return value;
103     // }
104     // }
105     final static String DELIM_START = "${";
106     final static char DELIM_STOP = '}';
107     final static String DELIM_DEFAULT = ":-";
108 
109     final static int DELIM_START_LEN = 2;
110     final static int DELIM_STOP_LEN = 1;
111     final static int DELIM_DEFAULT_LEN = 2;
112 
113     final static String _IS_UNDEFINED = "_IS_UNDEFINED";
114 
115     /**
116      * @see #substVars(String, PropertyContainer, PropertyContainer)
117      */
118     public static String substVars(String val, PropertyContainer pc1) throws ScanException {
119         return substVars(val, pc1, null);
120     }
121 
122     /**
123      * See http://logback.qos.ch/manual/configuration.html#variableSubstitution
124      */
125     public static String substVars(String input, PropertyContainer pc0, PropertyContainer pc1) throws ScanException {
126         // may throw IllegalArgumentException or ScanException
127         return NodeToStringTransformer.substituteVariable(input, pc0, pc1);
128 
129     }
130 
131     /**
132      * Try to lookup the property in the following order:
133      * <ol>
134      * <li>pc1 (the local property container, usually the {@link ch.qos.logback.core.model.processor.ModelInterpretationContext ModelInterpretationContext})</li>
135      * <li>pc2 (usually the {@link Context context})</li>
136      * <li>System properties</li>
137      * <li>Environment variables</li>
138      * </ol>
139      *
140      * @param key the property key
141      * @param pc1 the first property container to search
142      * @param pc2 the second property container to search
143      * @return the property value or null if not found
144      */
145     public static String propertyLookup(String key, PropertyContainer pc1, PropertyContainer pc2) {
146         String value = null;
147         // first try the props passed as parameter
148         value = pc1.getProperty(key);
149 
150         // then try the pc2
151         if (value == null && pc2 != null) {
152             value = pc2.getProperty(key);
153         }
154         // then try in System properties
155         if (value == null) {
156             value = getSystemProperty(key, null);
157         }
158         if (value == null) {
159             value = getEnv(key);
160         }
161         return value;
162     }
163 
164     /**
165      * Very similar to <code>System.getProperty</code> except that the
166      * {@link SecurityException} is absorbed.
167      *
168      * @param key The key to search for.
169      * @param def The default value to return.
170      * @return the string value of the system property, or the default value if
171      *         there is no property with that key.
172      */
173     public static String getSystemProperty(String key, String def) {
174         try {
175             return System.getProperty(key, def);
176         } catch (SecurityException e) {
177             return def;
178         }
179     }
180 
181     /**
182      * Lookup a key from the environment.
183      *
184      * @param key
185      * @return value corresponding to key from the OS environment
186      */
187     public static String getEnv(String key) {
188         try {
189             return System.getenv(key);
190         } catch (SecurityException e) {
191             return null;
192         }
193     }
194 
195     /**
196      * Very similar to <code>System.getProperty</code> except that the
197      * {@link SecurityException} is absorbed.
198      *
199      * @param key The key to search for.
200      * @return the string value of the system property.
201      */
202     public static String getSystemProperty(String key) {
203         try {
204             return System.getProperty(key);
205         } catch (SecurityException e) {
206             return null;
207         }
208     }
209 
210     public static void setSystemProperties(ContextAware contextAware, Properties props) {
211         for (Object o : props.keySet()) {
212             String key = (String) o;
213             String value = props.getProperty(key);
214             setSystemProperty(contextAware, key, value);
215         }
216     }
217 
218     public static void setSystemProperty(ContextAware contextAware, String key, String value) {
219         try {
220             System.setProperty(key, value);
221         } catch (SecurityException e) {
222             contextAware.addError("Failed to set system property [" + key + "]", e);
223         }
224     }
225 
226     /**
227      * Very similar to {@link System#getProperties()} except that the
228      * {@link SecurityException} is absorbed.
229      *
230      * @return the system properties
231      */
232     public static Properties getSystemProperties() {
233         try {
234             return System.getProperties();
235         } catch (SecurityException e) {
236             return new Properties();
237         }
238     }
239 
240     /**
241      * Return a String[] of size two. The first item containing the key part and the
242      * second item containing a default value specified by the user. The second item
243      * will be null if no default value is specified.
244      *
245      * @param key
246      * @return
247      */
248     static public String[] extractDefaultReplacement(String key) {
249         String[] result = new String[2];
250         if (key == null)
251             return result;
252 
253         result[0] = key;
254         int d = key.indexOf(DELIM_DEFAULT);
255         if (d != -1) {
256             result[0] = key.substring(0, d);
257             result[1] = key.substring(d + DELIM_DEFAULT_LEN);
258         }
259         return result;
260     }
261 
262 
263     /**
264      * Converts a string representation of a boolean value to a {@link Boolean} object.
265      * If the input string is "true" (case-insensitive), returns {@code Boolean.TRUE}.
266      * If the input string is "false" (case-insensitive), returns {@code Boolean.FALSE}.
267      * Returns {@code null} if the input string is null, empty, or does not match "true" or "false".
268      *
269      * @param value the string representation of a boolean value, may be null or empty
270      * @return a {@link Boolean} object corresponding to the input string, or {@code null} if the input is invalid
271      */
272     public static Boolean toBooleanObject(String value) {
273         if (OptionHelper.isNullOrEmptyOrAllSpaces(value)) {
274             return null;
275         }
276 
277         String trimmedVal = value.trim();
278 
279         if ("true".equalsIgnoreCase(trimmedVal)) {
280             return true;
281         }
282 
283         if ("false".equalsIgnoreCase(trimmedVal)) {
284             return false;
285         }
286         return null;
287     }
288 
289     /**
290      * <p>If <code>value</code> is "true", then <code>true</code> is returned. If
291      * <code>value</code> is "false", then <code>true</code> is returned. Otherwise,
292      * <code>defaultVal</code> is returned.
293      * </p>
294      *
295      * <p>Comparisons are case-insensitive.</p>
296      *
297      * @param value a string representation of a boolean value
298      * @param defaultVal the default value to return if <code>value</code> is null, empty or not a valid boolean value
299      */
300     public static boolean toBoolean(String value, boolean defaultVal) {
301         if (value == null) {
302             return defaultVal;
303         }
304 
305         String trimmedVal = value.trim();
306 
307         if ("true".equalsIgnoreCase(trimmedVal)) {
308             return true;
309         }
310 
311         if ("false".equalsIgnoreCase(trimmedVal)) {
312             return false;
313         }
314 
315         return defaultVal;
316     }
317 
318     /**
319      * @deprecated
320      * @since 1.3
321      */
322     public static boolean isEmpty(String str) {
323         return isNullOrEmptyOrAllSpaces(str);
324     }
325 
326     /**
327      * Returns true if input str is null or empty.
328      * 
329      * @param str
330      * @return
331      */
332     public static boolean isNullOrEmpty(String str) {
333         return ((str == null) || str.trim().length() == 0);
334     }
335 
336     /**
337      * isNullOrEmpty xisNullOrEmptyOrAllSpaces
338      * @param str
339      * @return
340      * @since  1.5.0
341      */
342     public static boolean isNullOrEmptyOrAllSpaces(String str) {
343         return ((str == null) || str.trim().length() == 0);
344     }
345 
346 
347     final public static boolean isNullOrEmpty(Object[] array) {
348         if(array == null || array.length == 0)
349                 return true;
350         else
351                 return false;
352     }
353 
354     final public static boolean isNotEmtpy(Object[] array) {
355         return !isNullOrEmpty(array);
356     }
357 }