Skip to content

Commit

Permalink
feat(api): Pointers supplier
Browse files Browse the repository at this point in the history
  • Loading branch information
kezz committed Feb 27, 2024
1 parent aeaeb99 commit 2788047
Show file tree
Hide file tree
Showing 3 changed files with 296 additions and 0 deletions.
127 changes: 127 additions & 0 deletions api/src/main/java/net/kyori/adventure/pointer/PointersSupplier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2021 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.pointer;

import java.util.function.Function;
import net.kyori.adventure.builder.AbstractBuilder;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* A supplier of {@link Pointers} that allows for the implementation of pointers
* in a static context without having to manually create a new pointers instance for
* each instance of a given type.
*
* <p>An example of how this could be implemented is as follows:</p>
* <pre>{@code
* public class MyPointeredObject extends SomePointeredParent implements Pointered {
* private static final PointersSupplier<MyPointeredObject> POINTERS = PointersSupplier.builder()
* .parent(SomePointeredParent.POINTERS) // Fallback to the parent to get pointers from.
* .resolving(Identity.UUID, MyPointeredObject::getUniqueId)
* .resolving(Identity.DISPLAY_NAME, MyPointeredObject::getDisplayName)
* .build();
*
* @Override
* public Pointers pointers() {
* return POINTERS.view(this);
* }
* }
* }</pre>
*
* @param <T> the type
* @since 4.17.0
*/
public interface PointersSupplier<T> {
/**
* Gets a new pointers supplier builder.
*
* @param <T> the type
* @return the builder
* @since 4.17.0
*/
static <T> @NotNull Builder<T> builder() {
return new PointersSupplierImpl.BuilderImpl<>();
}

/**
* Creates a pointers view for the given instance.
*
* @param instance the instance
* @return the view
* @since 4.17.0
*/
@NotNull Pointers view(final @NotNull T instance);

/**
* Checks if this supplier supports a given pointer.
*
* @param pointer the pointer
* @param <P> the type of the pointer
* @return if this supplier supports a given pointer
* @since 4.17.0
*/
<P> boolean supports(final @NotNull Pointer<P> pointer);

/**
* Returns the resolver for a given pointer (if any).
*
* @param pointer the pointer
* @param <P> the type of the pointer
* @return the resolver, if any
* @since 4.17.0
*/
<P> @Nullable Function<? super T, P> resolver(final @NotNull Pointer<P> pointer);

/**
* A builder for {@link PointersSupplier}.
*
* @param <T> the type to supply pointers for
* @since 4.17.0
*/
interface Builder<T> extends AbstractBuilder<PointersSupplier<T>> {
/**
* Sets (or removes, if {@code null}) the parent pointer supplier that will be used
* to resolve pointers that are not supplied by this supplier.
*
* @param parent the parent
* @return this builder
* @since 4.17.0
*/
@Contract("_ -> this")
@NotNull Builder<T> parent(final @Nullable PointersSupplier<? super T> parent);

/**
* Adds a resolver for a given pointer.
*
* @param pointer the pointer
* @param resolver the resolver
* @param <P> the type of the pointer
* @return this builder
* @since 4.17.0
*/
@Contract("_, _ -> this")
<P> @NotNull Builder<T> resolving(final @NotNull Pointer<P> pointer, final @NotNull Function<T, P> resolver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2021 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.pointer;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

final class PointersSupplierImpl<T> implements PointersSupplier<T> {
private final PointersSupplier<? super T> parent;
private final Map<Pointer<?>, Function<T, ?>> resolvers;

PointersSupplierImpl(final @NotNull BuilderImpl<T> builder) {
this.parent = builder.parent;
this.resolvers = new HashMap<>(builder.resolvers);
}

@Override
public @NotNull Pointers view(final @NotNull T instance) {
return new ForwardingPointers<>(instance, this);
}

@Override
public <P> boolean supports(@NotNull Pointer<P> pointer) {
if (this.resolvers.containsKey(Objects.requireNonNull(pointer, "pointer"))) {
return true;
} else if (this.parent == null) {
return false;
} else {
return this.parent.supports(pointer);
}
}

@Override
@SuppressWarnings("unchecked") // all values are checked on entry
public @Nullable <P> Function<? super T, P> resolver(@NotNull Pointer<P> pointer) {
Function<? super T, ?> resolver = this.resolvers.get(Objects.requireNonNull(pointer, "pointer"));

if (resolver != null) {
return (Function<? super T, P>) resolver;
} else if (this.parent == null) {
return null;
} else {
return this.parent.resolver(pointer);
}
}

static final class ForwardingPointers<U> implements Pointers {
private final U instance;
private final PointersSupplierImpl<U> supplier;

ForwardingPointers(final @NotNull U instance, final @NotNull PointersSupplierImpl<U> supplier) {
this.instance = instance;
this.supplier = supplier;
}

@Override
@SuppressWarnings("unchecked") // all values are checked on entry
public @NotNull <T> Optional<T> get(final @NotNull Pointer<T> pointer) {
Function<? super U, ?> resolver = this.supplier.resolvers.get(Objects.requireNonNull(pointer, "pointer"));

// Fallback to the parent.
if (resolver == null) {
resolver = this.supplier.parent.resolver(pointer);
}

// Finally, wrap in an optional.
if (resolver == null) {
return Optional.empty();
} else {
return Optional.ofNullable((T) resolver.apply(this.instance));
}
}

@Override
public <T> boolean supports(final @NotNull Pointer<T> pointer) {
return this.supplier.supports(pointer);
}

@Override
@SuppressWarnings("unchecked") // all values are checked on entry
public @NotNull Pointers.Builder toBuilder() {
final Pointers.Builder builder = this.supplier.parent == null ? Pointers.builder() : this.supplier.parent.view(this.instance).toBuilder();

for (final Map.Entry<Pointer<?>, Function<U, ?>> entry : this.supplier.resolvers.entrySet()) {
builder.withDynamic(entry.getKey(), (Supplier) () -> entry.getValue().apply(this.instance));
}

return builder;
}
}

static final class BuilderImpl<T> implements Builder<T> {
private PointersSupplier<? super T> parent = null;
private final Map<Pointer<?>, Function<T, ?>> resolvers;

BuilderImpl() {
this.resolvers = new HashMap<>();
}

@Override
public @NotNull Builder<T> parent(final @Nullable PointersSupplier<? super T> parent) {
this.parent = parent;
return this;
}

@Override
public @NotNull <P> Builder<T> resolving(final @NotNull Pointer<P> pointer, final @NotNull Function<T, P> resolver) {
this.resolvers.put(pointer, resolver);
return this;
}

@Override
public @NotNull PointersSupplier<T> build() {
return new PointersSupplierImpl<>(this);
}
}
}
25 changes: 25 additions & 0 deletions api/src/test/java/net/kyori/adventure/pointer/PointersTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,29 @@ public void ofPointers() {
assertEquals("test", p2.getOrDefault(pointer, null));
assertEquals("tset", p2.getOrDefault(pointer, null)); // make sure the value does change
}

@Test
public void ofPointersSuppliers() {
final Pointer<String> p0 = Pointer.pointer(String.class, Key.key("adventure:test1"));
final Pointer<String> p1 = Pointer.pointer(String.class, Key.key("adventure:test2"));

final PointersSupplier<Object> parent = PointersSupplier.builder()
.resolving(p0, (object) -> "0")
.build();
final PointersSupplier<Integer> child = PointersSupplier.<Integer>builder()
.parent(parent)
.resolving(p1, (object) -> "1")
.build();

assertTrue(child.supports(p0));
assertTrue(child.supports(p1));

final Pointers childPointers = child.view(10);
assertEquals("0", childPointers.get(p0).get());
assertEquals("1", childPointers.get(p1).get());

final Pointers rebuilt = childPointers.toBuilder().withStatic(p0, "1").build();
assertEquals("1", rebuilt.get(p0).get());
assertEquals("1", rebuilt.get(p1).get());
}
}

0 comments on commit 2788047

Please sign in to comment.