1 /*
2  * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.util.logging;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.ResourceBundle;
33 
34 /**
35  * The Level class defines a set of standard logging levels that
36  * can be used to control logging output.  The logging Level objects
37  * are ordered and are specified by ordered integers.  Enabling logging
38  * at a given level also enables logging at all higher levels.
39  * <p>
40  * Clients should normally use the predefined Level constants such
41  * as Level.SEVERE.
42  * <p>
43  * The levels in descending order are:
44  * <ul>
45  * <li>SEVERE (highest value)
46  * <li>WARNING
47  * <li>INFO
48  * <li>CONFIG
49  * <li>FINE
50  * <li>FINER
51  * <li>FINEST  (lowest value)
52  * </ul>
53  * In addition there is a level OFF that can be used to turn
54  * off logging, and a level ALL that can be used to enable
55  * logging of all messages.
56  * <p>
57  * It is possible for third parties to define additional logging
58  * levels by subclassing Level.  In such cases subclasses should
59  * take care to chose unique integer level values and to ensure that
60  * they maintain the Object uniqueness property across serialization
61  * by defining a suitable readResolve method.
62  *
63  * @since 1.4
64  */
65 
66 public class Level implements java.io.Serializable {
67     private static final String defaultBundle = "sun.util.logging.resources.logging";
68 
69     /**
70      * @serial  The non-localized name of the level.
71      */
72     private final String name;
73 
74     /**
75      * @serial  The integer value of the level.
76      */
77     private final int value;
78 
79     /**
80      * @serial The resource bundle name to be used in localizing the level name.
81      */
82     private final String resourceBundleName;
83 
84     // localized level name
85     private transient String localizedLevelName;
86     private transient Locale cachedLocale;
87 
88     /**
89      * OFF is a special level that can be used to turn off logging.
90      * This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.
91      */
92     public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);
93 
94     /**
95      * SEVERE is a message level indicating a serious failure.
96      * <p>
97      * In general SEVERE messages should describe events that are
98      * of considerable importance and which will prevent normal
99      * program execution.   They should be reasonably intelligible
100      * to end users and to system administrators.
101      * This level is initialized to <CODE>1000</CODE>.
102      */
103     public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
104 
105     /**
106      * WARNING is a message level indicating a potential problem.
107      * <p>
108      * In general WARNING messages should describe events that will
109      * be of interest to end users or system managers, or which
110      * indicate potential problems.
111      * This level is initialized to <CODE>900</CODE>.
112      */
113     public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
114 
115     /**
116      * INFO is a message level for informational messages.
117      * <p>
118      * Typically INFO messages will be written to the console
119      * or its equivalent.  So the INFO level should only be
120      * used for reasonably significant messages that will
121      * make sense to end users and system administrators.
122      * This level is initialized to <CODE>800</CODE>.
123      */
124     public static final Level INFO = new Level("INFO", 800, defaultBundle);
125 
126     /**
127      * CONFIG is a message level for static configuration messages.
128      * <p>
129      * CONFIG messages are intended to provide a variety of static
130      * configuration information, to assist in debugging problems
131      * that may be associated with particular configurations.
132      * For example, CONFIG message might include the CPU type,
133      * the graphics depth, the GUI look-and-feel, etc.
134      * This level is initialized to <CODE>700</CODE>.
135      */
136     public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
137 
138     /**
139      * FINE is a message level providing tracing information.
140      * <p>
141      * All of FINE, FINER, and FINEST are intended for relatively
142      * detailed tracing.  The exact meaning of the three levels will
143      * vary between subsystems, but in general, FINEST should be used
144      * for the most voluminous detailed output, FINER for somewhat
145      * less detailed output, and FINE for the  lowest volume (and
146      * most important) messages.
147      * <p>
148      * In general the FINE level should be used for information
149      * that will be broadly interesting to developers who do not have
150      * a specialized interest in the specific subsystem.
151      * <p>
152      * FINE messages might include things like minor (recoverable)
153      * failures.  Issues indicating potential performance problems
154      * are also worth logging as FINE.
155      * This level is initialized to <CODE>500</CODE>.
156      */
157     public static final Level FINE = new Level("FINE", 500, defaultBundle);
158 
159     /**
160      * FINER indicates a fairly detailed tracing message.
161      * By default logging calls for entering, returning, or throwing
162      * an exception are traced at this level.
163      * This level is initialized to <CODE>400</CODE>.
164      */
165     public static final Level FINER = new Level("FINER", 400, defaultBundle);
166 
167     /**
168      * FINEST indicates a highly detailed tracing message.
169      * This level is initialized to <CODE>300</CODE>.
170      */
171     public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
172 
173     /**
174      * ALL indicates that all messages should be logged.
175      * This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.
176      */
177     public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
178 
179     /**
180      * Create a named Level with a given integer value.
181      * <p>
182      * Note that this constructor is "protected" to allow subclassing.
183      * In general clients of logging should use one of the constant Level
184      * objects such as SEVERE or FINEST.  However, if clients need to
185      * add new logging levels, they may subclass Level and define new
186      * constants.
187      * @param name  the name of the Level, for example "SEVERE".
188      * @param value an integer value for the level.
189      * @throws NullPointerException if the name is null
190      */
Level(String name, int value)191     protected Level(String name, int value) {
192         this(name, value, null);
193     }
194 
195     /**
196      * Create a named Level with a given integer value and a
197      * given localization resource name.
198      * <p>
199      * @param name  the name of the Level, for example "SEVERE".
200      * @param value an integer value for the level.
201      * @param resourceBundleName name of a resource bundle to use in
202      *    localizing the given name. If the resourceBundleName is null
203      *    or an empty string, it is ignored.
204      * @throws NullPointerException if the name is null
205      */
Level(String name, int value, String resourceBundleName)206     protected Level(String name, int value, String resourceBundleName) {
207         this(name, value, resourceBundleName, true);
208     }
209 
210     // private constructor to specify whether this instance should be added
211     // to the KnownLevel list from which Level.parse method does its look up
Level(String name, int value, String resourceBundleName, boolean visible)212     private Level(String name, int value, String resourceBundleName, boolean visible) {
213         if (name == null) {
214             throw new NullPointerException();
215         }
216         this.name = name;
217         this.value = value;
218         this.resourceBundleName = resourceBundleName;
219         this.localizedLevelName = resourceBundleName == null ? name : null;
220         this.cachedLocale = null;
221         if (visible) {
222             KnownLevel.add(this);
223         }
224     }
225 
226     /**
227      * Return the level's localization resource bundle name, or
228      * null if no localization bundle is defined.
229      *
230      * @return localization resource bundle name
231      */
getResourceBundleName()232     public String getResourceBundleName() {
233         return resourceBundleName;
234     }
235 
236     /**
237      * Return the non-localized string name of the Level.
238      *
239      * @return non-localized name
240      */
getName()241     public String getName() {
242         return name;
243     }
244 
245     /**
246      * Return the localized string name of the Level, for
247      * the current default locale.
248      * <p>
249      * If no localization information is available, the
250      * non-localized name is returned.
251      *
252      * @return localized name
253      */
getLocalizedName()254     public String getLocalizedName() {
255         return getLocalizedLevelName();
256     }
257 
258     // package-private getLevelName() is used by the implementation
259     // instead of getName() to avoid calling the subclass's version
getLevelName()260     final String getLevelName() {
261         return this.name;
262     }
263 
computeLocalizedLevelName(Locale newLocale)264     private String computeLocalizedLevelName(Locale newLocale) {
265         // Android-changed: Use Thread.currentThread().getContextClassLoader().
266         // Otherwise, we might get a BootClassLoader.
267         // ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName, newLocale);
268         ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName, newLocale,
269                                                      Thread.currentThread().getContextClassLoader());
270         final String localizedName = rb.getString(name);
271 
272         final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
273         if (!isDefaultBundle) return localizedName;
274 
275         // This is a trick to determine whether the name has been translated
276         // or not. If it has not been translated, we need to use Locale.ROOT
277         // when calling toUpperCase().
278         final Locale rbLocale = rb.getLocale();
279         final Locale locale =
280                 Locale.ROOT.equals(rbLocale)
281                 || name.equals(localizedName.toUpperCase(Locale.ROOT))
282                 ? Locale.ROOT : rbLocale;
283 
284         // ALL CAPS in a resource bundle's message indicates no translation
285         // needed per Oracle translation guideline.  To workaround this
286         // in Oracle JDK implementation, convert the localized level name
287         // to uppercase for compatibility reason.
288         return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale);
289     }
290 
291     // Avoid looking up the localizedLevelName twice if we already
292     // have it.
getCachedLocalizedLevelName()293     final String getCachedLocalizedLevelName() {
294 
295         if (localizedLevelName != null) {
296             if (cachedLocale != null) {
297                 if (cachedLocale.equals(Locale.getDefault())) {
298                     // OK: our cached value was looked up with the same
299                     //     locale. We can use it.
300                     return localizedLevelName;
301                 }
302             }
303         }
304 
305         if (resourceBundleName == null) {
306             // No resource bundle: just use the name.
307             return name;
308         }
309 
310         // We need to compute the localized name.
311         // Either because it's the first time, or because our cached
312         // value is for a different locale. Just return null.
313         return null;
314     }
315 
getLocalizedLevelName()316     final synchronized String getLocalizedLevelName() {
317 
318         // See if we have a cached localized name
319         final String cachedLocalizedName = getCachedLocalizedLevelName();
320         if (cachedLocalizedName != null) {
321             return cachedLocalizedName;
322         }
323 
324         // No cached localized name or cache invalid.
325         // Need to compute the localized name.
326         final Locale newLocale = Locale.getDefault();
327         try {
328             localizedLevelName = computeLocalizedLevelName(newLocale);
329         } catch (Exception ex) {
330             localizedLevelName = name;
331         }
332         cachedLocale = newLocale;
333         return localizedLevelName;
334     }
335 
336     // Returns a mirrored Level object that matches the given name as
337     // specified in the Level.parse method.  Returns null if not found.
338     //
339     // It returns the same Level object as the one returned by Level.parse
340     // method if the given name is a non-localized name or integer.
341     //
342     // If the name is a localized name, findLevel and parse method may
343     // return a different level value if there is a custom Level subclass
344     // that overrides Level.getLocalizedName() to return a different string
345     // than what's returned by the default implementation.
346     //
findLevel(String name)347     static Level findLevel(String name) {
348         if (name == null) {
349             throw new NullPointerException();
350         }
351 
352         KnownLevel level;
353 
354         // Look for a known Level with the given non-localized name.
355         level = KnownLevel.findByName(name);
356         if (level != null) {
357             return level.mirroredLevel;
358         }
359 
360         // Now, check if the given name is an integer.  If so,
361         // first look for a Level with the given value and then
362         // if necessary create one.
363         try {
364             int x = Integer.parseInt(name);
365             level = KnownLevel.findByValue(x);
366             if (level == null) {
367                 // add new Level
368                 Level levelObject = new Level(name, x);
369                 level = KnownLevel.findByValue(x);
370             }
371             return level.mirroredLevel;
372         } catch (NumberFormatException ex) {
373             // Not an integer.
374             // Drop through.
375         }
376 
377         level = KnownLevel.findByLocalizedLevelName(name);
378         if (level != null) {
379             return level.mirroredLevel;
380         }
381 
382         return null;
383     }
384 
385     /**
386      * Returns a string representation of this Level.
387      *
388      * @return the non-localized name of the Level, for example "INFO".
389      */
390     @Override
toString()391     public final String toString() {
392         return name;
393     }
394 
395     /**
396      * Get the integer value for this level.  This integer value
397      * can be used for efficient ordering comparisons between
398      * Level objects.
399      * @return the integer value for this level.
400      */
intValue()401     public final int intValue() {
402         return value;
403     }
404 
405     private static final long serialVersionUID = -8176160795706313070L;
406 
407     // Serialization magic to prevent "doppelgangers".
408     // This is a performance optimization.
readResolve()409     private Object readResolve() {
410         KnownLevel o = KnownLevel.matches(this);
411         if (o != null) {
412             return o.levelObject;
413         }
414 
415         // Woops.  Whoever sent us this object knows
416         // about a new log level.  Add it to our list.
417         Level level = new Level(this.name, this.value, this.resourceBundleName);
418         return level;
419     }
420 
421     /**
422      * Parse a level name string into a Level.
423      * <p>
424      * The argument string may consist of either a level name
425      * or an integer value.
426      * <p>
427      * For example:
428      * <ul>
429      * <li>     "SEVERE"
430      * <li>     "1000"
431      * </ul>
432      *
433      * @param  name   string to be parsed
434      * @throws NullPointerException if the name is null
435      * @throws IllegalArgumentException if the value is not valid.
436      * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE>
437      * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names.
438      * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>,
439      * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with
440      * appropriate package access, or new levels defined or created
441      * by subclasses.
442      *
443      * @return The parsed value. Passing an integer that corresponds to a known name
444      * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>).
445      * Passing an integer that does not (e.g., 1) will return a new level name
446      * initialized to that value.
447      */
parse(String name)448     public static synchronized Level parse(String name) throws IllegalArgumentException {
449         // Check that name is not null.
450         name.length();
451 
452         KnownLevel level;
453 
454         // Look for a known Level with the given non-localized name.
455         level = KnownLevel.findByName(name);
456         if (level != null) {
457             return level.levelObject;
458         }
459 
460         // Now, check if the given name is an integer.  If so,
461         // first look for a Level with the given value and then
462         // if necessary create one.
463         try {
464             int x = Integer.parseInt(name);
465             level = KnownLevel.findByValue(x);
466             if (level == null) {
467                 // add new Level
468                 Level levelObject = new Level(name, x);
469                 level = KnownLevel.findByValue(x);
470             }
471             return level.levelObject;
472         } catch (NumberFormatException ex) {
473             // Not an integer.
474             // Drop through.
475         }
476 
477         // Finally, look for a known level with the given localized name,
478         // in the current default locale.
479         // This is relatively expensive, but not excessively so.
480         level = KnownLevel.findByLocalizedLevelName(name);
481         if (level != null) {
482             return level.levelObject;
483         }
484 
485         // OK, we've tried everything and failed
486         throw new IllegalArgumentException("Bad level \"" + name + "\"");
487     }
488 
489     /**
490      * Compare two objects for value equality.
491      * @return true if and only if the two objects have the same level value.
492      */
493     @Override
equals(Object ox)494     public boolean equals(Object ox) {
495         try {
496             Level lx = (Level)ox;
497             return (lx.value == this.value);
498         } catch (Exception ex) {
499             return false;
500         }
501     }
502 
503     /**
504      * Generate a hashcode.
505      * @return a hashcode based on the level value
506      */
507     @Override
hashCode()508     public int hashCode() {
509         return this.value;
510     }
511 
512     // KnownLevel class maintains the global list of all known levels.
513     // The API allows multiple custom Level instances of the same name/value
514     // be created. This class provides convenient methods to find a level
515     // by a given name, by a given value, or by a given localized name.
516     //
517     // KnownLevel wraps the following Level objects:
518     // 1. levelObject:   standard Level object or custom Level object
519     // 2. mirroredLevel: Level object representing the level specified in the
520     //                   logging configuration.
521     //
522     // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
523     // are non-final but the name and resource bundle name are parameters to
524     // the Level constructor.  Use the mirroredLevel object instead of the
525     // levelObject to prevent the logging framework to execute foreign code
526     // implemented by untrusted Level subclass.
527     //
528     // Implementation Notes:
529     // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
530     // were final, the following KnownLevel implementation can be removed.
531     // Future API change should take this into consideration.
532     static final class KnownLevel {
533         private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
534         private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
535         final Level levelObject;     // instance of Level class or Level subclass
536         final Level mirroredLevel;   // mirror of the custom Level
KnownLevel(Level l)537         KnownLevel(Level l) {
538             this.levelObject = l;
539             if (l.getClass() == Level.class) {
540                 this.mirroredLevel = l;
541             } else {
542                 // this mirrored level object is hidden
543                 this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName, false);
544             }
545         }
546 
add(Level l)547         static synchronized void add(Level l) {
548             // the mirroredLevel object is always added to the list
549             // before the custom Level instance
550             KnownLevel o = new KnownLevel(l);
551             List<KnownLevel> list = nameToLevels.get(l.name);
552             if (list == null) {
553                 list = new ArrayList<>();
554                 nameToLevels.put(l.name, list);
555             }
556             list.add(o);
557 
558             list = intToLevels.get(l.value);
559             if (list == null) {
560                 list = new ArrayList<>();
561                 intToLevels.put(l.value, list);
562             }
563             list.add(o);
564         }
565 
566         // Returns a KnownLevel with the given non-localized name.
findByName(String name)567         static synchronized KnownLevel findByName(String name) {
568             List<KnownLevel> list = nameToLevels.get(name);
569             if (list != null) {
570                 return list.get(0);
571             }
572             return null;
573         }
574 
575         // Returns a KnownLevel with the given value.
findByValue(int value)576         static synchronized KnownLevel findByValue(int value) {
577             List<KnownLevel> list = intToLevels.get(value);
578             if (list != null) {
579                 return list.get(0);
580             }
581             return null;
582         }
583 
584         // Returns a KnownLevel with the given localized name matching
585         // by calling the Level.getLocalizedLevelName() method (i.e. found
586         // from the resourceBundle associated with the Level object).
587         // This method does not call Level.getLocalizedName() that may
588         // be overridden in a subclass implementation
findByLocalizedLevelName(String name)589         static synchronized KnownLevel findByLocalizedLevelName(String name) {
590             for (List<KnownLevel> levels : nameToLevels.values()) {
591                 for (KnownLevel l : levels) {
592                     String lname = l.levelObject.getLocalizedLevelName();
593                     if (name.equals(lname)) {
594                         return l;
595                     }
596                 }
597             }
598             return null;
599         }
600 
matches(Level l)601         static synchronized KnownLevel matches(Level l) {
602             List<KnownLevel> list = nameToLevels.get(l.name);
603             if (list != null) {
604                 for (KnownLevel level : list) {
605                     Level other = level.mirroredLevel;
606                     Class<? extends Level> type = level.levelObject.getClass();
607                     if (l.value == other.value &&
608                            (l.resourceBundleName == other.resourceBundleName ||
609                                (l.resourceBundleName != null &&
610                                 l.resourceBundleName.equals(other.resourceBundleName)))) {
611                         if (type == l.getClass()) {
612                             return level;
613                         }
614                     }
615                 }
616             }
617             return null;
618         }
619     }
620 
621 }
622