Skip to content

Instantly share code, notes, and snippets.

@ItsDoot
Created June 7, 2019 20:11
Show Gist options
  • Select an option

  • Save ItsDoot/dd52cee83675ccafc445cf2e0d76835a to your computer and use it in GitHub Desktop.

Select an option

Save ItsDoot/dd52cee83675ccafc445cf2e0d76835a to your computer and use it in GitHub Desktop.
Asynchronous and Synchronous data access
/*
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* 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 org.spongepowered.api.data;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.api.data.persistence.DataSerializable;
import org.spongepowered.api.data.persistence.DataView;
import org.spongepowered.api.data.persistence.InvalidDataException;
import org.spongepowered.api.data.property.PropertyHolder;
import org.spongepowered.api.data.value.CollectionValue;
import org.spongepowered.api.data.value.MapValue;
import org.spongepowered.api.data.value.MergeFunction;
import org.spongepowered.api.data.value.Value;
import org.spongepowered.api.data.value.ValueContainer;
import org.spongepowered.api.entity.EntityType;
import org.spongepowered.api.item.ItemType;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
/**
* A data holder object allows the access of additional data on the object
* that is not simply expressed by its basic type.
*/
public interface DataHolder extends DataSerializable, PropertyHolder, ValueContainer {
interface Mutable extends DataHolder {
// ...
/**
* Offers the given {@code value} as defined by the provided {@link Key}
* such that a {@link DataTransactionResult} is returned for any
* successful, rejected, and replaced {@link Value}s from this
* {@link Mutable}.
*
* @param key The key to the value to set
* @param value The value to set
* @param <E> The type of value
* @return The transaction result
*/
<V extends Value<E>, E> DataTransactionResult offer(Key<V, DataProvider.Sync<V, E>> key, E value);
/**
* Offers the given {@code value} as defined by the provided {@link Key}
* such that a {@link DataTransactionResult} is returned for any
* successful, rejected, and replaced {@link Value}s from this
* {@link Mutable}.
*
* @param key The key to the value to set
* @param value The value to set
* @param <E> The type of value
* @return The transaction result
*/
<V extends Value<E>, E> CompletableFuture<DataTransactionResult> offerAsync(Key<V, DataProvider.Async<V, E>> key, E value);
// ...
}
}
/*
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* 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 org.spongepowered.api.data;
import com.google.common.reflect.TypeToken;
import org.spongepowered.api.Server;
import org.spongepowered.api.data.value.Value;
import org.spongepowered.api.event.data.ChangeDataHolderEvent;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public interface DataProvider<V extends Value<E>, E> {
/**
* Gets whether this provider will allow asynchronous access for retrieving
* and storing value changes through the API and implementation. This is
* usually sanity checked by the implementation through a simplified
* {@link Server#onMainThread()} as a majority of datas are required to be
* synchronous if the changes can end up throwing {@link ChangeDataHolderEvent}s.
*
* <p>A list of methods that are constrained by this check are:
* <ul>
* <li>- {@link #get(DataHolder)}</li>
* <li>- {@link #offer(DataHolder.Mutable, Object)}</li>
* <li>- {@link #remove(DataHolder.Mutable)}</li>
* </ul>
* Conceptually, an immutable {@link DataHolder} will be ignorant of
* asynchronous access, however, some cases may exist where attempting to
* create new immutable variants with different values can be still limited
* by synchronous access.
* </p>
*
* @param token The token of the {@link DataHolder} that is being requested
* @return True if this provider allows asynchronous access
*/
boolean allowsAsynchronousAccess(TypeToken<? extends DataHolder> token);
/**
* Gets the {@link Key} this provider supports.
*
* @return The key
*/
Key<V, ? extends DataProvider<V, E>> getKey();
interface Sync<V extends Value<E>, E> extends DataProvider<V, E> {
@Override
Key<V, Sync<V, E>> getKey();
/**
* Gets the elemental value from the provided {@link DataHolder}. This is
* generally considered the underlying implementation access for any
* {@link DataHolder#get(Key)} where the {@link Key} is registered with
* this {@link DataProvider}. Nominally, this means the data is provided
* outside traditional serialized data that is stored with the
* {@link DataHolder}. It's possible that there may be changing return values
* for even immutable types, since the provider is providing the data.
*
* @param container The dataholder
* @return The value, if it's supported and exists
*/
Optional<E> get(DataHolder container);
/**
* Gets a constructed {@link Value} for the provided {@link DataHolder}.
* Much like {@link #get(DataHolder)}, this is generally considered the
* underlying implementation access for any {@link DataHolder#get(Key)}
* where the {@link Key} is registered with this {@link DataProvider}.
* Nominally, this means the data is provided outside traditional serialized
* data that is stored with the {@link DataHolder}. It's possible that there
* may be changing return values for even immutable types, since the
* provider is providing the data.
*
* @param container The holder to get the constructed value from
* @return The value
*/
default Optional<V> getValue(DataHolder container) {
return get(container).map(element -> Value.mutableOf(getKey(), element));
}
DataTransactionResult offer(DataHolder.Mutable container, E element);
default DataTransactionResult offerValue(DataHolder.Mutable container, V value) {
return offer(container, value.get());
}
DataTransactionResult remove(DataHolder.Mutable container);
<I extends DataHolder.Immutable<I>> Optional<I> with(I immutable, E element);
default <I extends DataHolder.Immutable<I>> Optional<I> withValue(I immutable, V value) {
return with(immutable, value.get());
}
/**
* Gets a {@link DataHolder.Immutable} without
* a {@link Value} with the target {@link Key}, if successful.
*
* @param immutable The immutable value store
* @param <I> The type of the immutable value store
* @return The new value store, if successful
*/
<I extends DataHolder.Immutable<I>> Optional<I> without(I immutable);
}
interface Async<V extends Value<E>, E> extends DataProvider<V, E> {
@Override
Key<V, Async<V, E>> getKey();
/**
* Gets the elemental value from the provided {@link DataHolder}. This is
* generally considered the underlying implementation access for any
* {@link DataHolder#get(Key)} where the {@link Key} is registered with
* this {@link DataProvider}. Nominally, this means the data is provided
* outside traditional serialized data that is stored with the
* {@link DataHolder}. It's possible that there may be changing return values
* for even immutable types, since the provider is providing the data.
*
* @param container The dataholder
* @return The value, if it's supported and exists
*/
CompletableFuture<Optional<E>> get(DataHolder container);
/**
* Gets a constructed {@link Value} for the provided {@link DataHolder}.
* Much like {@link #get(DataHolder)}, this is generally considered the
* underlying implementation access for any {@link DataHolder#get(Key)}
* where the {@link Key} is registered with this {@link DataProvider}.
* Nominally, this means the data is provided outside traditional serialized
* data that is stored with the {@link DataHolder}. It's possible that there
* may be changing return values for even immutable types, since the
* provider is providing the data.
*
* @param container The holder to get the constructed value from
* @return The value
*/
default CompletableFuture<Optional<V>> getValue(DataHolder container) {
return get(container).thenApply(value -> value.map(element -> Value.mutableOf(getKey(), element)));
}
CompletableFuture<DataTransactionResult> offer(DataHolder.Mutable container, E element);
default CompletableFuture<DataTransactionResult> offerValue(DataHolder.Mutable container, V value) {
return offer(container, value.get());
}
CompletableFuture<DataTransactionResult> remove(DataHolder.Mutable container);
<I extends DataHolder.Immutable<I>> CompletableFuture<Optional<I>> with(I immutable, E element);
default <I extends DataHolder.Immutable<I>> CompletableFuture<Optional<I>> withValue(I immutable, V value) {
return with(immutable, value.get());
}
/**
* Gets a {@link DataHolder.Immutable} without
* a {@link Value} with the target {@link Key}, if successful.
*
* @param immutable The immutable value store
* @param <I> The type of the immutable value store
* @return The new value store, if successful
*/
<I extends DataHolder.Immutable<I>> CompletableFuture<Optional<I>> without(I immutable);
}
boolean isSupported(DataHolder container);
}
/*
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* 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 org.spongepowered.api.data;
import com.google.common.reflect.TypeToken;
import org.spongepowered.api.CatalogKey;
import org.spongepowered.api.CatalogType;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.data.value.BoundedValue;
import org.spongepowered.api.data.value.Value;
import org.spongepowered.api.data.value.ValueContainer;
import org.spongepowered.api.event.EventListener;
import org.spongepowered.api.event.data.ChangeDataHolderEvent;
import org.spongepowered.api.util.CatalogBuilder;
import org.spongepowered.api.util.TypeTokens;
import org.spongepowered.api.util.annotation.CatalogedBy;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/**
* Represents a key to an underlying {@link Value} such that the underlying
* value can be retrieved from a {@link ValueContainer}. For the key to be used
* through retrieval of {@link DataHolder}s, it's required to use a
* {@link DataRegistration} if the data is needed to be serialized, or dynamically
* provided for through external mechanisms, through {@link DataProvider}s.
*
* <p>If dynamic or persistent retention of the {@link Value Values} by
* {@link Key keys} is not desired, a registration with {@link DataRegistration}
* is optional. This would mean that any submitted {@link Value}s of a
* {@link Key} without an associated {@link DataRegistration} will be only
* stored on a
* {@link org.spongepowered.api.data.DataHolder.Mutable mutable DataHolder} for
* the duration that that holder exists. The value would not persist between
* reloads, restarts, etc.</p>
*
* @param <V> The type of {@link Value}
*/
@CatalogedBy(Keys.class)
public interface Key<V extends Value<?>, P extends DataProvider<V, ?>> extends CatalogType {
/**
* Creates a {@link Key.Builder} which allows creation of a {@link Key}
* to later be registered for accessing values from
* a {@link ValueContainer}. It is the default policy that a
* custom created {@link Key} is <strong>NOT PERSISTENT</strong> by
* Sponge. If custom keys for {@link DataHolder}s is desired to be
* persisted, a {@link DataRegistration} is required.
*
* <p>Registration of a custom created {@link Key} is required through
* {@link org.spongepowered.api.event.game.GameRegistryEvent.Register},
* though the creation is not restricted to any particular event. The
* registration of a {@link DataRegistration} is done separately.
* </p>
*
* @see DataRegistration
* @return The key builder
*/
@SuppressWarnings("unchecked")
static Builder<?, ?> builder() {
return Sponge.getRegistry().createBuilder(Builder.class);
}
/**
* Gets the class of the {@link Value} this {@link Key} is representing.
*
* @return The value class
*/
TypeToken<V> getValueToken();
/**
* Gets the class of the element of the {@link Value} this {@link Key}
* is representing. On occasion, if the element is a {@link Collection} type,
* one can occasionally use {@link TypeToken#resolveType(Type)} with
* {@link Class#getTypeParameters()} as the type parameter of a collection
* is retrievable, such as the element type parameter for {@link List} or
* {@link Map}.
*
* @return The element class
*/
TypeToken<?> getElementToken();
P getProvider();
/**
* Register an event listener which listens to the value the key accesses
* changing.
*
* @param holderFilter The data holder to filter with
* @param listener The event listener
* @param <E> The class type of the data holder
*/
<E extends DataHolder> void registerEvent(Class<E> holderFilter, EventListener<ChangeDataHolderEvent.ValueChange> listener);
interface Builder<E, V extends Value<E>> extends CatalogBuilder<Key<V, ?>, Builder<E, V>> {
/**
* Starter method for the builder, to be used immediately after
* {@link Key#builder()} is called. This defines the generics for the
* builder itself to provide the properly generified {@link Key}.
*
* <p>Common {@link TypeToken TypeTokens} can be found in
* {@link TypeTokens}. If a new TypeToken is to be created, it is
* recommended to create an anonymous class instance of a token,
* as recommended by Guava's wiki found
* <a href="https://github.com/google/guava/wiki/ReflectionExplained#introduction">here</a>.
* </p>
*
* <p>If the value type is a {@link BoundedValue} the
* {@link #boundedType(TypeToken)} method should be used instead.</p>
*
* @param token The type token, preferably an anonymous
* @param <T> The element type of the Key
* @param <B> The base value type of the key
* @return This builder, generified
*/
<T, B extends Value<T>> Builder<T, B> type(TypeToken<B> token);
/**
* Starter method for the bounded builder, to be used immediately after
* {@link Key#builder()} is called. This defines the generics for the
* builder itself to provide the properly generified {@link Key}.
*
* <p>Common {@link TypeToken TypeTokens} can be found in
* {@link TypeTokens}. If a new TypeToken is to be created, it is
* recommended to create an anonymous class instance of a token,
* as recommended by Guava's wiki found
* <a href="https://github.com/google/guava/wiki/ReflectionExplained#introduction">here</a>.
* </p>
*
* @param token The type token, preferably an anonymous
* @param <T> The element type of the Key
* @param <B> The base value type of the key
* @return This builder, generified
*/
<T, B extends BoundedValue<T>> BoundedBuilder<T, B> boundedType(TypeToken<B> token);
@Override
Builder<E, V> key(CatalogKey key);
/**
* Builds the {@link Key}.
*
* @return The built key
* @throws IllegalStateException If not all required options were specified;
* {@link #key(CatalogKey)} and {@link #type(TypeToken)}.
*/
@Override
Key<V, ?> build();
interface BoundedBuilder<E, V extends BoundedValue<E>> extends Builder<E, V> {
/**
* Sets the default minimum element.
*
* <p>Setting the minimum value is required.</p>
*
* @param minValue The minimum value
* @return This builder, for chaining
*/
BoundedBuilder<E, V> minValue(E minValue);
/**
* Sets the default minimum element supplier.
*
* <p>Use this method instead of {@link #minValue(Object)} if
* the element type {@code E} isn't immutable.</p>
*
* <p>Setting the minimum value is required.</p>
*
* @param supplier The minimum value supplier
* @return This builder, for chaining
*/
BoundedBuilder<E, V> minValueSupplier(Supplier<? extends E> supplier);
/**
* Sets the default maximum element.
*
* <p>Setting the maximum value is required.</p>
*
* @param minValue The minimum value
* @return This builder, for chaining
*/
BoundedBuilder<E, V> maxValue(E minValue);
/**
* Sets the default maximum element supplier.
*
* <p>Use this method instead of {@link #maxValue(Object)} if
* the element type {@code E} isn't immutable.</p>
*
* <p>Setting the maximum value is required.</p>
*
* @param supplier The maximum value supplier
* @return This builder, for chaining
*/
BoundedBuilder<E, V> maxValueSupplier(Supplier<? extends E> supplier);
/**
* Sets the {@link Comparator} that can be used to compare
* the elements.
*
* <p>Setting the comparator is a <strong>requirement</strong>
* if the element type isn't {@link Comparable}.</p>
*
* @param comparator The comparator
* @return This builder, for chaining
*/
BoundedBuilder<E, V> comparator(Comparator<? super E> comparator);
@Override
BoundedBuilder<E, V> key(CatalogKey key);
/**
* Builds the {@link Key}.
*
* @return The built key
* @throws IllegalStateException If not all required options were specified;
* {@link #key(CatalogKey)}, {@link #boundedType(TypeToken)},
* {@link #minValue(Object)}, {@link #maxValue(Object)} and
* {@link #comparator(Comparator)} if needed.
*/
@Override
Key<V, ?> build();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment