Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
personthecat.catlib.data.collections.LazyRegistry Maven / Gradle / Ivy
package personthecat.catlib.data.collections;
import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import personthecat.catlib.data.Lazy;
import personthecat.catlib.data.ResettableLazy;
import personthecat.catlib.exception.MissingElementException;
import javax.annotation.concurrent.ThreadSafe;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static personthecat.catlib.exception.Exceptions.missingElement;
/**
* A non-redundant set of objects which can only be written to the first time it is referenced.
* In OSV 6.2, this type was modified to support registry keys and be resettable. Resetting this
* registry is expected to be thread-safe, but may need further testing.
*
* This is the primary type of registry used in this mod. This registry is lazily initialized
* and cannot be modified. It is constructed with a data supplier which is then treated as an
* event. This event will fire at the first time any of this class' methods are called. It is
* equipped with each of the necessary overrides to be treated as a regular {@link Set}.
*
* Many of these methods are simply stubs which will throw an {@link UnsupportedOperationException}
* when called. They are not intended for use by external implementors.
*
* @param The type of key in the registry.
* @param The type of value in the registry.
*/
@ThreadSafe
@SuppressWarnings("unused")
public class LazyRegistry implements Map, Iterable {
/**
* The default error to throw on {@link LazyRegistry#getAsserted}
*/
private static final ErrorFunction DEFAULT_ERROR = k ->
missingElement("No value for key: {}", k);
/**
* An error to throw on {@link LazyRegistry#getAsserted}
*/
private final ErrorFunction err;
/**
* The underlying map whose contents will be filled on first use.
*/
private final Lazy> map;
/**
* Constructs Lazy ImmutableSet of objects which will be filled upon first use.
*
* @param map The underlying, lazily-initialized contents of this registry.
* @param err A function for generating missing element exceptions.
*/
private LazyRegistry(final Lazy> map, final ErrorFunction err) {
this.map = map;
this.err = err;
}
/**
* Generates a new, immutable registry when given an event which supplies the
* contents of this registry on first use.
*
* @param event Supplies the contents of this registry when it is first used.
* @param The type of key being stored in the registry.
* @param The type of value being stored in the registry.
* @return An immutable registry which cannot be refreshed.
*/
public static LazyRegistry of(final Supplier> event) {
return of(event, DEFAULT_ERROR::apply);
}
/**
* Variant of {@link #of(Supplier)} in which the data have already been supplied.
*
* @param map The contents of this registry, already available.
* @param The type of key being stored in the registry.
* @param The type of value being stored in the registry.
* @return An immutable registry which cannot be refreshed.
*/
public static LazyRegistry of(final Map map) {
return new LazyRegistry<>(new Lazy<>(Collections.unmodifiableMap(map)), DEFAULT_ERROR::apply);
}
/**
* Variant of {@link #of(Supplier)} which accepts a function for generating
* missing element exceptions.
*
* @param event Supplies the contents of this registry when it is first used.
* @param err A function for generating missing element exceptions.
* @param The type of key being stored in the registry.
* @param The type of value being stored in the registry.
* @return An immutable registry which cannot be refreshed.
*/
public static LazyRegistry of(final Supplier> event, final ErrorFunction err) {
return new LazyRegistry<>(new Lazy<>(() -> Collections.unmodifiableMap(event.get())), err);
}
/**
* Generates a new registry where keys are insignificant. Instead, the values
* will be enumerated.
*
* @param event Supplies the contents of this registry when it is first used.
* @param The type of value being stored in the registry.
* @return An immutable registry which cannot be refreshed.
*/
public static LazyRegistry enumerated(final Supplier> event) {
return enumerated(event, DEFAULT_ERROR::apply);
}
/**
* Variant of {@link #enumerated(Supplier)} in which the data have already been supplied.
*
* @param data The contents of this registry, already available.
* @param The type of value being stored in the registry.
* @return An immutable registry which cannot be refreshed.
*/
public static LazyRegistry enumerated(final Collection data) {
return new LazyRegistry<>(new Lazy<>(enumerate(data)), DEFAULT_ERROR::apply);
}
/**
* Variant of {@link #enumerated(Supplier)} which accepts a function for generating
* missing element exceptions.
*
* @param event Supplies the contents of this registry when it is first used.
* @param err A function for generating missing element exceptions.
* @param The type of value being stored in the registry.
* @return An immutable registry which cannot be refreshed.
*/
public static LazyRegistry enumerated(final Supplier> event, final ErrorFunction err) {
return of(() -> enumerate(event.get()), err);
}
/**
* Enumerates every value in a collection, mapping them to their index.
*
* @param values A collection of any type.
* @param The type of value being stored in the registry.
* @return An {@link ImmutableMap} containing the original values.
*/
private static ImmutableMap enumerate(final Collection values) {
final ImmutableMap.Builder map = ImmutableMap.builder();
int i = 0;
for (V value : values) {
map.put(i++, value);
}
return map.build();
}
/**
* A static utility for loading a series of multiple {@link LazyRegistry registries}.
*
* @param registries A series of registries being initialized at this time.
*/
public static void loadAll(final LazyRegistry, ?>... registries) {
for (final LazyRegistry, ?> registry : registries) {
registry.load();
}
}
/**
* A static utility for unloading a series of multiple {@link LazyRegistry registries}.
*
* Note that non-resettable registries will neither unload nor throw exceptions.
*
* @param registries A series of registries being unloaded at this time.
*/
public static void resetAll(final LazyRegistry, ?>... registries) {
for (final LazyRegistry, ?> registry : registries) {
registry.tryReset();
}
}
/**
* A static utility for reloading a series of multiple {@link LazyRegistry registries}.
*
* Note that non-resettable registries will neither reload nor throw exceptions.
*
* @param registries A series of registries being reloaded at this time.
*/
public static void reloadAll(final LazyRegistry, ?>... registries) {
for (final LazyRegistry, ?> registry : registries) {
registry.tryReload();
}
}
/**
* Generates a new registry containing the same data. The new registry will
* respond with this message when calling {@link #getAsserted(Object)}.
*
* @param err A function for generating missing element exceptions.
* @return A new registry with the updated error info.
*/
public LazyRegistry respondsWithError(final ErrorFunction err) {
return new LazyRegistry<>(this.map, err);
}
/**
* Variant of {@link #respondsWithError(ErrorFunction)} in which an error message
* is wrapped in a standard {@link MissingElementException}.
*
* @param msg A function for generating error messages.
* @return A new registry with the updated error info.
*/
public LazyRegistry respondsWith(final Function msg) {
return new LazyRegistry<>(this.map, k -> missingElement(msg.apply(k)));
}
/**
* Converts this registry into a resettable registry or explicitly marks it
* as non-resettable.
*
* @param resettable Whether this registry can now be reloaded.
* @return A new registry which can reloaded.
*/
public LazyRegistry canBeReset(final boolean resettable) {
if (resettable == this.map.isResettable()) {
return this;
}
return new LazyRegistry<>(this.map.asResettable(resettable), this.err);
}
/**
* Returns the value mapped to the given key, wrapped in {@link Optional}.
*
* @param k The key which an expected value is mapped to.
* @return The researched value, or else {@link Optional#empty}.
*/
public Optional getOptional(final K k) {
return Optional.ofNullable(this.get(k));
}
/**
* Variant of {@link #getOptional(Object)} which instead throws an exception if
* no value is mapped to the given key.
*
* @throws RuntimeException If no value is present.
* @param k The key which an expected value is mapped to.
* @return The researched value.
*/
public V getAsserted(final K k) {
final V v = this.get(k);
if (v == null) {
throw this.err.apply(k);
}
return v;
}
/**
* Returns a set of matching values in this registry for the given keys.
*
* @param ks The keys for each element being retrieved.
* @return A set containing the matching values in the registry.
*/
public Set tryGetAll(final Collection ks) {
final Set set = new HashSet<>();
ks.forEach(k -> this.getOptional(k).ifPresent(set::add));
return set;
}
/**
* Variant of {@link #tryGetAll(Collection)} which asserts that all keys are
* mapped in this registry.
*
* @param ks The keys for each element being retrieved.
* @return A set containing the matching values in the registry.
*/
public Set getAllAsserted(final Collection ks) {
return ks.stream().map(this::getAsserted).collect(Collectors.toSet());
}
/**
* Returns the value corresponding to whichever key matches the given predicate.
*
* @param predicate The predicate used to determine which key to accept.
* @return The expected value, or else {@link Optional#empty}.
*/
public Optional findByKey(final Predicate predicate) {
for (final Map.Entry entry : this.entrySet()) {
if (predicate.test(entry.getKey())) {
return Optional.of(entry.getValue());
}
}
return Optional.empty();
}
/**
* Returns the value corresponding to the given predicate.
*
* @param predicate The predicate used to determine which value to accept.
* @return The expected value, or else {@link Optional#empty()}.
*/
public Optional findByValue(final Predicate predicate) {
for (final V v : this.values()) {
if (predicate.test(v)) {
return Optional.of(v);
}
}
return Optional.empty();
}
/**
* Loads the underlying hash table immediately. This can be called after
* constructing the registry to have it load on construction instead of
* lazily.
*
* For example,
* {@code
* final SafeRegistry ITEMS =
* SafeRegistry.of(ItemInit::loadItems)
* .canBeReset(true)
* .load();
* }
*
* @return this
, for method chaining.
*/
public LazyRegistry load() {
this.map.get();
return this;
}
/**
* Attempts to reset the underlying map contained within the registry. If
* the map cannot be reset, nothing will happen.
*
* @return this
, for method chaining.
*/
public LazyRegistry tryReset() {
if (this.map.isResettable()) {
((ResettableLazy>) this.map).reset();
}
return this;
}
/**
* Variant of {@link #tryReset} which asserts that the registry can be reset.
*
* @throws UnsupportedOperationException If the registry is not resettable.
* @return this
, for method chaining.
*/
public LazyRegistry reset() {
if (this.map.isResettable()) {
((ResettableLazy>) this.map).reset();
return this;
}
throw new UnsupportedOperationException();
}
/**
* Attempts to reload the data in this registry. If the underlying map cannot
* be reset, nothing will happen.
*
* @return this
, for method chaining.
*/
public LazyRegistry tryReload() {
return this.tryReset().load();
}
/**
* Variant of {@link #tryReload} which asserts that the registry can be reset.
*
* @throws UnsupportedOperationException If the registry is not resettable.
* @return this
, for method chaining.
*/
public LazyRegistry reload() {
return this.reset().load();
}
/**
* Recalculates and returns the up to date data contained within the registry.
*
* @return The recalculated data corresponding to this registry.
*/
public Map getUpdated() {
return this.map.getUpdated();
}
/**
* Determines whether this registry has been accessed or finished loading.
*
* @return true
, if the registry has loaded.
*/
public boolean hasLoaded() {
return this.map.computed();
}
public Stream stream() {
return this.values().stream();
}
@Override
public int size() {
return this.map.get().size();
}
@Override
public boolean isEmpty() {
return this.map.get().isEmpty();
}
@Override
public boolean containsKey(final Object o) {
return this.map.get().containsKey(o);
}
@Override
public boolean containsValue(final Object o) {
return this.map.get().containsValue(o);
}
@Nullable
@Override
public V get(final Object o) {
return this.map.get().get(o);
}
@NotNull
@Override
public Set keySet() {
return this.map.get().keySet();
}
@NotNull
@Override
public Collection values() {
return this.map.get().values();
}
@NotNull
@Override
public Set> entrySet() {
return this.map.get().entrySet();
}
@NotNull
@Override
public Iterator iterator() {
return this.map.get().values().iterator();
}
@Nullable
@Override
@Deprecated
public V put(final K k, final V v) {
throw new UnsupportedOperationException();
}
@Override
@Deprecated
public V remove(final Object o) {
throw new UnsupportedOperationException();
}
@Override
@Deprecated
public void putAll(final @NotNull Map extends K, ? extends V> map) {
throw new UnsupportedOperationException();
}
@Override
@Deprecated
public void clear() {
throw new UnsupportedOperationException();
}
@FunctionalInterface
public interface ErrorFunction {
RuntimeException apply(final K k);
}
}