Skip to content

Commit

Permalink
✨ TypedRef to ref k-v type #50
Browse files Browse the repository at this point in the history
  • Loading branch information
trydofor committed Jan 22, 2025
1 parent 14979bf commit f858927
Show file tree
Hide file tree
Showing 8 changed files with 451 additions and 24 deletions.
3 changes: 0 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,8 @@
<configuration>
<excludes>
<!-- mark or assert -->
<exclude>**/best/Dummy*</exclude>
<exclude>**/best/Param*</exclude>
<exclude>**/best/Typed*</exclude>
<!-- data struct -->
<exclude>**/data/DataResult.*</exclude>
<exclude>**/data/Null*</exclude>
<exclude>**/data/Q.*</exclude>
<exclude>**/data/Q$*</exclude>
Expand Down
16 changes: 10 additions & 6 deletions src/main/java/pro/fessional/mirana/best/TypedKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
* <pre>
Expand Down Expand Up @@ -44,15 +45,18 @@ protected TypedKey() {
}

@SuppressWarnings("unchecked")
@Nullable
public V get(@NotNull Map<?, ?> map) throws ClassCastException {
final Object o = map.get(this);
return (V) o;
@Contract("_,true->!null")
public V get(@NotNull Function<TypedKey<V>,?> map, boolean nonnull) throws ClassCastException {
Object obj = map.apply(this);
if (obj == null && nonnull) {
throw new ClassCastException("null cast to nonnull");
}
return (V) obj;
}

@Contract("_,!null ->!null")
public V getOr(@NotNull Map<?, ?> map, V elze) throws ClassCastException {
final V obj = get(map);
public V getOr(@NotNull Function<TypedKey<V>,?> map, V elze) throws ClassCastException {
final V obj = get(map, false);
return obj != null ? obj : elze;
}

Expand Down
72 changes: 72 additions & 0 deletions src/main/java/pro/fessional/mirana/best/TypedRef.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package pro.fessional.mirana.best;


import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.function.Function;

/**
* <pre>
* Usage: mark value and its ref type.
* {@code
* Map<String, Integer> map = new HashMap<>();
* map.put("key", 42);
* TypedRef<String, Integer> ref = new TypedRef<>("key");
* Integer result = ref.get(map);
* }
* </pre>
*
* @author trydofor
* @since 2025-01-21
*/
public class TypedRef<V, R> {
@NotNull
public final V value;

public TypedRef(@NotNull V value) {
this.value = value;
}

@SuppressWarnings("unchecked")
@Contract("_,true->!null")
public R get(@NotNull Function<V, ?> map, boolean nonnull) throws ClassCastException {
Object obj = map.apply(value);
if (obj == null && nonnull) {
throw new ClassCastException("null cast to nonnull");
}
return (R) obj;
}

@Contract("_,!null ->!null")
public R getOr(@NotNull Function<V, ?> map, R elze) throws ClassCastException {
final R obj = get(map, false);
return obj != null ? obj : elze;
}

@SuppressWarnings("unchecked")
@Contract("_,!null ->!null")
public R tryOr(@Nullable Object obj, R elze) throws ClassCastException {
return obj != null ? (R) obj : elze;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TypedRef<?, ?> typedRef = (TypedRef<?, ?>) o;
return Objects.equals(value, typedRef.value);
}

@Override
public int hashCode() {
return Objects.hashCode(value);
}

@Override
public String toString() {
return value.toString();
}
}
22 changes: 13 additions & 9 deletions src/main/java/pro/fessional/mirana/best/TypedReg.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
* <pre>
Expand Down Expand Up @@ -47,6 +48,10 @@ protected TypedReg() {
INSTANCE.put(clz.getName(), this);
}

public Key<K, V> key(K key) {
return new Key<>(this, key);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down Expand Up @@ -100,10 +105,6 @@ public static <K, V> TypedReg<K, V> deserialize(@NotNull String clz, boolean non
}
}

public static <K, V> Key<K, V> key(@NotNull TypedReg<K, V> reg, K key) {
return new Key<>(reg, key);
}

public static class Key<K, V> {
@NotNull
public final TypedReg<K, V> reg;
Expand All @@ -116,14 +117,17 @@ public Key(@NotNull TypedReg<K, V> reg, K key) {

@SuppressWarnings("unchecked")
@Nullable
public V get(@NotNull Map<?, ?> map) throws ClassCastException {
final Object o = map.get(this);
return (V) o;
public V get(@NotNull Function<Key<K, V>, ?> map, boolean nonnull) throws ClassCastException {
Object obj = map.apply(this);
if (obj == null && nonnull) {
throw new ClassCastException("null cast to nonnull");
}
return (V) obj;
}

@Contract("_,!null ->!null")
public V getOr(@NotNull Map<?, ?> map, V elze) throws ClassCastException {
final V obj = get(map);
public V getOr(@NotNull Function<Key<K, V>, ?> map, V elze) throws ClassCastException {
final V obj = get(map, false);
return obj != null ? obj : elze;
}

Expand Down
64 changes: 64 additions & 0 deletions src/test/java/pro/fessional/mirana/best/DummyBlockTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package pro.fessional.mirana.best;

import org.junit.jupiter.api.Test;

import java.util.concurrent.atomic.AtomicReference;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class DummyBlockTest {

@Test
void testIgnore() {
AtomicReference<Throwable> captured = new AtomicReference<>();
DummyBlock.TweakIgnore.tweakGlobal(captured::set);

Throwable testException = new RuntimeException("Test exception");
DummyBlock.ignore(testException);

assertEquals(testException, captured.get());

DummyBlock.TweakIgnore.resetGlobal();
}

@Test
void testIgnoreWithoutHandler() {
// Test ignore with no handler set
Throwable testException = new RuntimeException("Test exception");
assertDoesNotThrow(() -> DummyBlock.ignore(testException));
}

@Test
void testEmpty() {
assertDoesNotThrow(DummyBlock::empty);
}

@Test
void testNever() {
IllegalStateException exception = assertThrows(IllegalStateException.class, DummyBlock::never);
assertEquals("should NOT invoke NEVER", exception.getMessage());

exception = assertThrows(IllegalStateException.class, () -> DummyBlock.never("Custom message"));
assertEquals("should NOT invoke NEVER:Custom message", exception.getMessage());
}

@Test
void testTodo() {
IllegalStateException exception = assertThrows(IllegalStateException.class, DummyBlock::todo);
assertEquals("should NOT invoke TODO", exception.getMessage());

exception = assertThrows(IllegalStateException.class, () -> DummyBlock.todo("Custom message"));
assertEquals("should NOT invoke TODO:Custom message", exception.getMessage());
}

@Test
void testFixme() {
IllegalStateException exception = assertThrows(IllegalStateException.class, DummyBlock::fixme);
assertEquals("should NOT invoke FIXME", exception.getMessage());

exception = assertThrows(IllegalStateException.class, () -> DummyBlock.fixme("Custom message"));
assertEquals("should NOT invoke FIXME:Custom message", exception.getMessage());
}
}
113 changes: 113 additions & 0 deletions src/test/java/pro/fessional/mirana/best/TypedKeyTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package pro.fessional.mirana.best;

import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* @author trydofor
* @since 2025-01-22
*/
class TypedKeyTest {
static class TestTypedKey extends TypedKey<String> {}

@Test
void testConstructor() {
TypedKey<String> key = new TestTypedKey();
assertNotNull(key.regType);
assertNotNull(key.valType);
assertEquals(String.class, key.valType);
}

@Test
void testGet() {
TypedKey<String> key = new TestTypedKey();
Map<TypedKey<?>, Object> map = new HashMap<>();
map.put(key, "value");

String value = key.get(map::get, true);
assertEquals("value", value);
}

@Test
void testGetOr() {
TypedKey<String> key = new TestTypedKey();
Map<TypedKey<?>, Object> map = new HashMap<>();

// Test with no value in the map
String defaultValue = "default";
String value = key.getOr(map::get, defaultValue);
assertEquals(defaultValue, value);

// Test with a value in the map
map.put(key, "value");
value = key.getOr(map::get, defaultValue);
assertEquals("value", value);
}

@Test
void testTryOr() {
TypedKey<String> key = new TestTypedKey();

// Test with null object
String defaultValue = "default";
String value = key.tryOr(null, defaultValue);
assertEquals(defaultValue, value);

// Test with non-null object
value = key.tryOr("value", defaultValue);
assertEquals("value", value);
}

@Test
void testSerializeDeserialize() {
TypedKey<String> key = new TestTypedKey();
String serialized = key.serialize();

TypedKey<String> deserializedKey = TypedKey.deserialize(serialized);
assertNotNull(deserializedKey);
assertEquals(key.regType, deserializedKey.regType);
}

@Test
void testDeserializeNonNull() {
TypedKey<String> key = new TestTypedKey();
String serialized = key.serialize();

TypedKey<String> deserializedKey = TypedKey.deserialize(serialized, true);
assertNotNull(deserializedKey);
assertSame(key, deserializedKey);
}

@Test
void testDeserializeNullable() {
TypedKey<String> deserializedKey = TypedKey.deserialize("nonexistent", false);
assertNull(deserializedKey);
}

@Test
void testEqualsAndHashCode() {
TypedKey<String> key1 = new TestTypedKey();
TypedKey<String> key2 = new TestTypedKey();

assertEquals(key1, key1); // Reflexive
assertNotEquals(key1, key2); // Different instances
assertEquals(key1.hashCode(), key1.hashCode());
}

@Test
void testToString() {
TypedKey<String> key = new TestTypedKey();
String toString = key.toString();
assertTrue(toString.contains("regType"));
assertTrue(toString.contains("valType"));
}
}
Loading

0 comments on commit f858927

Please sign in to comment.