[main] Rewrite logger to support hot-swapping strategies, utils for weak references
This commit is contained in:
parent
5ca20446c6
commit
eb71fb6533
|
@ -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);
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<String, LogStrategy> LOGGER_BUILDER;
|
||||
private static final Set<Logger> KNOWN_LOGGERS = new WeakSet<>();
|
||||
|
||||
static {
|
||||
resetStrategy();
|
||||
}
|
||||
|
||||
static void registerFactory(@NotNull Function<String, Logger> 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.<String, LogStrategy>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<String, LogStrategy> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, Logger> LOGGER_BUILDER;
|
||||
|
||||
static {
|
||||
reset();
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
try {
|
||||
LOGGER_BUILDER = Reflect.<String, Logger>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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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 + "] ";
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package io.gitlab.jfronny.commons.ref;
|
||||
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.lang.ref.*;
|
||||
import java.util.*;
|
||||
|
||||
public class WeakSet<E> extends AbstractSet<E> {
|
||||
private final ReferenceQueue<E> queue = new ReferenceQueue<>();
|
||||
private final HashSet<Reference<E>> delegate = new HashSet<>();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized Iterator<E> iterator() {
|
||||
return new Iterator<>() {
|
||||
final Iterator<Reference<E>> 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> T unwrap(@Nullable Reference<T> ref) {
|
||||
return ref == null ? null : ref.get();
|
||||
}
|
||||
|
||||
private void processQueue() {
|
||||
Reference<? extends E> wv;
|
||||
|
||||
while ((wv = this.queue.poll()) != null) {
|
||||
super.remove(wv);
|
||||
}
|
||||
}
|
||||
|
||||
private static class WeakElement<E> extends WeakReference<E> {
|
||||
private final int hash;
|
||||
private WeakElement(E value) {
|
||||
super(value);
|
||||
hash = value.hashCode();
|
||||
}
|
||||
|
||||
private WeakElement(E value, ReferenceQueue<E> queue) {
|
||||
super(value, queue);
|
||||
hash = value.hashCode();
|
||||
}
|
||||
|
||||
private static <V> WeakElement<V> create(V value) {
|
||||
return value == null ? null : new WeakElement<>(value);
|
||||
}
|
||||
|
||||
private static <V> WeakElement<V> create(V value, ReferenceQueue<V> 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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
package io.gitlab.jfronny.commons.ref;
|
||||
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.lang.ref.*;
|
||||
import java.util.*;
|
||||
|
||||
public class WeakValueMap<K, V> implements Map<K, V> {
|
||||
private final ReferenceQueue<V> queue = new ReferenceQueue<>();
|
||||
private final HashMap<K, WeakValue<K, V>> 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<? extends K, ? extends V> map) {
|
||||
processQueue();
|
||||
for (Entry<? extends K, ? extends V> 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<K> keySet() {
|
||||
processQueue();
|
||||
return delegate.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return entrySet;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> T unwrap(@Nullable Reference<T> ref) {
|
||||
return ref == null ? null : ref.get();
|
||||
}
|
||||
|
||||
public void processQueue() {
|
||||
WeakValue<K, V> ref;
|
||||
while ((ref = (WeakValue<K, V>) queue.poll()) != null) {
|
||||
delegate.remove(ref.key);
|
||||
}
|
||||
}
|
||||
|
||||
private static class WeakValue<K, V> extends WeakReference<V> {
|
||||
private K key;
|
||||
private WeakValue(V value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
private WeakValue(K key, V value, ReferenceQueue<V> queue) {
|
||||
super(value, queue);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
private static <K, V> WeakValue<K, V> create(V value) {
|
||||
if (value == null) return null;
|
||||
else return new WeakValue<>(value);
|
||||
}
|
||||
|
||||
private static <K, V> WeakValue<K, V> create(K key, V value, ReferenceQueue<V> 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<K, V> {
|
||||
private final Entry<K, WeakValue<K, V>> ent;
|
||||
private V value;
|
||||
|
||||
DelegateEntry(Entry<K, WeakValue<K, V>> 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<Entry<K, V>> {
|
||||
@Override
|
||||
public @NotNull Iterator<Entry<K, V>> iterator() {
|
||||
processQueue();
|
||||
|
||||
return new Iterator<>() {
|
||||
final Iterator<Entry<K, WeakValue<K, V>>> delegate = WeakValueMap.this.delegate.entrySet().iterator();
|
||||
Entry<K, V> next = null;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (delegate.hasNext()) {
|
||||
Entry<K, WeakValue<K, V>> ent = delegate.next();
|
||||
next = new DelegateEntry(ent, unwrap(ent.getValue()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> next() {
|
||||
if ((next == null) && !hasNext())
|
||||
throw new NoSuchElementException();
|
||||
Entry<K, V> 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<V> {
|
||||
@Override
|
||||
public @NotNull Iterator<V> iterator() {
|
||||
return new Iterator<>() {
|
||||
final Iterator<Entry<K, V>> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue