Использование ключей

Getting and offering data using a key

A data holder provides methods to retrieve or alter a single point of data identified by a Key. Let’s just start out with an example:

Code Example: Healing a data holder, if possible

import org.spongepowered.api.data.DataHolder;
import org.spongepowered.api.data.key.Keys;

public void heal(DataHolder target) {
    if (target.supports(Keys.HEALTH)) {
        double maxHealth = target.get(Keys.MAX_HEALTH).get();
        target.offer(Keys.HEALTH, maxHealth);
    }
}

А теперь подробнее об указанной выше функции.

The first line checks if our given data holder supports a current health value. Only if it does, it can be healed after all. Since a data holder cannot have current health without having a maximum health and vice versa, a check for one of the keys using the supports() method suffices.

Вторая строка использует функцию get() для запроса к источнику данных о максимальном здоровье. Кроме метода get(), есть так же getOrNull() и getOrElse(), все из которых принимают Key как первый параметр. Обычно надо использовать get(), которое выдаст Optional запрашиваемых данных или Optional.empty() если источник данных не поддерживает предоставленный ключ. Так как мы уже убедились, что Key поддерживается, мы можем просто вызвать get() без дальнейших проверок. Можно также использовать getOrNull(), который является сокращенной версией вызова get(key).orNull(), таким образом избавляясь от Optional. Третьим вариантом будет getOrElse(), который принимает значение по умолчанию как второй параметр для выдачи, если значения нет в источнике данных.

В третьей строке мы предлагаем возврат данных в источник данных. Мы предлагаем через Key определить текущее здоровье и ранее полученное максимальное здоровье, таким образом вылечивая источник данных до полного здоровья. Есть несколько offer() методов, принимающих различный набор параметров, все из которых выдают DataTransactionResult, содержащий информацию, если предложение было принято. На данный момент, мы будем использовать тот вариант, который принимает Key и соответствующее значение, но мы продемонстрируем и другие на следующих страницах. Так как мы уже знаем, что наше предложение текущего здоровья принято (так как источник данных его поддерживает), мы можем молча сбросить результат.

Также можно полностью удалить данные из DataHolder, используя функцию remove(). Просто предоставьте Key, представляющий данные, которые вы хотите удалить. В примере ниже показана попытка удалить имя пользователя из указанного источника данных:

public void removeName(DataHolder target) {
    target.remove(Keys.DISPLAY_NAME);
}

Преобразование данных

Есть также и другой способ взаимодействия с данными, кроме получения, изменения и выдачи значения. Используя методы transform() источника данных, мы можем ввести Key и Function. Внутри будет получено значение ключа и применена указанная функция. Результат будет сохранён под данным ключом и метод transform() выдаст соответствующий DataTransactionResult.

Теперь, например, предположим, что мы хотим прокачать источник данных, увеличив максимальное здоровье в два раза.

import java.util.function.Function;

public void buff(DataHolder target) {
    target.transform(Keys.MAX_HEALTH, new Function<Double,Double>() {
        @Override
        public Double apply(Double input) {
            return (input == null) ? 0 : input * 2;
        }
    });
}

Or, if you use Java 8, you’re able to shorten the line with lambda expressions:

public void buff(DataHolder target) {
    target.transform(Keys.MAX_HEALTH, d -> (d == null) ? 0 : 2*d);
}

Note that in both cases we need to make sure our passed function can handle null. You will also notice that no check has been performed if the target actually supports the Keys#MAX_HEALTH key. If a target does not support it, the transform() function will fail and return a DataTransactionResult indicating so.

Keyed Values

There are cases where you may care about not only the direct value for a Key, but the keyed value encapsulating it. In that case, use the getValue(key) method instead of get(key). You will receive an object inheriting from BaseValue which contains a copy of the original value. Since we know that current health is a MutableBoundedValue, we can find out the minimum possible value and set our target’s health just a tiny bit above that.

Code Example: Bring a target to the brink of death

import org.spongepowered.api.data.value.mutable.MutableBoundedValue;

public void scare(DataHolder target) {
    if (target.supports(Keys.HEALTH)) {
        MutableBoundedValue<Double> health = target.getValue(Keys.HEALTH).get();
        double nearDeath = health.getMinValue() + 1;
        health.set(nearDeath);
        target.offer(health);
    }
}

Again, we check if our target supports the health key and then obtain the keyed value. A MutableBoundedValue contains a getMinValue() method, so we obtain the minimal value, add 1 and then set it to our data container. Internally, the set() method performs a check if our supplied value is valid and silently fails if it is not. Calling health.set(-2) would not change the value within health since it would fail the validity checks. To finally apply our changes to the target, we need to offer the keyed value back to it. As a keyed value also contains the Key used to identify it, calling target.offer(health) is equivalent to target.offer(health.getKey(), health.get()).