From eb71fb65331b1f8a139dacdccfbb5acf667eb39c Mon Sep 17 00:00:00 2001 From: JFronny Date: Wed, 15 Jun 2022 14:49:57 +0200 Subject: [PATCH] [main] Rewrite logger to support hot-swapping strategies, utils for weak references --- ...SLF4JLogger.java => SLF4JLogStrategy.java} | 4 +- ...oggerTest.java => SLFLogStrategyTest.java} | 9 +- .../commons/log/DelegateLogStrategy.java | 139 +++++++++ ...ilLogger.java => JavaUtilLogStrategy.java} | 4 +- .../jfronny/commons/log/LogStrategy.java | 92 ++++++ .../io/gitlab/jfronny/commons/log/Logger.java | 134 +++------ .../jfronny/commons/log/LoggerHolder.java | 25 -- .../{NopLogger.java => NopLogStrategy.java} | 2 +- ...doutLogger.java => StdoutLogStrategy.java} | 4 +- .../gitlab/jfronny/commons/ref/WeakSet.java | 124 ++++++++ .../jfronny/commons/ref/WeakValueMap.java | 264 ++++++++++++++++++ .../jfronny/commons/test/LoggerTest.java | 10 +- 12 files changed, 678 insertions(+), 133 deletions(-) rename commons-slf4j/src/main/java/io/gitlab/jfronny/commons/log/{SLF4JLogger.java => SLF4JLogStrategy.java} (95%) rename commons-slf4j/src/test/java/io/gitlab/jfronny/commons/test/{SLFLoggerTest.java => SLFLogStrategyTest.java} (53%) create mode 100644 src/main/java/io/gitlab/jfronny/commons/log/DelegateLogStrategy.java rename src/main/java/io/gitlab/jfronny/commons/log/{JavaUtilLogger.java => JavaUtilLogStrategy.java} (96%) create mode 100644 src/main/java/io/gitlab/jfronny/commons/log/LogStrategy.java delete mode 100644 src/main/java/io/gitlab/jfronny/commons/log/LoggerHolder.java rename src/main/java/io/gitlab/jfronny/commons/log/{NopLogger.java => NopLogStrategy.java} (76%) rename src/main/java/io/gitlab/jfronny/commons/log/{StdoutLogger.java => StdoutLogStrategy.java} (81%) create mode 100644 src/main/java/io/gitlab/jfronny/commons/ref/WeakSet.java create mode 100644 src/main/java/io/gitlab/jfronny/commons/ref/WeakValueMap.java diff --git a/commons-slf4j/src/main/java/io/gitlab/jfronny/commons/log/SLF4JLogger.java b/commons-slf4j/src/main/java/io/gitlab/jfronny/commons/log/SLF4JLogStrategy.java similarity index 95% rename from commons-slf4j/src/main/java/io/gitlab/jfronny/commons/log/SLF4JLogger.java rename to commons-slf4j/src/main/java/io/gitlab/jfronny/commons/log/SLF4JLogStrategy.java index f6d8d4c..02ec978 100644 --- a/commons-slf4j/src/main/java/io/gitlab/jfronny/commons/log/SLF4JLogger.java +++ b/commons-slf4j/src/main/java/io/gitlab/jfronny/commons/log/SLF4JLogStrategy.java @@ -2,10 +2,10 @@ package io.gitlab.jfronny.commons.log; import org.slf4j.LoggerFactory; -public class SLF4JLogger implements Logger { +public class SLF4JLogStrategy implements LogStrategy { private final org.slf4j.Logger logger; - public SLF4JLogger(String name) { + public SLF4JLogStrategy(String name) { this.logger = LoggerFactory.getLogger(name); } diff --git a/commons-slf4j/src/test/java/io/gitlab/jfronny/commons/test/SLFLoggerTest.java b/commons-slf4j/src/test/java/io/gitlab/jfronny/commons/test/SLFLogStrategyTest.java similarity index 53% rename from commons-slf4j/src/test/java/io/gitlab/jfronny/commons/test/SLFLoggerTest.java rename to commons-slf4j/src/test/java/io/gitlab/jfronny/commons/test/SLFLogStrategyTest.java index 75b6e8f..573182b 100644 --- a/commons-slf4j/src/test/java/io/gitlab/jfronny/commons/test/SLFLoggerTest.java +++ b/commons-slf4j/src/test/java/io/gitlab/jfronny/commons/test/SLFLogStrategyTest.java @@ -1,20 +1,19 @@ package io.gitlab.jfronny.commons.test; -import io.gitlab.jfronny.commons.log.Logger; -import io.gitlab.jfronny.commons.log.SLF4JLogger; +import io.gitlab.jfronny.commons.log.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -public class SLFLoggerTest { +public class SLFLogStrategyTest { @BeforeEach void prepare() { - Logger.resetFactory(); + Logger.resetStrategy(); } @Test void testFactory() { - assertEquals(SLF4JLogger.class, Logger.forName("Joe").getClass()); + assertEquals(SLF4JLogStrategy.class, Logger.forName("Joe").getDelegate().getClass()); } } diff --git a/src/main/java/io/gitlab/jfronny/commons/log/DelegateLogStrategy.java b/src/main/java/io/gitlab/jfronny/commons/log/DelegateLogStrategy.java new file mode 100644 index 0000000..1107ee2 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/log/DelegateLogStrategy.java @@ -0,0 +1,139 @@ +package io.gitlab.jfronny.commons.log; + +import org.jetbrains.annotations.*; + +public class DelegateLogStrategy implements LogStrategy { + public DelegateLogStrategy() { + this(null); + } + + public DelegateLogStrategy(LogStrategy delegate) { + this.delegate = delegate; + } + + protected LogStrategy delegate; + + @Override + public @Nullable String getName() { + return delegate.getName(); + } + + @Override + public void trace(String msg) { + delegate.trace(msg); + } + + @Override + public void trace(String format, Object arg) { + delegate.trace(format, arg); + } + + @Override + public void trace(String format, Object... args) { + delegate.trace(format, args); + } + + @Override + public void trace(String msg, Throwable t) { + delegate.trace(msg, t); + } + + @Override + public void debug(String msg) { + delegate.debug(msg); + } + + @Override + public void debug(String format, Object arg) { + delegate.debug(format, arg); + } + + @Override + public void debug(String format, Object... args) { + delegate.debug(format, args); + } + + @Override + public void debug(String msg, Throwable t) { + delegate.debug(msg, t); + } + + @Override + public void info(String msg) { + delegate.info(msg); + } + + @Override + public void info(String format, Object arg) { + delegate.info(format, arg); + } + + @Override + public void info(String format, Object... args) { + delegate.info(format, args); + } + + @Override + public void info(String msg, Throwable t) { + delegate.info(msg, t); + } + + @Override + public void warn(String msg) { + delegate.warn(msg); + } + + @Override + public void warn(String format, Object arg) { + delegate.warn(format, arg); + } + + @Override + public void warn(String format, Object... args) { + delegate.warn(format, args); + } + + @Override + public void warn(String msg, Throwable t) { + delegate.warn(msg, t); + } + + @Override + public void error(String msg) { + delegate.error(msg); + } + + @Override + public void error(String format, Object arg) { + delegate.error(format, arg); + } + + @Override + public void error(String format, Object... args) { + delegate.error(format, args); + } + + @Override + public void error(String msg, Throwable t) { + delegate.error(msg, t); + } + + @Override + public String format(String format, Object arg) { + return delegate.format(format, arg); + } + + @Override + public String format(String format, Object... args) { + return delegate.format(format, args); + } + + @Override + public String format(String msg, Throwable t) { + return delegate.format(msg, t); + } + + public LogStrategy getDelegate() { + return delegate; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/log/JavaUtilLogger.java b/src/main/java/io/gitlab/jfronny/commons/log/JavaUtilLogStrategy.java similarity index 96% rename from src/main/java/io/gitlab/jfronny/commons/log/JavaUtilLogger.java rename to src/main/java/io/gitlab/jfronny/commons/log/JavaUtilLogStrategy.java index 3a0cded..0f5c958 100644 --- a/src/main/java/io/gitlab/jfronny/commons/log/JavaUtilLogger.java +++ b/src/main/java/io/gitlab/jfronny/commons/log/JavaUtilLogStrategy.java @@ -2,10 +2,10 @@ package io.gitlab.jfronny.commons.log; import java.util.logging.Level; -public class JavaUtilLogger implements Logger { +public class JavaUtilLogStrategy implements LogStrategy { private final java.util.logging.Logger logger; - public JavaUtilLogger(String name) { + public JavaUtilLogStrategy(String name) { this.logger = java.util.logging.Logger.getLogger(name); } diff --git a/src/main/java/io/gitlab/jfronny/commons/log/LogStrategy.java b/src/main/java/io/gitlab/jfronny/commons/log/LogStrategy.java new file mode 100644 index 0000000..5fe147d --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/log/LogStrategy.java @@ -0,0 +1,92 @@ +package io.gitlab.jfronny.commons.log; + +import io.gitlab.jfronny.commons.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Function; + +public interface LogStrategy { + @Nullable String getName(); + + default void trace(String msg) { + debug(msg); + } + default void trace(String format, Object arg) { + trace(format(format, arg)); + } + default void trace(String format, Object... args) { + trace(format(format, args)); + } + default void trace(String msg, Throwable t) { + trace(format(msg, t)); + } + + default void debug(String msg) { + info(msg); + } + default void debug(String format, Object arg) { + debug(format(format, arg)); + } + default void debug(String format, Object... args) { + debug(format(format, args)); + } + default void debug(String msg, Throwable t) { + debug(format(msg, t)); + } + + void info(String msg); + default void info(String format, Object arg) { + info(format(format, arg)); + } + default void info(String format, Object... args) { + info(format(format, args)); + } + default void info(String msg, Throwable t) { + info(format(msg, t)); + } + + default void warn(String msg) { + info(msg); + } + default void warn(String format, Object arg) { + warn(format(format, arg)); + } + default void warn(String format, Object... args) { + warn(format(format, args)); + } + default void warn(String msg, Throwable t) { + warn(format(msg, t)); + } + + default void error(String msg) { + warn(msg); + } + default void error(String format, Object arg) { + error(format(format, arg)); + } + default void error(String format, Object... args) { + error(format(format, args)); + } + default void error(String msg, Throwable t) { + error(format(msg, t)); + } + + default String format(String format, Object arg) { + return format.replaceFirst("\\{}", StringFormatter.toString(arg)); + } + + default String format(String format, Object... args) { + if (args == null || format == null) return format; + for (Object arg : args) { + format = format.replaceFirst("\\{}", StringFormatter.toString(arg)); + } + return format; + } + + default String format(String msg, Throwable t) { + if (t == null) return msg; + return msg + System.lineSeparator() + t; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/log/Logger.java b/src/main/java/io/gitlab/jfronny/commons/log/Logger.java index 4968e7e..f6f430f 100644 --- a/src/main/java/io/gitlab/jfronny/commons/log/Logger.java +++ b/src/main/java/io/gitlab/jfronny/commons/log/Logger.java @@ -1,104 +1,54 @@ package io.gitlab.jfronny.commons.log; -import io.gitlab.jfronny.commons.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import io.gitlab.jfronny.commons.ref.*; +import io.gitlab.jfronny.commons.reflect.Reflect; +import org.jetbrains.annotations.*; -import java.util.Objects; +import java.util.*; import java.util.function.Function; -public interface Logger { - static Logger forName(@NotNull String name) { - return LoggerHolder.LOGGER_BUILDER.apply(name); +public class Logger extends DelegateLogStrategy { + public static Function LOGGER_BUILDER; + private static final Set KNOWN_LOGGERS = new WeakSet<>(); + + static { + resetStrategy(); } - static void registerFactory(@NotNull Function factory) { - LoggerHolder.LOGGER_BUILDER = Objects.requireNonNull(factory); - } - - static void resetFactory() { - LoggerHolder.reset(); - } - - @Nullable String getName(); - - default void trace(String msg) { - debug(msg); - } - default void trace(String format, Object arg) { - trace(format(format, arg)); - } - default void trace(String format, Object... args) { - trace(format(format, args)); - } - default void trace(String msg, Throwable t) { - trace(format(msg, t)); - } - - default void debug(String msg) { - info(msg); - } - default void debug(String format, Object arg) { - debug(format(format, arg)); - } - default void debug(String format, Object... args) { - debug(format(format, args)); - } - default void debug(String msg, Throwable t) { - debug(format(msg, t)); - } - - void info(String msg); - default void info(String format, Object arg) { - info(format(format, arg)); - } - default void info(String format, Object... args) { - info(format(format, args)); - } - default void info(String msg, Throwable t) { - info(format(msg, t)); - } - - default void warn(String msg) { - info(msg); - } - default void warn(String format, Object arg) { - warn(format(format, arg)); - } - default void warn(String format, Object... args) { - warn(format(format, args)); - } - default void warn(String msg, Throwable t) { - warn(format(msg, t)); - } - - default void error(String msg) { - warn(msg); - } - default void error(String format, Object arg) { - error(format(format, arg)); - } - default void error(String format, Object... args) { - error(format(format, args)); - } - default void error(String msg, Throwable t) { - error(format(msg, t)); - } - - default String format(String format, Object arg) { - return format.replaceFirst("\\{}", StringFormatter.toString(arg)); - } - - default String format(String format, Object... args) { - if (args == null || format == null) return format; - for (Object arg : args) { - format = format.replaceFirst("\\{}", StringFormatter.toString(arg)); + public static void resetStrategy() { + try { + LOGGER_BUILDER = Reflect.getConstructor("io.gitlab.jfronny.commons.log.SLF4JLogStrategy", String.class) + .orThrow(); + } catch (ClassNotFoundException | NoSuchMethodException e) { + // SLF4J logger is unavailable, use java.util.logging + LOGGER_BUILDER = JavaUtilLogStrategy::new; } - return format; } - default String format(String msg, Throwable t) { - if (t == null) return msg; - return msg + System.lineSeparator() + t; + public static void updateStrategy(@NotNull Function factory) { + LOGGER_BUILDER = Objects.requireNonNull(factory); + synchronized (KNOWN_LOGGERS) { + for (Logger ref : KNOWN_LOGGERS) { + ref.updateStrategy(); + } + } + } + + public static Logger forName(@NotNull String name) { + return new Logger(name); + } + + private final String name; + + Logger(String name) { + this.name = name; + updateStrategy(); + synchronized (KNOWN_LOGGERS) { + KNOWN_LOGGERS.add(this); + } + } + + private void updateStrategy() { + delegate = LOGGER_BUILDER.apply(name); } } diff --git a/src/main/java/io/gitlab/jfronny/commons/log/LoggerHolder.java b/src/main/java/io/gitlab/jfronny/commons/log/LoggerHolder.java deleted file mode 100644 index c7315bb..0000000 --- a/src/main/java/io/gitlab/jfronny/commons/log/LoggerHolder.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.gitlab.jfronny.commons.log; - -import io.gitlab.jfronny.commons.reflect.Reflect; -import org.jetbrains.annotations.ApiStatus; - -import java.util.function.Function; - -@ApiStatus.Internal -class LoggerHolder { - public static Function LOGGER_BUILDER; - - static { - reset(); - } - - public static void reset() { - try { - LOGGER_BUILDER = Reflect.getConstructor("io.gitlab.jfronny.commons.log.SLF4JLogger", String.class) - .orThrow(); - } catch (ClassNotFoundException | NoSuchMethodException e) { - // SLF4J logger is unavailable, use java.util.logging - LOGGER_BUILDER = JavaUtilLogger::new; - } - } -} diff --git a/src/main/java/io/gitlab/jfronny/commons/log/NopLogger.java b/src/main/java/io/gitlab/jfronny/commons/log/NopLogStrategy.java similarity index 76% rename from src/main/java/io/gitlab/jfronny/commons/log/NopLogger.java rename to src/main/java/io/gitlab/jfronny/commons/log/NopLogStrategy.java index 36633d3..765ae05 100644 --- a/src/main/java/io/gitlab/jfronny/commons/log/NopLogger.java +++ b/src/main/java/io/gitlab/jfronny/commons/log/NopLogStrategy.java @@ -1,6 +1,6 @@ package io.gitlab.jfronny.commons.log; -public class NopLogger implements Logger { +public class NopLogStrategy implements LogStrategy { @Override public String getName() { return null; diff --git a/src/main/java/io/gitlab/jfronny/commons/log/StdoutLogger.java b/src/main/java/io/gitlab/jfronny/commons/log/StdoutLogStrategy.java similarity index 81% rename from src/main/java/io/gitlab/jfronny/commons/log/StdoutLogger.java rename to src/main/java/io/gitlab/jfronny/commons/log/StdoutLogStrategy.java index 4749296..b1fe93f 100644 --- a/src/main/java/io/gitlab/jfronny/commons/log/StdoutLogger.java +++ b/src/main/java/io/gitlab/jfronny/commons/log/StdoutLogStrategy.java @@ -1,10 +1,10 @@ package io.gitlab.jfronny.commons.log; -public class StdoutLogger implements Logger { +public class StdoutLogStrategy implements LogStrategy { private final String name; private final String prefix; - public StdoutLogger(String name) { + public StdoutLogStrategy(String name) { this.name = name; this.prefix = "[" + name + "] "; } diff --git a/src/main/java/io/gitlab/jfronny/commons/ref/WeakSet.java b/src/main/java/io/gitlab/jfronny/commons/ref/WeakSet.java new file mode 100644 index 0000000..0262719 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/ref/WeakSet.java @@ -0,0 +1,124 @@ +package io.gitlab.jfronny.commons.ref; + +import org.jetbrains.annotations.*; + +import java.lang.ref.*; +import java.util.*; + +public class WeakSet extends AbstractSet { + private final ReferenceQueue queue = new ReferenceQueue<>(); + private final HashSet> delegate = new HashSet<>(); + + @NotNull + @Override + public synchronized Iterator iterator() { + return new Iterator<>() { + final Iterator> delegate = WeakSet.this.delegate.iterator(); + E next = null; + + @Override + public boolean hasNext() { + if (delegate.hasNext()) { + next = unwrap(delegate.next()); + return true; + } + return false; + } + + @Override + public E next() { + if ((next == null) && !hasNext()) + throw new NoSuchElementException(); + E e = next; + next = null; + return e; + } + + @Override + public void remove() { + delegate.remove(); + } + }; + } + + @Override + public synchronized int size() { + processQueue(); + return delegate.size(); + } + + @Override + public synchronized boolean contains(Object o) { + return delegate.contains(WeakElement.create(o)); + } + + @Override + public synchronized boolean add(E e) { + processQueue(); + return delegate.add(WeakElement.create(e, this.queue)); + } + + @Override + public synchronized boolean remove(Object o) { + boolean ret = delegate.remove(WeakElement.create(o)); + processQueue(); + return ret; + } + + @Override + public void clear() { + processQueue(); + delegate.clear(); + } + + @Nullable + private static T unwrap(@Nullable Reference ref) { + return ref == null ? null : ref.get(); + } + + private void processQueue() { + Reference wv; + + while ((wv = this.queue.poll()) != null) { + super.remove(wv); + } + } + + private static class WeakElement extends WeakReference { + private final int hash; + private WeakElement(E value) { + super(value); + hash = value.hashCode(); + } + + private WeakElement(E value, ReferenceQueue queue) { + super(value, queue); + hash = value.hashCode(); + } + + private static WeakElement create(V value) { + return value == null ? null : new WeakElement<>(value); + } + + private static WeakElement create(V value, ReferenceQueue queue) { + return value == null ? null : new WeakElement<>(value, queue); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof WeakElement v)) return false; + return Objects.equals(this.get(), v.get()); + } + + @Override + public String toString() { + return Objects.toString(get()); + } + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/ref/WeakValueMap.java b/src/main/java/io/gitlab/jfronny/commons/ref/WeakValueMap.java new file mode 100644 index 0000000..1beab57 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/ref/WeakValueMap.java @@ -0,0 +1,264 @@ +package io.gitlab.jfronny.commons.ref; + +import org.jetbrains.annotations.*; + +import java.lang.ref.*; +import java.util.*; + +public class WeakValueMap implements Map { + private final ReferenceQueue queue = new ReferenceQueue<>(); + private final HashMap> delegate = new HashMap<>(); + private final EntrySet entrySet = new EntrySet(); + private final ValueCollection values = new ValueCollection(); + + @Override + public synchronized int size() { + processQueue(); + return delegate.size(); + } + + @Override + public synchronized boolean isEmpty() { + processQueue(); + return delegate.isEmpty(); + } + + @Override + public synchronized boolean containsKey(Object o) { + processQueue(); + return delegate.containsKey(o); + } + + @Override + public synchronized boolean containsValue(Object o) { + return delegate.containsValue(WeakValue.create(o)); + } + + @Override + public synchronized V get(Object o) { + return unwrap(delegate.get(o)); + } + + @Nullable + @Override + public synchronized V put(K k, V v) { + processQueue(); + return unwrap(delegate.put(k, WeakValue.create(k, v, queue))); + } + + @Override + public synchronized V remove(Object o) { + return unwrap(delegate.remove(o)); + } + + @Override + public synchronized void putAll(@NotNull Map map) { + processQueue(); + for (Entry entry : map.entrySet()) { + delegate.put(entry.getKey(), WeakValue.create(entry.getKey(), entry.getValue(), queue)); + } + } + + @Override + public synchronized void clear() { + delegate.clear(); + } + + @NotNull + @Override + public synchronized Set keySet() { + processQueue(); + return delegate.keySet(); + } + + @NotNull + @Override + public Set> entrySet() { + return entrySet; + } + + @NotNull + @Override + public Collection values() { + return values; + } + + @Nullable + private static T unwrap(@Nullable Reference ref) { + return ref == null ? null : ref.get(); + } + + public void processQueue() { + WeakValue ref; + while ((ref = (WeakValue) queue.poll()) != null) { + delegate.remove(ref.key); + } + } + + private static class WeakValue extends WeakReference { + private K key; + private WeakValue(V value) { + super(value); + } + + private WeakValue(K key, V value, ReferenceQueue queue) { + super(value, queue); + this.key = key; + } + + private static WeakValue create(V value) { + if (value == null) return null; + else return new WeakValue<>(value); + } + + private static WeakValue create(K key, V value, ReferenceQueue queue) { + if (value == null) return null; + else return new WeakValue<>(key, value, queue); + } + + @Override + public int hashCode() { + V ref = this.get(); + return ref == null ? 0 : ref.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof WeakValueMap.WeakValue v)) return false; + return Objects.equals(this.get(), v.get()); + } + + @Override + public String toString() { + return Objects.toString(get()); + } + } + + private class DelegateEntry implements Entry { + private final Entry> ent; + private V value; + + DelegateEntry(Entry> ent, V value) { + this.ent = ent; + this.value = value; + } + + @Override + public K getKey() { + return ent.getKey(); + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(Object o) { + Object oldValue = this.value; + this.value = (V) o; + ent.setValue(WeakValue.create(getKey(), this.value, queue)); + return (V) oldValue; + } + + @Override + public int hashCode() { + K k; + return ((((k = ent.getKey()) == null) ? 0 : k.hashCode()) + ^ ((value == null) ? 0 : value.hashCode())); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Entry e)) return false; + return Objects.equals(getKey(), e.getKey()) && Objects.equals(getValue(), e.getValue()); + } + } + + private class EntrySet extends AbstractSet> { + @Override + public @NotNull Iterator> iterator() { + processQueue(); + + return new Iterator<>() { + final Iterator>> delegate = WeakValueMap.this.delegate.entrySet().iterator(); + Entry next = null; + + @Override + public boolean hasNext() { + if (delegate.hasNext()) { + Entry> ent = delegate.next(); + next = new DelegateEntry(ent, unwrap(ent.getValue())); + return true; + } + return false; + } + + @Override + public Entry next() { + if ((next == null) && !hasNext()) + throw new NoSuchElementException(); + Entry e = next; + next = null; + return e; + } + + @Override + public void remove() { + delegate.remove(); + } + }; + } + + @Override + public boolean isEmpty() { + return WeakValueMap.this.isEmpty(); + } + + @Override + public int size() { + return WeakValueMap.this.size(); + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof Map.Entry e)) return false; + return WeakValueMap.this.remove(e.getKey(), e.getValue()); + } + } + + private class ValueCollection extends AbstractCollection { + @Override + public @NotNull Iterator iterator() { + return new Iterator<>() { + final Iterator> delegate = entrySet().iterator(); + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public V next() { + return delegate.next().getValue(); + } + + @Override + public void remove() { + delegate.remove(); + } + }; + } + + @Override + public int size() { + return WeakValueMap.this.size(); + } + + @Override + public boolean contains(Object o) { + return WeakValueMap.this.containsValue(o); + } + } +} diff --git a/src/test/java/io/gitlab/jfronny/commons/test/LoggerTest.java b/src/test/java/io/gitlab/jfronny/commons/test/LoggerTest.java index 6a3f5ed..c097cd9 100644 --- a/src/test/java/io/gitlab/jfronny/commons/test/LoggerTest.java +++ b/src/test/java/io/gitlab/jfronny/commons/test/LoggerTest.java @@ -8,17 +8,19 @@ import static org.junit.jupiter.api.Assertions.*; public class LoggerTest { @BeforeEach void prepare() { - Logger.resetFactory(); + Logger.resetStrategy(); } @Test void testFactory() { - assertEquals(JavaUtilLogger.class, Logger.forName("Joe").getClass()); + assertEquals(JavaUtilLogStrategy.class, Logger.forName("Joe").getDelegate().getClass()); } @Test void testRegisterFactory() { - Logger.registerFactory(s -> new NopLogger()); - assertEquals(NopLogger.class, Logger.forName("Joe").getClass()); + Logger logger = Logger.forName("Joe"); + assertEquals(JavaUtilLogStrategy.class, logger.getDelegate().getClass()); + Logger.updateStrategy(s -> new NopLogStrategy()); + assertEquals(NopLogStrategy.class, logger.getDelegate().getClass()); } }