1 /*
   2  * Copyright (c) 2000, 2004, 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.ResourceBundle;
  28 
  29 /**
  30  * The Level class defines a set of standard logging levels that
  31  * can be used to control logging output.  The logging Level objects
  32  * are ordered and are specified by ordered integers.  Enabling logging
  33  * at a given level also enables logging at all higher levels.
  34  * <p>
  35  * Clients should normally use the predefined Level constants such
  36  * as Level.SEVERE.
  37  * <p>
  38  * The levels in descending order are:
  39  * <ul>
  40  * <li>SEVERE (highest value)
  41  * <li>WARNING
  42  * <li>INFO
  43  * <li>CONFIG
  44  * <li>FINE
  45  * <li>FINER
  46  * <li>FINEST  (lowest value)
  47  * </ul>
  48  * In addition there is a level OFF that can be used to turn
  49  * off logging, and a level ALL that can be used to enable
  50  * logging of all messages.
  51  * <p>
  52  * It is possible for third parties to define additional logging
  53  * levels by subclassing Level.  In such cases subclasses should
  54  * take care to chose unique integer level values and to ensure that
  55  * they maintain the Object uniqueness property across serialization
  56  * by defining a suitable readResolve method.
  57  *
  58  * @since 1.4
  59  */
  60 
  61 public class Level implements java.io.Serializable {
  62     private static java.util.ArrayList<Level> known = new java.util.ArrayList<>();
  63     private static String defaultBundle = "sun.util.logging.resources.logging";
  64 
  65     /**
  66      * @serial  The non-localized name of the level.
  67      */
  68     private final String name;
  69 
  70     /**
  71      * @serial  The integer value of the level.
  72      */
  73     private final int value;
  74 
  75     /**
  76      * @serial The resource bundle name to be used in localizing the level name.
  77      */
  78     private final String resourceBundleName;
  79 
  80     /**
  81      * OFF is a special level that can be used to turn off logging.
  82      * This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.
  83      */
  84     public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);
  85 
  86     /**
  87      * SEVERE is a message level indicating a serious failure.
  88      * <p>
  89      * In general SEVERE messages should describe events that are
  90      * of considerable importance and which will prevent normal
  91      * program execution.   They should be reasonably intelligible
  92      * to end users and to system administrators.
  93      * This level is initialized to <CODE>1000</CODE>.
  94      */
  95     public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
  96 
  97     /**
  98      * WARNING is a message level indicating a potential problem.
  99      * <p>
 100      * In general WARNING messages should describe events that will
 101      * be of interest to end users or system managers, or which
 102      * indicate potential problems.
 103      * This level is initialized to <CODE>900</CODE>.
 104      */
 105     public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
 106 
 107     /**
 108      * INFO is a message level for informational messages.
 109      * <p>
 110      * Typically INFO messages will be written to the console
 111      * or its equivalent.  So the INFO level should only be
 112      * used for reasonably significant messages that will
 113      * make sense to end users and system administrators.
 114      * This level is initialized to <CODE>800</CODE>.
 115      */
 116     public static final Level INFO = new Level("INFO", 800, defaultBundle);
 117 
 118     /**
 119      * CONFIG is a message level for static configuration messages.
 120      * <p>
 121      * CONFIG messages are intended to provide a variety of static
 122      * configuration information, to assist in debugging problems
 123      * that may be associated with particular configurations.
 124      * For example, CONFIG message might include the CPU type,
 125      * the graphics depth, the GUI look-and-feel, etc.
 126      * This level is initialized to <CODE>700</CODE>.
 127      */
 128     public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
 129 
 130     /**
 131      * FINE is a message level providing tracing information.
 132      * <p>
 133      * All of FINE, FINER, and FINEST are intended for relatively
 134      * detailed tracing.  The exact meaning of the three levels will
 135      * vary between subsystems, but in general, FINEST should be used
 136      * for the most voluminous detailed output, FINER for somewhat
 137      * less detailed output, and FINE for the  lowest volume (and
 138      * most important) messages.
 139      * <p>
 140      * In general the FINE level should be used for information
 141      * that will be broadly interesting to developers who do not have
 142      * a specialized interest in the specific subsystem.
 143      * <p>
 144      * FINE messages might include things like minor (recoverable)
 145      * failures.  Issues indicating potential performance problems
 146      * are also worth logging as FINE.
 147      * This level is initialized to <CODE>500</CODE>.
 148      */
 149     public static final Level FINE = new Level("FINE", 500, defaultBundle);
 150 
 151     /**
 152      * FINER indicates a fairly detailed tracing message.
 153      * By default logging calls for entering, returning, or throwing
 154      * an exception are traced at this level.
 155      * This level is initialized to <CODE>400</CODE>.
 156      */
 157     public static final Level FINER = new Level("FINER", 400, defaultBundle);
 158 
 159     /**
 160      * FINEST indicates a highly detailed tracing message.
 161      * This level is initialized to <CODE>300</CODE>.
 162      */
 163     public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
 164 
 165     /**
 166      * ALL indicates that all messages should be logged.
 167      * This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.
 168      */
 169     public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
 170 
 171     /**
 172      * Create a named Level with a given integer value.
 173      * <p>
 174      * Note that this constructor is "protected" to allow subclassing.
 175      * In general clients of logging should use one of the constant Level
 176      * objects such as SEVERE or FINEST.  However, if clients need to
 177      * add new logging levels, they may subclass Level and define new
 178      * constants.
 179      * @param name  the name of the Level, for example "SEVERE".
 180      * @param value an integer value for the level.
 181      * @throws NullPointerException if the name is null
 182      */
 183     protected Level(String name, int value) {
 184         this(name, value, null);
 185     }
 186 
 187     /**
 188      * Create a named Level with a given integer value and a
 189      * given localization resource name.
 190      * <p>
 191      * @param name  the name of the Level, for example "SEVERE".
 192      * @param value an integer value for the level.
 193      * @param resourceBundleName name of a resource bundle to use in
 194      *    localizing the given name. If the resourceBundleName is null
 195      *    or an empty string, it is ignored.
 196      * @throws NullPointerException if the name is null
 197      */
 198     protected Level(String name, int value, String resourceBundleName) {
 199         if (name == null) {
 200             throw new NullPointerException();
 201         }
 202         this.name = name;
 203         this.value = value;
 204         this.resourceBundleName = resourceBundleName;
 205         synchronized (Level.class) {
 206             known.add(this);
 207         }
 208     }
 209 
 210     /**
 211      * Return the level's localization resource bundle name, or
 212      * null if no localization bundle is defined.
 213      *
 214      * @return localization resource bundle name
 215      */
 216     public String getResourceBundleName() {
 217         return resourceBundleName;
 218     }
 219 
 220     /**
 221      * Return the non-localized string name of the Level.
 222      *
 223      * @return non-localized name
 224      */
 225     public String getName() {
 226         return name;
 227     }
 228 
 229     /**
 230      * Return the localized string name of the Level, for
 231      * the current default locale.
 232      * <p>
 233      * If no localization information is available, the
 234      * non-localized name is returned.
 235      *
 236      * @return localized name
 237      */
 238     public String getLocalizedName() {
 239         try {
 240             ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName);
 241             return rb.getString(name);
 242         } catch (Exception ex) {
 243             return name;
 244         }
 245     }
 246 
 247     /**
 248      * Returns a string representation of this Level.
 249      *
 250      * @return the non-localized name of the Level, for example "INFO".
 251      */
 252     public final String toString() {
 253         return name;
 254     }
 255 
 256     /**
 257      * Get the integer value for this level.  This integer value
 258      * can be used for efficient ordering comparisons between
 259      * Level objects.
 260      * @return the integer value for this level.
 261      */
 262     public final int intValue() {
 263         return value;
 264     }
 265 
 266     private static final long serialVersionUID = -8176160795706313070L;
 267 
 268     // Serialization magic to prevent "doppelgangers".
 269     // This is a performance optimization.
 270     private Object readResolve() {
 271         synchronized (Level.class) {
 272             for (int i = 0; i < known.size(); i++) {
 273                 Level other = known.get(i);
 274                 if (this.name.equals(other.name) && this.value == other.value
 275                         && (this.resourceBundleName == other.resourceBundleName ||
 276                             (this.resourceBundleName != null &&
 277                             this.resourceBundleName.equals(other.resourceBundleName)))) {
 278                     return other;
 279                 }
 280             }
 281             // Woops.  Whoever sent us this object knows
 282             // about a new log level.  Add it to our list.
 283             known.add(this);
 284             return this;
 285         }
 286     }
 287 
 288     /**
 289      * Parse a level name string into a Level.
 290      * <p>
 291      * The argument string may consist of either a level name
 292      * or an integer value.
 293      * <p>
 294      * For example:
 295      * <ul>
 296      * <li>     "SEVERE"
 297      * <li>     "1000"
 298      * </ul>
 299      * @param  name   string to be parsed
 300      * @throws NullPointerException if the name is null
 301      * @throws IllegalArgumentException if the value is not valid.
 302      * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE>
 303      * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names.
 304      * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>,
 305      * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with
 306      * appropriate package access, or new levels defined or created
 307      * by subclasses.
 308      *
 309      * @return The parsed value. Passing an integer that corresponds to a known name
 310      * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>).
 311      * Passing an integer that does not (e.g., 1) will return a new level name
 312      * initialized to that value.
 313      */
 314     public static synchronized Level parse(String name) throws IllegalArgumentException {
 315         // Check that name is not null.
 316         name.length();
 317 
 318         // Look for a known Level with the given non-localized name.
 319         for (int i = 0; i < known.size(); i++) {
 320             Level l = known.get(i);
 321             if (name.equals(l.name)) {
 322                 return l;
 323             }
 324         }
 325 
 326         // Now, check if the given name is an integer.  If so,
 327         // first look for a Level with the given value and then
 328         // if necessary create one.
 329         try {
 330             int x = Integer.parseInt(name);
 331             for (int i = 0; i < known.size(); i++) {
 332                 Level l = known.get(i);
 333                 if (l.value == x) {
 334                     return l;
 335                 }
 336             }
 337             // Create a new Level.
 338             return new Level(name, x);
 339         } catch (NumberFormatException ex) {
 340             // Not an integer.
 341             // Drop through.
 342         }
 343 
 344         // Finally, look for a known level with the given localized name,
 345         // in the current default locale.
 346         // This is relatively expensive, but not excessively so.
 347         for (int i = 0; i < known.size(); i++) {
 348             Level l =  known.get(i);
 349             if (name.equals(l.getLocalizedName())) {
 350                 return l;
 351             }
 352         }
 353 
 354         // OK, we've tried everything and failed
 355         throw new IllegalArgumentException("Bad level \"" + name + "\"");
 356     }
 357 
 358     /**
 359      * Compare two objects for value equality.
 360      * @return true if and only if the two objects have the same level value.
 361      */
 362     public boolean equals(Object ox) {
 363         try {
 364             Level lx = (Level)ox;
 365             return (lx.value == this.value);
 366         } catch (Exception ex) {
 367             return false;
 368         }
 369     }
 370 
 371     /**
 372      * Generate a hashcode.
 373      * @return a hashcode based on the level value
 374      */
 375     public int hashCode() {
 376         return this.value;
 377     }
 378 }