< prev index next >

src/java.logging/share/classes/java/util/logging/Level.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -22,17 +22,24 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
 package java.util.logging;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Module;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.ResourceBundle;
+import java.util.function.Function;
+import java.util.stream.Stream;
 
 /**
  * The Level class defines a set of standard logging levels that
  * can be used to control logging output.  The logging Level objects
  * are ordered and are specified by ordered integers.  Enabling logging

@@ -265,11 +272,12 @@
     private String computeLocalizedLevelName(Locale newLocale) {
         // Resource bundle should be loaded from the defining module
         // or its defining class loader, if it's unnamed module,
         // of this Level instance that can be a custom Level subclass;
         Module module = this.getClass().getModule();
-        ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName, newLocale, module);
+        ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName,
+                newLocale, module);
 
         final String localizedName = rb.getString(name);
         final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
         if (!isDefaultBundle) return localizedName;
 

@@ -348,38 +356,38 @@
     static Level findLevel(String name) {
         if (name == null) {
             throw new NullPointerException();
         }
 
-        KnownLevel level;
+        Optional<Level> level;
 
         // Look for a known Level with the given non-localized name.
-        level = KnownLevel.findByName(name);
-        if (level != null) {
-            return level.mirroredLevel;
+        level = KnownLevel.findByName(name, KnownLevel::mirrored);
+        if (level.isPresent()) {
+            return level.get();
         }
 
         // Now, check if the given name is an integer.  If so,
         // first look for a Level with the given value and then
         // if necessary create one.
         try {
             int x = Integer.parseInt(name);
-            level = KnownLevel.findByValue(x);
-            if (level == null) {
+            level = KnownLevel.findByValue(x, KnownLevel::mirrored);
+            if (!level.isPresent()) {
                 // add new Level
                 Level levelObject = new Level(name, x);
-                level = KnownLevel.findByValue(x);
+                return KnownLevel.findByValue(x, KnownLevel::mirrored).get();
             }
-            return level.mirroredLevel;
         } catch (NumberFormatException ex) {
             // Not an integer.
             // Drop through.
         }
 
-        level = KnownLevel.findByLocalizedLevelName(name);
-        if (level != null) {
-            return level.mirroredLevel;
+        level = KnownLevel.findByLocalizedLevelName(name,
+                KnownLevel::mirrored);
+        if (level.isPresent()) {
+            return level.get();
         }
 
         return null;
     }
 

@@ -406,19 +414,17 @@
     private static final long serialVersionUID = -8176160795706313070L;
 
     // Serialization magic to prevent "doppelgangers".
     // This is a performance optimization.
     private Object readResolve() {
-        KnownLevel o = KnownLevel.matches(this);
-        if (o != null) {
-            return o.levelObject;
+        Optional<Level> level = KnownLevel.matches(this);
+        if (level.isPresent()) {
+            return level.get();
         }
-
         // Woops.  Whoever sent us this object knows
         // about a new log level.  Add it to our list.
-        Level level = new Level(this.name, this.value, this.resourceBundleName);
-        return level;
+        return new Level(this.name, this.value, this.resourceBundleName);
     }
 
     /**
      * Parse a level name string into a Level.
      * <p>

@@ -448,41 +454,41 @@
      */
     public static synchronized Level parse(String name) throws IllegalArgumentException {
         // Check that name is not null.
         name.length();
 
-        KnownLevel level;
+        Optional<Level> level;
 
         // Look for a known Level with the given non-localized name.
-        level = KnownLevel.findByName(name);
-        if (level != null) {
-            return level.levelObject;
+        level = KnownLevel.findByName(name, KnownLevel::referent);
+        if (level.isPresent()) {
+            return level.get();
         }
 
         // Now, check if the given name is an integer.  If so,
         // first look for a Level with the given value and then
         // if necessary create one.
         try {
             int x = Integer.parseInt(name);
-            level = KnownLevel.findByValue(x);
-            if (level == null) {
-                // add new Level
-                Level levelObject = new Level(name, x);
-                level = KnownLevel.findByValue(x);
+            level = KnownLevel.findByValue(x, KnownLevel::referent);
+            if (level.isPresent()) {
+                return level.get();
             }
-            return level.levelObject;
+            // add new Level.
+            Level levelObject = new Level(name, x);
+            return KnownLevel.findByValue(x, KnownLevel::referent).get();
         } catch (NumberFormatException ex) {
             // Not an integer.
             // Drop through.
         }
 
         // Finally, look for a known level with the given localized name,
         // in the current default locale.
         // This is relatively expensive, but not excessively so.
-        level = KnownLevel.findByLocalizedLevelName(name);
-        if (level != null) {
-            return level.levelObject;
+        level = KnownLevel.findByLocalizedLevelName(name, KnownLevel::referent);
+        if (level .isPresent()) {
+            return level.get();
         }
 
         // OK, we've tried everything and failed
         throw new IllegalArgumentException("Bad level \"" + name + "\"");
     }

@@ -528,26 +534,55 @@
     //
     // Implementation Notes:
     // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
     // were final, the following KnownLevel implementation can be removed.
     // Future API change should take this into consideration.
-    static final class KnownLevel {
+    static final class KnownLevel extends WeakReference<Level> {
         private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
         private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
-        final Level levelObject;     // instance of Level class or Level subclass
+        private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>();
+
         final Level mirroredLevel;   // mirror of the custom Level
         KnownLevel(Level l) {
-            this.levelObject = l;
+            super(l, QUEUE);
             if (l.getClass() == Level.class) {
                 this.mirroredLevel = l;
             } else {
                 // this mirrored level object is hidden
-                this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName, false);
+                this.mirroredLevel = new Level(l.name, l.value,
+                        l.resourceBundleName, false);
+            }
+        }
+
+        Stream<Level> mirrored() {
+            return Stream.of(mirroredLevel);
+        }
+
+        Stream<Level> referent() {
+            final Level ref = get();
+            return ref == null ? Stream.empty() : Stream.of(ref);
+        }
+
+        private void remove() {
+            Optional.ofNullable(nameToLevels.get(mirroredLevel.name))
+                    .ifPresent((x) -> x.remove(this));
+            Optional.ofNullable(intToLevels.get(mirroredLevel.value))
+                    .ifPresent((x) -> x.remove(this));
+        }
+
+        // Remove all stale KnownLevel instances
+        static synchronized void purge() {
+            Reference<? extends Level> ref;
+            while ((ref = QUEUE.poll()) != null) {
+                if (ref instanceof KnownLevel) {
+                    ((KnownLevel)ref).remove();
+                }
             }
         }
 
         static synchronized void add(Level l) {
+            purge();
             // the mirroredLevel object is always added to the list
             // before the custom Level instance
             KnownLevel o = new KnownLevel(l);
             List<KnownLevel> list = nameToLevels.get(l.name);
             if (list == null) {

@@ -563,57 +598,61 @@
             }
             list.add(o);
         }
 
         // Returns a KnownLevel with the given non-localized name.
-        static synchronized KnownLevel findByName(String name) {
-            List<KnownLevel> list = nameToLevels.get(name);
-            if (list != null) {
-                return list.get(0);
-            }
-            return null;
+        static synchronized Optional<Level> findByName(String name,
+                Function<KnownLevel, Stream<Level>> selector) {
+            purge();
+            return nameToLevels.getOrDefault(name, Collections.emptyList())
+                        .stream()
+                        .flatMap(selector)
+                        .findFirst();
         }
 
         // Returns a KnownLevel with the given value.
-        static synchronized KnownLevel findByValue(int value) {
-            List<KnownLevel> list = intToLevels.get(value);
-            if (list != null) {
-                return list.get(0);
-            }
-            return null;
+        static synchronized Optional<Level> findByValue(int value,
+                Function<KnownLevel, Stream<Level>> selector) {
+            purge();
+            return intToLevels.getOrDefault(value, Collections.emptyList())
+                        .stream()
+                        .flatMap(selector)
+                        .findFirst();
         }
 
         // Returns a KnownLevel with the given localized name matching
         // by calling the Level.getLocalizedLevelName() method (i.e. found
         // from the resourceBundle associated with the Level object).
         // This method does not call Level.getLocalizedName() that may
         // be overridden in a subclass implementation
-        static synchronized KnownLevel findByLocalizedLevelName(String name) {
-            for (List<KnownLevel> levels : nameToLevels.values()) {
-                for (KnownLevel l : levels) {
-                    String lname = l.levelObject.getLocalizedLevelName();
-                    if (name.equals(lname)) {
-                        return l;
-                    }
-                }
-            }
-            return null;
+        static synchronized Optional<Level> findByLocalizedLevelName(String name,
+                Function<KnownLevel, Stream<Level>> selector) {
+            purge();
+            return nameToLevels.values()
+                    .stream()
+                    .flatMap(List::stream)
+                    .flatMap(selector)
+                    .filter(lo -> name.equals(lo.getLocalizedLevelName()))
+                    .findFirst();
         }
 
-        static synchronized KnownLevel matches(Level l) {
+        static synchronized Optional<Level> matches(Level l) {
+            purge();
             List<KnownLevel> list = nameToLevels.get(l.name);
             if (list != null) {
-                for (KnownLevel level : list) {
-                    Level other = level.mirroredLevel;
+                for (KnownLevel ref : list) {
+                    Level levelObject = ref.get();
+                    if (levelObject == null) continue;
+                    Level other = ref.mirroredLevel;
                     if (l.value == other.value &&
                            (l.resourceBundleName == other.resourceBundleName ||
                                (l.resourceBundleName != null &&
                                 l.resourceBundleName.equals(other.resourceBundleName)))) {
-                        return level;
+                        return Optional.of(levelObject);
                     }
                 }
             }
-            return null;
+            return Optional.empty();
         }
     }
 
 }
< prev index next >