/* * This file is part of LanternServer, licensed under the MIT License (MIT). * * Copyright (c) LanternPowered <https://www.lanternpowered.org> * 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.lanternpowered.server.data.value; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import org.lanternpowered.server.data.value.immutable.ImmutableLanternEntityValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternItemValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternListValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternMapValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternOptionalValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternPatternListValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternSetValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternValue; import org.lanternpowered.server.data.value.immutable.ImmutableLanternWeightedCollectionValue; import org.lanternpowered.server.data.value.mutable.LanternBoundedValue; import org.lanternpowered.server.data.value.mutable.LanternEntityValue; import org.lanternpowered.server.data.value.mutable.LanternItemValue; import org.lanternpowered.server.data.value.mutable.LanternListValue; import org.lanternpowered.server.data.value.mutable.LanternMapValue; import org.lanternpowered.server.data.value.mutable.LanternOptionalValue; import org.lanternpowered.server.data.value.mutable.LanternPatternListValue; import org.lanternpowered.server.data.value.mutable.LanternSetValue; import org.lanternpowered.server.data.value.mutable.LanternValue; import org.lanternpowered.server.data.value.mutable.LanternWeightedCollectionValue; import org.spongepowered.api.data.key.Key; import org.spongepowered.api.data.meta.PatternLayer; import org.spongepowered.api.data.value.BaseValue; import org.spongepowered.api.data.value.BoundedValue; import org.spongepowered.api.data.value.ValueFactory; import org.spongepowered.api.data.value.immutable.ImmutableListValue; import org.spongepowered.api.data.value.immutable.ImmutableMapValue; import org.spongepowered.api.data.value.immutable.ImmutableOptionalValue; import org.spongepowered.api.data.value.immutable.ImmutablePatternListValue; import org.spongepowered.api.data.value.immutable.ImmutableSetValue; import org.spongepowered.api.data.value.immutable.ImmutableValue; import org.spongepowered.api.data.value.immutable.ImmutableWeightedCollectionValue; import org.spongepowered.api.data.value.mutable.ListValue; import org.spongepowered.api.data.value.mutable.MapValue; import org.spongepowered.api.data.value.mutable.MutableBoundedValue; import org.spongepowered.api.data.value.mutable.OptionalValue; import org.spongepowered.api.data.value.mutable.PatternListValue; import org.spongepowered.api.data.value.mutable.SetValue; import org.spongepowered.api.data.value.mutable.Value; import org.spongepowered.api.data.value.mutable.WeightedCollectionValue; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.util.weighted.WeightedTable; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; @SuppressWarnings("unchecked") public class LanternValueFactory implements ValueFactory { private static LanternValueFactory instance = new LanternValueFactory(); public static LanternValueFactory getInstance() { return instance; } private final Map<Class<?>, ValueSupplier> valueSuppliers = new IdentityHashMap<>(); private final Map<Key<?>, SimpleKeyRegistration> keyRegistrations = new HashMap<>(); private final static Comparator<Integer> COMPARATOR = Integer::compare; private LanternValueFactory() { this.registerSupplier(Value.class, ValueSupplier.<Value, Object>of( (key, element) -> { if (element instanceof ItemStack) { return new LanternItemValue(key, (ItemStack) element); } else if (element instanceof Entity) { return new LanternEntityValue<>(key, (Entity) element); } else { return new LanternValue<>(key, element); } }, (key, element, defElement) -> { if (element instanceof ItemStack) { return new LanternItemValue(key, (ItemStack) defElement, (ItemStack) element); } else if (element instanceof Entity) { return new LanternEntityValue<>(key, (Entity) element); } else { return new LanternValue<>(key, defElement, element); } })); this.registerSupplier(MutableBoundedValue.class, ValueSupplier.of( (key, element) -> { if (key.getElementToken().getRawType() == Integer.class) { return new LanternBoundedValue(key, element, COMPARATOR, -Integer.MAX_VALUE, Integer.MAX_VALUE); } throw new IllegalStateException(); }, (key, element, defElement) -> { if (key.getElementToken().getRawType() == Integer.class) { return new LanternBoundedValue(key, element, COMPARATOR, -Integer.MAX_VALUE, Integer.MAX_VALUE); } throw new IllegalStateException(); })); this.registerSupplier(ListValue.class, ValueSupplier.<ListValue, List<Object>>of( LanternListValue::new, (key, element, defElement) -> new LanternListValue<>(key, defElement, element))); this.registerSupplier(SetValue.class, ValueSupplier.<SetValue, Set<Object>>of( LanternSetValue::new, (key, element, defElement) -> new LanternSetValue<>(key, defElement, element))); this.registerSupplier(MapValue.class, ValueSupplier.<MapValue, Map<Object, Object>>of( LanternMapValue::new, (key, element, defElement) -> new LanternMapValue<>(key, defElement, element))); this.registerSupplier(OptionalValue.class, ValueSupplier.<OptionalValue, Optional<Object>>of( LanternOptionalValue::new, (key, element, defElement) -> new LanternOptionalValue(key, defElement, element))); this.registerSupplier(PatternListValue.class, ValueSupplier.<PatternListValue, List<PatternLayer>>of( LanternPatternListValue::new, (key, element, defElement) -> new LanternPatternListValue(key, defElement, element))); this.registerSupplier(WeightedCollectionValue.class, ValueSupplier.<WeightedCollectionValue, WeightedTable<?>>of( LanternWeightedCollectionValue::new, (key, element, defElement) -> new LanternWeightedCollectionValue(key, defElement, element))); this.registerSupplier(ImmutableValue.class, ValueSupplier.<ImmutableValue, Object>of( (key, element) -> { if (element instanceof ItemStack) { return new ImmutableLanternItemValue(key, (ItemStack) element); } else if (element instanceof Entity) { return new ImmutableLanternEntityValue<>(key, (Entity) element); } else { return new ImmutableLanternValue<>(key, element); } }, (key, element, defElement) -> { if (element instanceof ItemStack) { return new ImmutableLanternItemValue(key, (ItemStack) defElement, (ItemStack) element); } else if (element instanceof Entity) { return new ImmutableLanternEntityValue<>(key, (Entity) element); } else { return new ImmutableLanternValue<>(key, defElement, element); } })); this.registerSupplier(ImmutableListValue.class, ValueSupplier.<ImmutableListValue, List<Object>>of( ImmutableLanternListValue::new, (key, element, defElement) -> new ImmutableLanternListValue<>(key, defElement, element))); this.registerSupplier(ImmutableSetValue.class, ValueSupplier.<ImmutableSetValue, Set<Object>>of( ImmutableLanternSetValue::new, (key, element, defElement) -> new ImmutableLanternSetValue<>(key, defElement, element))); this.registerSupplier(ImmutableMapValue.class, ValueSupplier.<ImmutableMapValue, Map<Object, Object>>of( ImmutableLanternMapValue::new, (key, element, defElement) -> new ImmutableLanternMapValue<>(key, defElement, element))); this.registerSupplier(ImmutableOptionalValue.class, ValueSupplier.<ImmutableOptionalValue, Optional<Object>>of( ImmutableLanternOptionalValue::new, (key, element, defElement) -> new ImmutableLanternOptionalValue(key, defElement, element))); this.registerSupplier(ImmutablePatternListValue.class, ValueSupplier.<ImmutablePatternListValue, List<PatternLayer>>of( ImmutableLanternPatternListValue::new, (key, element, defElement) -> new ImmutableLanternPatternListValue(key, defElement, element))); this.registerSupplier(ImmutableWeightedCollectionValue.class, ValueSupplier.<ImmutableWeightedCollectionValue, WeightedTable<?>>of( ImmutableLanternWeightedCollectionValue::new, (key, element, defElement) -> new ImmutableLanternWeightedCollectionValue(key, defElement, element))); } public <V extends BaseValue<E>, E> KeyRegistration<V, E> registerKey(Key<? extends V> key) { SimpleKeyRegistration<V, E> registration = new SimpleKeyRegistration.MultipleProcessors<>(key); this.keyRegistrations.put(key, registration); return registration; } @Nullable public <V extends BaseValue<E>, E> KeyRegistration<V, E> getKeyRegistration(Key<? extends V> key) { return this.keyRegistrations.get(checkNotNull(key, "key")); } public <R extends BaseValue, E> void registerSupplier(Class<R> type, ValueSupplier<R, E> supplier) { this.valueSuppliers.put(type, supplier); } /** * Creates a {@link Value} with the proper type for the specified {@link Key}. * * @param key the key to create a value for * @param element the element (object) that is stored in the value * @param <V> the value type * @param <E> the element type * @return the value instance */ public <V extends BaseValue<E>, E> V createValueForKey(Key<? extends V> key, E element) { checkNotNull(key, "key"); checkNotNull(element, "element"); final ValueSupplier supplier = this.valueSuppliers.get(key.getValueToken().getRawType()); checkArgument(supplier != null, "The BaseValue type used by the key (" + key.getValueToken().getRawType().getName() + ") isn't supported."); return (V) supplier.get(key, element); } /** * Creates a {@link Value} with the proper type for the specified {@link Key}. * * @param key the key to create a value for * @param element the element (object) that is stored in the value * @param defaultElement the default element (object) that is stored in the value * @param <V> the value type * @param <E> the element type * @return the value instance */ public <V extends BaseValue<E>, E> V createValueForKey(Key<V> key, E element, E defaultElement) { checkNotNull(key, "key"); checkNotNull(element, "element"); ValueSupplier supplier = this.valueSuppliers.get(key.getValueToken().getRawType()); checkArgument(supplier != null, "The BaseValue type used by the key (" + key.getValueToken().getRawType().getName() + ") isn't supported."); return (V) supplier.get(key, element, defaultElement); } @Override public <E> Value<E> createValue(Key<Value<E>> key, E element) { return new LanternValue<>(checkNotNull(key, "key"), checkNotNull(element, "element")); } @Override public <E> Value<E> createValue(Key<Value<E>> key, E element, E defaultValue) { return new LanternValue<>(checkNotNull(key, "key"), checkNotNull(defaultValue, "defaultValue"), checkNotNull(element, "element")); } @Override public <E> ListValue<E> createListValue(Key<ListValue<E>> key, List<E> elements) { return new LanternListValue<>(checkNotNull(key, "key"), checkNotNull(elements, "elements")); } @Override public <E> ListValue<E> createListValue(Key<ListValue<E>> key, List<E> elements, List<E> defaults) { return new LanternListValue<>(checkNotNull(key, "key"), checkNotNull(defaults, "defaults"), checkNotNull(elements, "elements")); } @Override public <E> SetValue<E> createSetValue(Key<SetValue<E>> key, Set<E> elements) { return new LanternSetValue<>(checkNotNull(key, "key"), checkNotNull(elements, "elements")); } @Override public <E> SetValue<E> createSetValue(Key<SetValue<E>> key, Set<E> elements, Set<E> defaults) { return new LanternSetValue<>(checkNotNull(key, "key"), checkNotNull(defaults, "defaults"), checkNotNull(elements, "elements")); } @Override public <K, V> MapValue<K, V> createMapValue(Key<MapValue<K, V>> key, Map<K, V> map) { return new LanternMapValue<>(checkNotNull(key, "key"), checkNotNull(map, "map")); } @Override public <K, V> MapValue<K, V> createMapValue(Key<MapValue<K, V>> key, Map<K, V> map, Map<K, V> defaults) { return new LanternMapValue<>(checkNotNull(key, "key"), checkNotNull(defaults, "defaults"), checkNotNull(map, "map")); } @Override public <E> BoundedValueBuilder<E> createBoundedValueBuilder(Key<MutableBoundedValue<E>> key) { return new LanternBoundedValueBuilder<>(checkNotNull(key, "key")); } @Override public <E> OptionalValue<E> createOptionalValue(Key<OptionalValue<E>> key, @Nullable E element) { return new LanternOptionalValue<>(checkNotNull(key, "key"), Optional.empty(), Optional.ofNullable(element)); } @Override public <E> OptionalValue<E> createOptionalValue(Key<OptionalValue<E>> key, @Nullable E element, E defaultElement) { return new LanternOptionalValue<>(checkNotNull(key, "key"), Optional.of(defaultElement), Optional.ofNullable(element)); } public static <E> BoundedValueBuilder<E> boundedBuilder(Key<? extends BoundedValue<E>> key) { return new LanternBoundedValueBuilder<>(checkNotNull(key)); } public static final class LanternBoundedValueBuilder<E> implements BoundedValueBuilder<E> { private final Key<? extends BoundedValue<E>> key; private Comparator<E> comparator; private E minimum; private E maximum; private E defaultValue; @Nullable private E value; public LanternBoundedValueBuilder(Key<? extends BoundedValue<E>> key) { this.key = checkNotNull(key); } @Override public BoundedValueBuilder<E> comparator(Comparator<E> comparator) { this.comparator = checkNotNull(comparator); return this; } @SuppressWarnings("unchecked") @Override public BoundedValueBuilder<E> minimum(E minimum) { this.minimum = checkNotNull(minimum); //noinspection ConstantConditions if (this.comparator == null && minimum instanceof Comparable) { this.comparator = (o1, o2) -> ((Comparable<E>) o1).compareTo(o2); } return this; } @SuppressWarnings("unchecked") @Override public BoundedValueBuilder<E> maximum(E maximum) { this.maximum = checkNotNull(maximum); //noinspection ConstantConditions if (this.comparator == null && maximum instanceof Comparable) { this.comparator = (o1, o2) -> ((Comparable<E>) o1).compareTo(o2); } return this; } @Override public BoundedValueBuilder<E> defaultValue(E defaultValue) { this.defaultValue = checkNotNull(defaultValue); return this; } @Override public BoundedValueBuilder<E> actualValue(E value) { this.value = checkNotNull(value); return this; } @Override public LanternBoundedValue<E> build() { checkState(this.comparator != null); checkState(this.minimum != null); checkState(this.maximum != null); checkState(this.defaultValue != null); if (this.value == null) { return new LanternBoundedValue<>(this.key, this.defaultValue, this.comparator, this.minimum, this.maximum); } else { return new LanternBoundedValue<>(this.key, this.defaultValue, this.comparator, this.minimum, this.maximum, this.value); } } } }