org.lanternpowered.server.data.IValueContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.lanternpowered.server.data.IValueContainer.java

Source

/*
 * 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;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import org.lanternpowered.server.data.processor.ValueProcessorKeyRegistration;
import org.lanternpowered.server.data.processor.Processor;
import org.lanternpowered.server.data.value.LanternValueFactory;
import org.lanternpowered.server.data.value.ValueHelper;
import org.spongepowered.api.data.key.Key;
import org.spongepowered.api.data.value.BaseValue;
import org.spongepowered.api.data.value.ValueContainer;
import org.spongepowered.api.data.value.immutable.ImmutableValue;

import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.StreamSupport;

@SuppressWarnings("unchecked")
public interface IValueContainer<C extends ValueContainer<C>> extends ValueContainer<C>, IValueHolder {

    /**
     * Converts the {@link BaseValue}s of the {@link ValueContainer} into a nicely
     * formatted {@code String}.
     *
     * @param valueContainer The value container
     * @return The string
     */
    static String valuesToString(ValueContainer<?> valueContainer) {
        return valuesToString(valueContainer.getValues());
    }

    /**
     * Converts the {@link BaseValue}s into a nicely
     * formatted {@code String}.
     *
     * @param values The values
     * @return The string
     */
    static String valuesToString(Iterable<? extends BaseValue<?>> values) {
        return Arrays.toString(StreamSupport.stream(values.spliterator(), false).map(
                e -> MoreObjects.toStringHelper("").add("key", e.getKey().getId()).add("value", e.get()).toString())
                .toArray());
    }

    /**
     * Matches the contents of the two {@link ValueContainer}s.
     *
     * @param valueContainerA The first value container
     * @param valueContainerB The second value container
     * @return Whether the contents match
     */
    static boolean matchContents(IValueContainer<?> valueContainerA, IValueContainer<?> valueContainerB) {
        final boolean additional = valueContainerA instanceof IAdditionalCompositeValueStore;
        if (additional != valueContainerB instanceof IAdditionalCompositeValueStore) {
            return false;
        }

        final ValueCollection valueCollectionA = valueContainerA.getValueCollection();
        final ValueCollection valueCollectionB = valueContainerB.getValueCollection();

        final Collection<Key<?>> keysA = valueCollectionA.getKeys();
        final Collection<Key<?>> keysB = valueCollectionB.getKeys();

        // The same keys have to be present in both of the containers
        if (keysA.size() != keysB.size() || !keysA.containsAll(keysB)) {
            return false;
        }

        for (KeyRegistration<?, ?> registration1 : valueCollectionA.getAll()) {
            final KeyRegistration registration2 = (KeyRegistration) valueCollectionB
                    .get((Key) registration1.getKey()).get();
            // Get the values from both of the containers and match them
            final Object value1 = ((Processor) registration1).getFrom(valueContainerA).orElse(null);
            final Object value2 = ((Processor) registration2).getFrom(valueContainerB).orElse(null);
            if (!Objects.equals(value1, value2)) {
                return false;
            }
        }

        // Match additional containers
        if (additional) {
            final Map<Class<?>, ValueContainer<?>> mapA = ((IAdditionalCompositeValueStore) valueContainerA)
                    .getAdditionalContainers().getMap();
            final Map<Class<?>, ValueContainer<?>> mapB = ((IAdditionalCompositeValueStore) valueContainerB)
                    .getAdditionalContainers().getMap();
            if (mapA.size() != mapB.size() || !mapA.keySet().containsAll(mapB.keySet())) {
                return false;
            }
            for (Map.Entry<Class<?>, ValueContainer<?>> entry : mapA.entrySet()) {
                final ValueContainer<?> containerA = entry.getValue();
                final ValueContainer<?> containerB = mapB.get(entry.getKey());
                if (!Objects.equals(containerA, containerB)) {
                    return false;
                }
            }
        }

        return true;
    }

    @Override
    default <E, V extends BaseValue<E>> Optional<V> getValue(Key<V> key) {
        return IValueHolder.super.getValueFor(key);
    }

    @SuppressWarnings("unchecked")
    @Override
    default boolean supports(Key<?> key) {
        checkNotNull(key, "key");

        // Check the local key registration
        final KeyRegistration<?, ?> localKeyRegistration = (KeyRegistration<?, ?>) getValueCollection()
                .get((Key) key).orElse(null);
        if (localKeyRegistration != null) {
            return ((Processor<BaseValue<?>, ?>) localKeyRegistration).isApplicableTo(this);
        }

        // Check for a global registration
        final Optional<ValueProcessorKeyRegistration> globalRegistration = LanternValueFactory.get()
                .getKeyRegistration((Key) key);
        if (globalRegistration.isPresent()) {
            return ((Processor<BaseValue<?>, ?>) globalRegistration.get()).isApplicableTo(this);
        }

        // Check if custom data is supported by this container
        if (this instanceof AdditionalContainerHolder) {
            // Check for the custom value containers
            final AdditionalContainerCollection<?> containers = ((AdditionalContainerHolder<?>) this)
                    .getAdditionalContainers();
            for (ValueContainer<?> valueContainer : containers.getAll()) {
                if (valueContainer.supports(key)) {
                    return true;
                }
            }
        }

        return false;
    }

    @SuppressWarnings("unchecked")
    @Override
    default <E> Optional<E> get(Key<? extends BaseValue<E>> key) {
        checkNotNull(key, "key");

        // Check the local key registration
        final KeyRegistration<BaseValue<E>, E> localKeyRegistration = getValueCollection().get(key).orElse(null);
        if (localKeyRegistration != null) {
            return ((Processor<BaseValue<E>, E>) localKeyRegistration).getFrom(this);
        }

        // Check for a global registration
        final Optional<ValueProcessorKeyRegistration<BaseValue<E>, E>> globalRegistration = LanternValueFactory
                .get().getKeyRegistration(key);
        if (globalRegistration.isPresent()) {
            return ((Processor<BaseValue<E>, E>) globalRegistration.get()).getFrom(this);
        }

        // Check if custom data is supported by this container
        if (this instanceof AdditionalContainerHolder) {
            // Check for the custom value containers
            final AdditionalContainerCollection<?> containers = ((AdditionalContainerHolder<?>) this)
                    .getAdditionalContainers();
            for (ValueContainer<?> valueContainer : containers.getAll()) {
                if (valueContainer.supports(key)) {
                    return valueContainer.get(key);
                }
            }
        }

        return Optional.empty();
    }

    @SuppressWarnings("unchecked")
    @Override
    default <E, V extends BaseValue<E>> Optional<V> getRawValueFor(Key<V> key) {
        // Check the local key registration
        final KeyRegistration<BaseValue<E>, E> localKeyRegistration = getValueCollection().get(key).orElse(null);
        if (localKeyRegistration != null) {
            return ((Processor<V, E>) localKeyRegistration).getValueFrom(this);
        }

        // Check for a global registration
        final Optional<ValueProcessorKeyRegistration<V, E>> globalRegistration = LanternValueFactory.get()
                .getKeyRegistration(key);
        if (globalRegistration.isPresent()) {
            return ((Processor<V, E>) globalRegistration.get()).getValueFrom(this);
        }

        // Check if custom data is supported by this container
        if (this instanceof AdditionalContainerHolder) {
            // Check for the custom value containers
            final AdditionalContainerCollection<?> containers = ((AdditionalContainerHolder<?>) this)
                    .getAdditionalContainers();
            for (ValueContainer<?> valueContainer : containers.getAll()) {
                if (valueContainer.supports(key)) {
                    return valueContainer.getValue(key);
                }
            }
        }

        return Optional.empty();
    }

    @SuppressWarnings("unchecked")
    @Override
    default Set<Key<?>> getKeys() {
        final ImmutableSet.Builder<Key<?>> keys = ImmutableSet.builder();

        // Check local registrations
        keys.addAll(getValueCollection().getKeys());

        // Check for global registrations
        LanternValueFactory.get().getKeyRegistrations().stream()
                .filter(registration -> ((Processor<BaseValue<?>, ?>) registration).isApplicableTo(this))
                .forEach(registration -> keys.add(registration.getKey()));

        // Check if custom data is supported by this container
        if (this instanceof AdditionalContainerHolder) {
            final AdditionalContainerCollection<?> containers = ((AdditionalContainerHolder<?>) this)
                    .getAdditionalContainers();
            containers.getAll().forEach(manipulator -> keys.addAll(manipulator.getKeys()));
        }

        return keys.build();
    }

    @SuppressWarnings("unchecked")
    @Override
    default Set<ImmutableValue<?>> getValues() {
        final ImmutableSet.Builder<ImmutableValue<?>> values = ImmutableSet.builder();

        // Check local registrations
        for (KeyRegistration<?, ?> entry : getValueCollection().getAll()) {
            final Key key = entry.getKey();
            final Optional<BaseValue> optValue = getValue(key);
            optValue.ifPresent(baseValue -> values.add(ValueHelper.toImmutable(baseValue)));
        }

        // Check for global registrations
        for (ValueProcessorKeyRegistration<?, ?> registration : LanternValueFactory.get().getKeyRegistrations()) {
            final Optional<BaseValue> optValue = ((Processor) registration).getValueFrom(this);
            optValue.ifPresent(baseValue -> values.add(ValueHelper.toImmutable(baseValue)));
        }

        // Check if custom data is supported by this container
        if (this instanceof AdditionalContainerHolder) {
            final AdditionalContainerCollection<?> containers = ((AdditionalContainerHolder<?>) this)
                    .getAdditionalContainers();
            containers.getAll().forEach(manipulator -> values.addAll(manipulator.getValues()));
        }

        return values.build();
    }

    /**
     * Gets the {@link ValueCollection}.
     *
     * @return The value collection
     */
    ValueCollection getValueCollection();
}