1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.util.logging; 19 20 import dalvik.system.VMStack; 21 import java.io.IOException; 22 import java.io.ObjectInputStream; 23 import java.io.Serializable; 24 import java.util.ArrayList; 25 import java.util.List; 26 import java.util.Locale; 27 import java.util.MissingResourceException; 28 import java.util.ResourceBundle; 29 import libcore.util.Objects; 30 31 /** 32 * {@code Level} objects are used to indicate the level of logging. There are a 33 * set of predefined logging levels, each associated with an integer value. 34 * Enabling a certain logging level also enables all logging levels with larger 35 * values. 36 * <p> 37 * The predefined levels in ascending order are FINEST, FINER, FINE, CONFIG, 38 * INFO, WARNING, SEVERE. There are two additional predefined levels, which are 39 * ALL and OFF. ALL indicates logging all messages, and OFF indicates logging no 40 * messages. 41 */ 42 public class Level implements Serializable { 43 44 private static final long serialVersionUID = -8176160795706313070L; 45 46 private static final List<Level> levels = new ArrayList<Level>(9); 47 48 /** 49 * The OFF level provides no logging messages. 50 */ 51 public static final Level OFF = new Level("OFF", Integer.MAX_VALUE); 52 53 /** 54 * The SEVERE level provides severe failure messages. 55 */ 56 public static final Level SEVERE = new Level("SEVERE", 1000); 57 58 /** 59 * The WARNING level provides warnings. 60 */ 61 public static final Level WARNING = new Level("WARNING", 900); 62 63 /** 64 * The INFO level provides informative messages. 65 */ 66 public static final Level INFO = new Level("INFO", 800); 67 68 /** 69 * The CONFIG level provides static configuration messages. 70 */ 71 public static final Level CONFIG = new Level("CONFIG", 700); 72 73 /** 74 * The FINE level provides tracing messages. 75 */ 76 public static final Level FINE = new Level("FINE", 500); 77 78 /** 79 * The FINER level provides more detailed tracing messages. 80 */ 81 public static final Level FINER = new Level("FINER", 400); 82 83 /** 84 * The FINEST level provides highly detailed tracing messages. 85 */ 86 public static final Level FINEST = new Level("FINEST", 300); 87 88 /** 89 * The ALL level provides all logging messages. 90 */ 91 public static final Level ALL = new Level("ALL", Integer.MIN_VALUE); 92 93 /** 94 * Parses a level name into a {@code Level} object. 95 * 96 * @param name 97 * the name of the desired {@code level}, which cannot be 98 * {@code null}. 99 * @return the level with the specified name. 100 * @throws NullPointerException 101 * if {@code name} is {@code null}. 102 * @throws IllegalArgumentException 103 * if {@code name} is not valid. 104 */ 105 public static Level parse(String name) throws IllegalArgumentException { 106 if (name == null) { 107 throw new NullPointerException("name == null"); 108 } 109 110 boolean isNameAnInt; 111 int nameAsInt; 112 try { 113 nameAsInt = Integer.parseInt(name); 114 isNameAnInt = true; 115 } catch (NumberFormatException e) { 116 nameAsInt = 0; 117 isNameAnInt = false; 118 } 119 120 synchronized (levels) { 121 for (Level level : levels) { 122 if (name.equals(level.getName())) { 123 return level; 124 } 125 } 126 127 if (isNameAnInt) { 128 /* 129 * Loop through levels a second time, so that the returned 130 * instance will be passed on the order of construction. 131 */ 132 for (Level level : levels) { 133 if (nameAsInt == level.intValue()) { 134 return level; 135 } 136 } 137 } 138 } 139 140 if (!isNameAnInt) { 141 throw new IllegalArgumentException("Cannot parse name '" + name + "'"); 142 } 143 144 return new Level(name, nameAsInt); 145 } 146 147 /** 148 * The name of this Level. 149 * 150 * @serial 151 */ 152 private final String name; 153 154 /** 155 * The integer value indicating the level. 156 * 157 * @serial 158 */ 159 private final int value; 160 161 /** 162 * The name of the resource bundle used to localize the level name. 163 * 164 * @serial 165 */ 166 private final String resourceBundleName; 167 168 /** 169 * The resource bundle associated with this level, used to localize the 170 * level name. 171 */ 172 private transient ResourceBundle rb; 173 174 /** 175 * Constructs an instance of {@code Level} taking the supplied name and 176 * level value. 177 * 178 * @param name 179 * the name of the level. 180 * @param level 181 * an integer value indicating the level. 182 * @throws NullPointerException 183 * if {@code name} is {@code null}. 184 */ 185 protected Level(String name, int level) { 186 this(name, level, null); 187 } 188 189 /** 190 * Constructs an instance of {@code Level} taking the supplied name, level 191 * value and resource bundle name. 192 * 193 * @param name 194 * the name of the level. 195 * @param level 196 * an integer value indicating the level. 197 * @param resourceBundleName 198 * the name of the resource bundle to use. 199 * @throws NullPointerException 200 * if {@code name} is {@code null}. 201 */ 202 protected Level(String name, int level, String resourceBundleName) { 203 if (name == null) { 204 throw new NullPointerException("name == null"); 205 } 206 this.name = name; 207 this.value = level; 208 this.resourceBundleName = resourceBundleName; 209 if (resourceBundleName != null) { 210 try { 211 rb = ResourceBundle.getBundle(resourceBundleName, 212 Locale.getDefault(), VMStack.getCallingClassLoader()); 213 } catch (MissingResourceException e) { 214 rb = null; 215 } 216 } 217 synchronized (levels) { 218 levels.add(this); 219 } 220 } 221 222 /** 223 * Gets the name of this level. 224 * 225 * @return this level's name. 226 */ 227 public String getName() { 228 return this.name; 229 } 230 231 /** 232 * Gets the name of the resource bundle associated with this level. 233 * 234 * @return the name of this level's resource bundle. 235 */ 236 public String getResourceBundleName() { 237 return this.resourceBundleName; 238 } 239 240 /** 241 * Gets the integer value indicating this level. 242 * 243 * @return this level's integer value. 244 */ 245 public final int intValue() { 246 return this.value; 247 } 248 249 /** 250 * Serialization helper method to maintain singletons and add any new 251 * levels. 252 * 253 * @return the resolved instance. 254 */ 255 private Object readResolve() { 256 synchronized (levels) { 257 for (Level level : levels) { 258 if (value != level.value) { 259 continue; 260 } 261 if (!name.equals(level.name)) { 262 continue; 263 } 264 if (Objects.equal(resourceBundleName, level.resourceBundleName)) { 265 return level; 266 } 267 } 268 // This is a new value, so add it. 269 levels.add(this); 270 return this; 271 } 272 } 273 274 /** 275 * Serialization helper to setup transient resource bundle instance. 276 * 277 * @param in 278 * the input stream to read the instance data from. 279 * @throws IOException 280 * if an IO error occurs. 281 * @throws ClassNotFoundException 282 * if a class is not found. 283 */ 284 private void readObject(ObjectInputStream in) throws IOException, 285 ClassNotFoundException { 286 in.defaultReadObject(); 287 if (resourceBundleName != null) { 288 try { 289 rb = ResourceBundle.getBundle(resourceBundleName); 290 } catch (MissingResourceException e) { 291 rb = null; 292 } 293 } 294 } 295 296 /** 297 * Gets the localized name of this level. The default locale is used. If no 298 * resource bundle is associated with this level then the original level 299 * name is returned. 300 * 301 * @return the localized name of this level. 302 */ 303 public String getLocalizedName() { 304 if (rb == null) { 305 return name; 306 } 307 308 try { 309 return rb.getString(name); 310 } catch (MissingResourceException e) { 311 return name; 312 } 313 } 314 315 /** 316 * Compares two {@code Level} objects for equality. They are considered to 317 * be equal if they have the same level value. 318 * 319 * @param o 320 * the other object to compare this level to. 321 * @return {@code true} if this object equals to the supplied object, 322 * {@code false} otherwise. 323 */ 324 @Override 325 public boolean equals(Object o) { 326 if (this == o) { 327 return true; 328 } 329 330 if (!(o instanceof Level)) { 331 return false; 332 } 333 334 return ((Level) o).intValue() == this.value; 335 } 336 337 /** 338 * Returns the hash code of this {@code Level} object. 339 * 340 * @return this level's hash code. 341 */ 342 @Override 343 public int hashCode() { 344 return this.value; 345 } 346 347 /** 348 * Returns the string representation of this {@code Level} object. In 349 * this case, it is the level's name. 350 * 351 * @return the string representation of this level. 352 */ 353 @Override 354 public final String toString() { 355 return this.name; 356 } 357 } 358