/*
* 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.immutable;
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.ManipulatorHelper;
import org.lanternpowered.server.data.manipulator.mutable.IDataManipulator;
import org.lanternpowered.server.data.value.AbstractValueContainer;
import org.lanternpowered.server.data.value.KeyRegistration;
import org.spongepowered.api.data.DataContainer;
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.persistence.AbstractDataBuilder;
import org.spongepowered.api.data.persistence.InvalidDataException;
import org.spongepowered.api.data.value.BaseValue;
import org.spongepowered.api.data.value.immutable.ImmutableValue;
import org.spongepowered.api.data.value.mutable.Value;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public abstract class AbstractImmutableData<I extends ImmutableDataManipulator<I, M>, M extends DataManipulator<M, I>>
implements AbstractValueContainer<I, M>, IImmutableDataManipulator<I, M> {
private final Map<Key<?>, KeyRegistration> rawValueMap;
private final Map<Key<?>, Optional> cachedValues = new ConcurrentHashMap<>();
private final Map<Key<?>, Optional> cachedImmutableValues = new ConcurrentHashMap<>();
private final Class<M> manipulatorType;
private final Class<I> immutableManipulatorType;
public AbstractImmutableData(Class<I> immutableManipulatorType, Class<M> manipulatorType) {
this.immutableManipulatorType = immutableManipulatorType;
this.manipulatorType = manipulatorType;
this.rawValueMap = new HashMap<>();
registerKeys();
}
public AbstractImmutableData(M manipulator) {
//noinspection unchecked
final IDataManipulator<M, I> iDataManipulator = (IDataManipulator<M, I>) 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 <E, V extends BaseValue<E>> Optional<V> getValue(Key<V> key) {
// Cache the values if needed, to avoid unneeded object creation
return (Optional) this.cachedValues.computeIfAbsent(checkNotNull(key, "key"),
key1 -> (Optional) AbstractValueContainer.super.getValue((Key) key1));
}
@Override
public <E, R extends ImmutableValue<E>> Optional<R> getImmutableValue(Key<? extends BaseValue<E>> key) {
//noinspection unchecked
return this.cachedImmutableValues.computeIfAbsent(key, key1 -> {
//noinspection unchecked
final Optional<? extends BaseValue<E>> optValue = AbstractValueContainer.super.getValue((Key) key1);
if (optValue.isPresent()) {
if (optValue.get() instanceof ImmutableValue) {
return optValue;
} else {
return Optional.of(((Value<E>) optValue.get()).asImmutable());
}
}
return Optional.empty();
});
}
@Override
public M asMutable() {
final DataManipulatorRegistration<M, I> registration = DataManipulatorRegistry.get().getByImmutable(this.immutableManipulatorType).get();
//noinspection unchecked
return registration.getImmutableToMutableFunction().apply((I) this);
}
@Override
public int getContentVersion() {
return 1;
}
@Override
public DataContainer toContainer() {
return ManipulatorHelper.toContainer(this);
}
@Override
public Class<I> getImmutableType() {
return this.immutableManipulatorType;
}
@Override
public Class<M> getMutableType() {
return this.manipulatorType;
}
public static abstract class AbstractImmutableManipulatorDataBuilder<T extends AbstractImmutableData> extends AbstractDataBuilder<T> {
protected AbstractImmutableManipulatorDataBuilder(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();
}
}