/* * 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.manipulator.mutable; import static com.google.common.base.Preconditions.checkNotNull; import org.lanternpowered.server.data.manipulator.DataManipulatorRegistration; import org.lanternpowered.server.data.manipulator.DataManipulatorRegistry; import org.lanternpowered.server.data.manipulator.IDataManipulatorBase; import org.lanternpowered.server.data.manipulator.ManipulatorHelper; import org.lanternpowered.server.data.manipulator.immutable.IImmutableDataManipulator; import org.lanternpowered.server.data.value.AbstractValueContainer; import org.lanternpowered.server.data.value.ElementHolder; import org.lanternpowered.server.data.value.KeyRegistration; import org.lanternpowered.server.data.value.LanternValueFactory; import org.lanternpowered.server.data.value.processor.ValueProcessor; import org.lanternpowered.server.util.functions.TriFunction; import org.spongepowered.api.data.DataContainer; import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.DataView; import org.spongepowered.api.data.key.Key; import org.spongepowered.api.data.manipulator.DataManipulator; import org.spongepowered.api.data.manipulator.ImmutableDataManipulator; import org.spongepowered.api.data.merge.MergeFunction; import org.spongepowered.api.data.persistence.AbstractDataBuilder; import org.spongepowered.api.data.persistence.InvalidDataException; import org.spongepowered.api.data.value.BaseValue; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; public abstract class AbstractData<M extends DataManipulator<M, I>, I extends ImmutableDataManipulator<I, M>> implements AbstractValueContainer<M, I>, IDataManipulator<M, I> { private final Map<Key<?>, KeyRegistration> rawValueMap; private final Class<M> manipulatorType; private final Class<I> immutableManipulatorType; public AbstractData(Class<M> manipulatorType, Class<I> immutableManipulatorType) { this.immutableManipulatorType = immutableManipulatorType; this.manipulatorType = manipulatorType; this.rawValueMap = new HashMap<>(); registerKeys(); } public AbstractData(I manipulator) { //noinspection unchecked this((IDataManipulatorBase<M, I>) manipulator); } public AbstractData(M manipulator) { //noinspection unchecked this((IDataManipulatorBase<M, I>) manipulator); } protected AbstractData(IDataManipulatorBase<M, I> manipulator) { //noinspection unchecked final IImmutableDataManipulator<I, M> iDataManipulator = (IImmutableDataManipulator<I, M>) manipulator; this.immutableManipulatorType = iDataManipulator.getImmutableType(); this.manipulatorType = iDataManipulator.getMutableType(); if (manipulator instanceof AbstractValueContainer) { //noinspection unchecked this.rawValueMap = ((AbstractValueContainer) manipulator).copyRawValueMap(); } else { throw new IllegalArgumentException( "The default DataManipulator's should extend AbstractValueContainer, others are currently unsupported."); } } @Override public Map<Key<?>, KeyRegistration> getRawValueMap() { return this.rawValueMap; } @SuppressWarnings("unchecked") @Override public Optional<M> fill(DataHolder dataHolder, MergeFunction overlap) { return Optional.of(checkNotNull(overlap, "overlap").merge(copy(), dataHolder.get(this.manipulatorType).orElse(null))); } @Override public Optional<M> from(DataContainer container) { return Optional.empty(); } private void throwUnsupportedKeyException(Key<?> key) { throw new IllegalArgumentException("This data manipulator doesn't support the following key: " + key.toString()); } @SuppressWarnings("unchecked") private <E> M setWith(Key<? extends BaseValue<E>> key, E value, ValueProcessor<BaseValue<E>, E> processor) { ((TriFunction) processor.getOfferHandler()).apply(key, this, value); return (M) this; } @SuppressWarnings("unchecked") @Override public <E> M set(Key<? extends BaseValue<E>> key, E value) { checkNotNull(key, "key"); checkNotNull(value, "value"); // Check the local key registration KeyRegistration<BaseValue<E>, E> localKeyRegistration = getKeyRegistration(key); if (localKeyRegistration == null) { if (requiresKeyRegistration()) { throwUnsupportedKeyException(key); } } else { List<ValueProcessor<BaseValue<E>, E>> processors = localKeyRegistration.getValueProcessors(); if (!processors.isEmpty()) { return setWith(key, value, processors.get(0)); } } // Check the global key registrations KeyRegistration<BaseValue<E>, E> keyRegistration = LanternValueFactory.getInstance().getKeyRegistration(key); if (keyRegistration != null) { for (ValueProcessor<BaseValue<E>, E> valueProcessor : keyRegistration.getValueProcessors()) { if (valueProcessor.getApplicableTester().test((Key) key, this)) { return setWith(key, value, valueProcessor); } } } // Use the global processor if (localKeyRegistration != null && localKeyRegistration instanceof ElementHolder) { return setWith(key, value, ValueProcessor.getDefaultAttachedValueProcessor()); } throwUnsupportedKeyException(key); return (M) this; } @SuppressWarnings("unchecked") private <E> M transformWith(Key<? extends BaseValue<E>> key, Function<E, E> function, ValueProcessor<BaseValue<E>, E> processor) { ((TriFunction) processor.getOfferHandler()).apply(key, this, function.apply( (E) ((Optional) ((BiFunction) processor.getRetrieveHandler()).apply(key, this)).orElse(null))); return (M) this; } @SuppressWarnings("unchecked") @Override public <E> M transform(Key<? extends BaseValue<E>> key, Function<E, E> function) { checkNotNull(key, "key"); checkNotNull(function, "function"); // Check the local key registration KeyRegistration<BaseValue<E>, E> localKeyRegistration = getKeyRegistration(key); if (localKeyRegistration == null) { if (requiresKeyRegistration()) { throwUnsupportedKeyException(key); } } else { List<ValueProcessor<BaseValue<E>, E>> processors = localKeyRegistration.getValueProcessors(); if (!processors.isEmpty()) { return transformWith(key, function, processors.get(0)); } } // Check the global key registrations KeyRegistration<BaseValue<E>, E> keyRegistration = LanternValueFactory.getInstance().getKeyRegistration(key); if (keyRegistration != null) { for (ValueProcessor<BaseValue<E>, E> valueProcessor : keyRegistration.getValueProcessors()) { if (valueProcessor.getApplicableTester().test((Key) key, this)) { return transformWith(key, function, valueProcessor); } } } // Use the global processor if (localKeyRegistration != null && localKeyRegistration instanceof ElementHolder) { return transformWith(key, function, ValueProcessor.getDefaultAttachedValueProcessor()); } throwUnsupportedKeyException(key); return (M) this; } @Override public I asImmutable() { final DataManipulatorRegistration<M, I> registration = DataManipulatorRegistry.get().getByImmutable(this.immutableManipulatorType).get(); //noinspection unchecked return registration.getMutableToImmutableFunction().apply((M) this); } @Override public int getContentVersion() { return 1; } @SuppressWarnings("unchecked") @Override public DataContainer toContainer() { return ManipulatorHelper.toContainer(this); } @Override public M copy() { final DataManipulatorRegistration<M, I> registration = DataManipulatorRegistry.get().getByImmutable(this.immutableManipulatorType).get(); //noinspection unchecked return registration.getManipulatorCopyFunction().apply((M) this); } @Override public Class<I> getImmutableType() { return this.immutableManipulatorType; } @Override public Class<M> getMutableType() { return this.manipulatorType; } public static abstract class AbstractManipulatorDataBuilder<T extends AbstractData> extends AbstractDataBuilder<T> { protected AbstractManipulatorDataBuilder(Class<T> requiredClass, int supportedVersion) { super(requiredClass, supportedVersion); } @SuppressWarnings("unchecked") @Override protected Optional<T> buildContent(DataView container) throws InvalidDataException { return ManipulatorHelper.buildContent(container, this::buildManipulator); } protected abstract T buildManipulator(); } }