Using Keys¶
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);
}
}
Now for the details of the above function.
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.
The second line uses the get()
function to ask the data holder for its maximum health. Besides
get()
, the methods getOrNull()
and getOrElse()
exist, all of which accept a Key
as their first
parameter. Generally, get()
should be used, which will return an Optional
of the data requested or
Optional.empty()
if the data holder does not support the supplied key. Since we already verified that the
Key
is supported, we can just call get()
on the Optional without further checks. We could also use
getOrNull()
which is basically a shortcut to call get(key).orNull()
, thus getting rid of the
Optional
. The third possibility would be the getOrElse()
, which accepts a default value as a second
parameter to be returned if the value is not present on the data holder.
In the third line, we offer data back to the data holder. We provide a Key
denoting the current health and the
before acquired maximum health, thus healing the data holder to full health. There are a variety of offer()
methods accepting different parameter sets, all of which return a DataTransactionResult containing
information if the offer was accepted. For now, we’ll use the one accepting a Key
and a corresponding value, but we
will encounter more in the next pages. Since we already know that our offer of current health is accepted (as the data
holder supports it), we can silently discard the result.
It is also possible to completely remove data from a DataHolder using the remove()
function. Simply
provide a Key
representing the data you want removed. The following example will attempt to remove a custom name
from a given data holder:
public void removeName(DataHolder target) {
target.remove(Keys.DISPLAY_NAME);
}
Transforming Data¶
Other than getting, modifying and offering a value, there is another way of interacting with data. Using a data
holder’s transform()
method we can pass a Key
and a Function
. Internally, the value for the key will be
retrieved and the given function applied to it. The result is then stored under the key and the transform()
method will return a DataTransactionResult
accordingly.
Now, as an example, imagine we want to buff a data holder by doubling his maximum health.
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())
.