java.lang.Object
↳androidx.car.navigation.utils.BundleMarshaller
Gradle dependencies
compile group: 'androidx.car', name: 'car', version: '1.0.0-alpha7'
- groupId: androidx.car
- artifactId: car
- version: 1.0.0-alpha7
Artifact androidx.car:car:1.0.0-alpha7 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.car:car com.android.support:car
Overview
Class responsible for serializing and deserializing data into a . It also
provides a way to detect what items in the have been modified during
marshalling.
A single BundleMarshaller can be re-used to serialize or deserialize data multiple times.
Similarity, deserialization can be done in-place, updating existing Bundlables. This
reduces the number of instances being allocated.
When serializing, use BundleMarshaller.resetBundle() before marshalling and BundleMarshaller.getBundle() to
obtain an snap-shot of the serialized content. Or use BundleMarshaller.resetDelta() and
BundleMarshaller.getDelta() to obtain a representing the patch between the last and
the new serialized data.
When deserializing, use BundleMarshaller.setBundle(Bundle) to deserialize a containing an
snap-shot, or BundleMarshaller.applyDelta(Bundle) to process a patch from the last deserialized data.
Keys used in the "get" and "put" methods must be lower camel case alphanumerical identifiers
(e.g.: "distanceUnit"). Symbols like "." and "_" are reserved by the system.
When deserializing java.util.List
objects, this class assumes that they implement random access
(e.g. java.util.ArrayList
), or they are relatively small (see more details at
BundleMarshaller)
Summary
Methods |
---|
public void | applyDelta(Bundle delta)
Merges the provided on top of the one stored in this BundleMarshaller. |
public boolean | getBoolean(java.lang.String key)
Returns the value associated with the given key, or false if no mapping of the desired type
exists for the given key. |
public Bundlable | getBundlable(java.lang.String key, Bundlable current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key. |
public java.util.List<Bundlable> | getBundlableList(java.lang.String key, java.util.List<Bundlable> current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key. |
public java.util.List<Bundlable> | getBundlableListNonNull(java.lang.String key, java.util.List<Bundlable> current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or an empty list if no mapping of the
desired type exists for the given key. |
public Bundlable | getBundlableNonNull(java.lang.String key, Bundlable current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or a default value if no mapping of the
desired type exists for the given key. |
public Bundle | getBundle()
Returns data serialized since the last time this instance was constructed, or
BundleMarshaller.resetBundle() was called. |
public Bundle | getDelta()
Gets a containing only the entries of BundleMarshaller.getBundle() that were modified
since this instance was constructed, or BundleMarshaller.resetDelta() was called. |
public double | getDouble(java.lang.String key)
Returns the value associated with the given key, or 0.0 if no mapping of the desired type
exists for the given key. |
public java.lang.Enum<E> | getEnum(java.lang.String key, java.lang.Class<java.lang.Enum> clazz)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key. |
public java.lang.Enum<E> | getEnumNonNull(java.lang.String key, java.lang.Class<java.lang.Enum> clazz, java.lang.Enum<E> defaultValue)
Returns the value associated with the given key, or the provided default value if no mapping
of the desired type exists for the given key. |
public float | getFloat(java.lang.String key)
Returns the value associated with the given key, or 0.0f if no mapping of the desired type
exists for the given key. |
public int | getInt(java.lang.String key)
Returns the value associated with the given key, or 0 if no mapping of the desired type
exists for the given key. |
public java.lang.String | getString(java.lang.String key)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key. |
public java.lang.String | getStringNonNull(java.lang.String key, java.lang.String defaultValue)
Returns the value associated with the given key, or the provided default value if no mapping
of the desired type exists for the given key. |
public void | putBoolean(java.lang.String key, boolean value)
Inserts a boolean value, replacing any existing value for the given key. |
public void | putBundlable(java.lang.String key, Bundlable value)
Inserts a Bundlable value, replacing any existing value for the given key. |
public void | putBundlableList(java.lang.String key, java.util.List<Bundlable> values)
Inserts a java.util.List of Bundlable values, replacing any existing value for the
given key. |
public void | putDouble(java.lang.String key, double value)
Inserts a double value, replacing any existing value for the given key. |
public void | putEnum(java.lang.String key, java.lang.Enum<E> value)
Inserts an enum value, replacing any existing value for the given key. |
public void | putFloat(java.lang.String key, float value)
Inserts a float value, replacing any existing value for the given key. |
public void | putInt(java.lang.String key, int value)
Inserts an int value, replacing any existing value for the given key. |
public void | putString(java.lang.String key, java.lang.String value)
Inserts a string value, replacing any existing value for the given key. |
public void | resetBundle()
Resets this BundleMarshaller causing BundleMarshaller.getBundle() to return an empty
until the next marshalling is executed. |
public void | resetDelta()
Resets tracking of modified entries, causing BundleMarshaller.getDelta() to return an empty
until the next marshalling is executed. |
public void | setBundle(Bundle bundle)
Replaces the to serialize into or deserialize from. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructors
public
BundleMarshaller()
Methods
public Bundle
getBundle()
Returns data serialized since the last time this instance was constructed, or
BundleMarshaller.resetBundle() was called.
public void
resetBundle()
Resets this BundleMarshaller causing BundleMarshaller.getBundle() to return an empty
until the next marshalling is executed. This can be used occasionally to
remove unused keys in the .
public void
setBundle(Bundle bundle)
Replaces the to serialize into or deserialize from.
Gets a containing only the entries of BundleMarshaller.getBundle() that were modified
since this instance was constructed, or BundleMarshaller.resetDelta() was called.
public void
applyDelta(Bundle delta)
Merges the provided on top of the one stored in this BundleMarshaller.
Parameters:
delta: a containing entries to be updated on one stored in this
BundleMarshaller instance. Such can be produced by
using the BundleMarshaller.resetDelta() and BundleMarshaller.getDelta() methods during data
serialization.
Resets tracking of modified entries, causing BundleMarshaller.getDelta() to return an empty
until the next marshalling is executed. This can be used between
serializations make BundleMarshaller.getDelta() return only the differences.
public void
putInt(java.lang.String key, int value)
Inserts an int value, replacing any existing value for the given key.
Parameters:
key: lower camel case alphanumerical identifier
value: an int
public int
getInt(java.lang.String key)
Returns the value associated with the given key, or 0 if no mapping of the desired type
exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
Returns:
an int
public void
putFloat(java.lang.String key, float value)
Inserts a float value, replacing any existing value for the given key.
Parameters:
key: lower camel case alphanumerical identifier
value: a float
public float
getFloat(java.lang.String key)
Returns the value associated with the given key, or 0.0f if no mapping of the desired type
exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
Returns:
a float
public void
putDouble(java.lang.String key, double value)
Inserts a double value, replacing any existing value for the given key.
Parameters:
key: lower camel case alphanumerical identifier
value: a double
public double
getDouble(java.lang.String key)
Returns the value associated with the given key, or 0.0 if no mapping of the desired type
exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
Returns:
a double
public void
putBoolean(java.lang.String key, boolean value)
Inserts a boolean value, replacing any existing value for the given key.
Parameters:
key: lower camel case alphanumerical identifier
value: a boolean
public boolean
getBoolean(java.lang.String key)
Returns the value associated with the given key, or false if no mapping of the desired type
exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
Returns:
a boolean
public void
putString(java.lang.String key, java.lang.String value)
Inserts a string value, replacing any existing value for the given key.
Parameters:
key: lower camel case alphanumerical identifier
value: a string, or null
public java.lang.String
getString(java.lang.String key)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
Returns:
a string, or null
public java.lang.String
getStringNonNull(java.lang.String key, java.lang.String defaultValue)
Returns the value associated with the given key, or the provided default value if no mapping
of the desired type exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
defaultValue: value to return if key does not exist or if a null value is associated
with the given key.
Returns:
a string
public void
putEnum(java.lang.String key, java.lang.Enum<E> value)
Inserts an enum value, replacing any existing value for the given key. The provided enum
will be serialized as a string using name
.
Parameters:
key: lower camel case alphanumerical identifier
value: an enum, or null
public java.lang.Enum<E>
getEnum(java.lang.String key, java.lang.Class<java.lang.Enum> clazz)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
clazz: java.lang.Enum
class to be used to deserialize the value.
Returns:
an enum, or null
public java.lang.Enum<E>
getEnumNonNull(java.lang.String key, java.lang.Class<java.lang.Enum> clazz, java.lang.Enum<E> defaultValue)
Returns the value associated with the given key, or the provided default value if no mapping
of the desired type exists for the given key.
Parameters:
key: lower camel case alphanumerical identifier
clazz: java.lang.Enum
class to be used to deserialize the value.
defaultValue: value to return if key does not exist or if a null value is associated
with the given key.
Returns:
an enum
public void
putBundlable(java.lang.String key,
Bundlable value)
Inserts a Bundlable value, replacing any existing value for the given key.
Parameters:
key: lower camel case alphanumerical identifier
value: a Bundlable, or null
public
Bundlable getBundlable(java.lang.String key,
Bundlable current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key. If a non-null "current" instance is provided, then the
deserialization would be done in place. Otherwise, a new instance will be created using the
provided factory.
Parameters:
key: lower camel case alphanumerical identifier
current: current value (if available) to perform in-place deserialization, or null
factory: a java.util.function.Supplier
capable of providing an instance of a Bundlable of
type T. The suggested implementation is to pass a reference to the default
constructor of that class.
Returns:
an instance of type T, or null
public
Bundlable getBundlableNonNull(java.lang.String key,
Bundlable current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or a default value if no mapping of the
desired type exists for the given key. If a non-null value is available, then such value
will be deserialized in-place on the given "current" instance. Otherwise, a default value
will be generated using the provided factory.
Parameters:
key: lower camel case alphanumerical identifier
current: current value to perform in-place deserialization
factory: a java.util.function.Supplier
capable of providing an instance of a Bundlable of
type T. The suggested implementation is to pass a reference to the default
constructor of that class.
Returns:
an instance of type T
public void
putBundlableList(java.lang.String key, java.util.List<Bundlable> values)
Inserts a java.util.List
of Bundlable values, replacing any existing value for the
given key.
Parameters:
key: lower camel case alphanumerical identifier
values: a java.util.List
of Bundlable values, or null
public java.util.List<Bundlable>
getBundlableList(java.lang.String key, java.util.List<Bundlable> current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or null if no mapping of the desired type
exists for the given key. If a non-null "current" list is provided, then the deserialization
would be done in place. Otherwise, a new list will be created and items will be instantiated
using the provided factory.
Parameters:
key: lower camel case alphanumerical identifier
current: current value (if available) to perform in-place deserialization, or null
factory: a java.util.function.Supplier
capable of providing an instance of a Bundlable of
type T. The suggested implementation is to pass a reference to the default
constructor of that class.
Returns:
a list of instances of type T, or null. The resulting list might contain null
elements.
public java.util.List<Bundlable>
getBundlableListNonNull(java.lang.String key, java.util.List<Bundlable> current, java.util.function.Supplier<Bundlable> factory)
Returns the value associated with the given key, or an empty list if no mapping of the
desired type exists for the given key. If a non-null "current" list is provided, then the
deserialization would be done in place. Otherwise, a new list will be created and items will
be instantiated using the provided factory.
Parameters:
key: lower camel case alphanumerical identifier
current: current value (if available) to perform in-place deserialization, or null
factory: a java.util.function.Supplier
capable of providing an instance of a Bundlable of
type T. The suggested implementation is to pass a reference to the default
constructor of that class.
Returns:
a list of instances of type T, or an empty list. The resulting list might contain
null elements.
Source
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.car.navigation.utils;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
/**
* Class responsible for serializing and deserializing data into a {@link Bundle}. It also
* provides a way to detect what items in the {@link Bundle} have been modified during
* marshalling.
* <p>
* A single {@link BundleMarshaller} can be re-used to serialize or deserialize data multiple times.
* Similarity, deserialization can be done in-place, updating existing {@link Bundlable}s. This
* reduces the number of instances being allocated.
* <p>
* When serializing, use {@link #resetBundle()} before marshalling and {@link #getBundle()} to
* obtain an snap-shot of the serialized content. Or use {@link #resetDelta()} and
* {@link #getDelta()} to obtain a {@link Bundle} representing the patch between the last and
* the new serialized data.
* <p>
* When deserializing, use {@link #setBundle(Bundle)} to deserialize a {@link Bundle} containing an
* snap-shot, or {@link #applyDelta(Bundle)} to process a patch from the last deserialized data.
* <p>
* Keys used in the "get" and "put" methods must be lower camel case alphanumerical identifiers
* (e.g.: "distanceUnit"). Symbols like "." and "_" are reserved by the system.
* <p>
* When deserializing {@link List} objects, this class assumes that they implement random access
* (e.g. {@link ArrayList}), or they are relatively small (see more details at
* {@link #trimList(List, int)})
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public class BundleMarshaller {
/**
* Separator used to concatenate identifiers when marshalling non-primitive types (e.g. lists
* or {@link Bundlable}s).
*/
private static final String KEY_SEPARATOR = ".";
/**
* Identifier used to record if a given non-primitive field is null or not. This allows
* serializing null objects without the need of using reflection or static methods.
*/
private static final String IS_NULL_KEY = "_isNull";
/**
* Identifier used to record the length of a collection. This allows serializing changes to the
* length of a collection without having to remove elements or having to iterate over every
* possible collection key.
*/
private static final String SIZE_KEY = "_size";
/**
* Special value for {@link #SIZE_KEY} to serialize a null collection.
*/
private static final int NULL_SIZE = -1;
private Bundle mBundle = new Bundle();
private String mKeyPrefix = "";
private final Bundle mBundleDelta = new Bundle();
/**
* Returns data serialized since the last time this instance was constructed, or
* {@link #resetBundle()} was called.
*/
public Bundle getBundle() {
return mBundle;
}
/**
* Resets this {@link BundleMarshaller} causing {@link #getBundle()} to return an empty
* {@link Bundle} until the next marshalling is executed. This can be used occasionally to
* remove unused keys in the {@link Bundle}.
*/
public void resetBundle() {
mBundle.clear();
}
/**
* Replaces the {@link Bundle} to serialize into or deserialize from.
*/
public void setBundle(Bundle bundle) {
mBundle = bundle;
}
/**
* Gets a {@link Bundle} containing only the entries of {@link #getBundle()} that were modified
* since this instance was constructed, or {@link #resetDelta()} was called.
*/
public Bundle getDelta() {
return mBundleDelta;
}
/**
* Merges the provided {@link Bundle} on top of the one stored in this {@link BundleMarshaller}.
*
* @param delta a {@link Bundle} containing entries to be updated on one stored in this
* {@link BundleMarshaller} instance. Such {@link Bundle} can be produced by
* using the {@link #resetDelta()} and {@link #getDelta()} methods during data
* serialization.
*/
public void applyDelta(Bundle delta) {
mBundle.putAll(delta);
}
/**
* Resets tracking of modified entries, causing {@link #getDelta()} to return an empty
* {@link Bundle} until the next marshalling is executed. This can be used between
* serializations make {@link #getDelta()} return only the differences.
*/
public void resetDelta() {
mBundleDelta.clear();
}
/**
* Inserts an int value, replacing any existing value for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param value an int
*/
public void putInt(@NonNull String key, int value) {
String mangledKey = getMangledKey(key);
if (!mBundle.containsKey(mangledKey) || mBundle.getInt(mangledKey) != value) {
mBundleDelta.putInt(mangledKey, value);
mBundle.putInt(mangledKey, value);
}
}
/**
* Returns the value associated with the given key, or 0 if no mapping of the desired type
* exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @return an int
*/
public int getInt(@NonNull String key) {
return mBundle.getInt(getMangledKey(key));
}
/**
* Inserts a float value, replacing any existing value for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param value a float
*/
public void putFloat(@NonNull String key, float value) {
String mangledKey = getMangledKey(key);
if (!mBundle.containsKey(mangledKey)
|| Float.compare(mBundle.getFloat(mangledKey), value) != 0) {
mBundleDelta.putFloat(mangledKey, value);
mBundle.putFloat(mangledKey, value);
}
}
/**
* Returns the value associated with the given key, or 0.0f if no mapping of the desired type
* exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @return a float
*/
public float getFloat(@NonNull String key) {
return mBundle.getFloat(getMangledKey(key));
}
/**
* Inserts a double value, replacing any existing value for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param value a double
*/
public void putDouble(@NonNull String key, double value) {
String mangledKey = getMangledKey(key);
if (!mBundle.containsKey(mangledKey)
|| Double.compare(mBundle.getDouble(mangledKey), value) != 0) {
mBundleDelta.putDouble(mangledKey, value);
mBundle.putDouble(mangledKey, value);
}
}
/**
* Returns the value associated with the given key, or 0.0 if no mapping of the desired type
* exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @return a double
*/
public double getDouble(@NonNull String key) {
return mBundle.getDouble(getMangledKey(key));
}
/**
* Inserts a boolean value, replacing any existing value for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param value a boolean
*/
public void putBoolean(@NonNull String key, boolean value) {
String mangledKey = getMangledKey(key);
if (!mBundle.containsKey(mangledKey) || mBundle.getBoolean(mangledKey) != value) {
mBundleDelta.putBoolean(mangledKey, value);
mBundle.putBoolean(mangledKey, value);
}
}
/**
* Returns the value associated with the given key, or false if no mapping of the desired type
* exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @return a boolean
*/
public boolean getBoolean(@NonNull String key) {
return mBundle.getBoolean(getMangledKey(key));
}
/**
* Inserts a string value, replacing any existing value for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param value a string, or null
*/
public void putString(@NonNull String key, @Nullable String value) {
String mangledKey = getMangledKey(key);
if (!mBundle.containsKey(mangledKey)
|| !Objects.equals(mBundle.getString(mangledKey), value)) {
mBundleDelta.putString(mangledKey, value);
mBundle.putString(mangledKey, value);
}
}
/**
* Returns the value associated with the given key, or null if no mapping of the desired type
* exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @return a string, or null
*/
@Nullable
public String getString(@NonNull String key) {
return mBundle.getString(getMangledKey(key));
}
/**
* Returns the value associated with the given key, or the provided default value if no mapping
* of the desired type exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param defaultValue value to return if key does not exist or if a null value is associated
* with the given key.
* @return a string
*/
@NonNull
public String getStringNonNull(@NonNull String key, @NonNull String defaultValue) {
return mBundle.getString(getMangledKey(key), defaultValue);
}
/**
* Inserts an enum value, replacing any existing value for the given key. The provided enum
* will be serialized as a string using {@link Enum#name()}.
*
* @param key lower camel case alphanumerical identifier
* @param value an enum, or null
*/
public <T extends Enum<T>> void putEnum(@NonNull String key, @Nullable T value) {
putString(key, value != null ? value.name() : null);
}
/**
* Returns the value associated with the given key, or null if no mapping of the desired type
* exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param clazz {@link Enum} class to be used to deserialize the value.
* @param <T> {@link Enum} type to be returned.
* @return an enum, or null
*/
@Nullable
public <T extends Enum<T>> T getEnum(@NonNull String key, @NonNull Class<T> clazz) {
String name = getString(key);
try {
return name != null ? Enum.valueOf(clazz, name) : null;
} catch (IllegalArgumentException ex) {
return null;
}
}
/**
* Returns the value associated with the given key, or the provided default value if no mapping
* of the desired type exists for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param clazz {@link Enum} class to be used to deserialize the value.
* @param defaultValue value to return if key does not exist or if a null value is associated
* with the given key.
* @param <T> {@link Enum} type to be returned.
* @return an enum
*/
@NonNull
public <T extends Enum<T>> T getEnumNonNull(@NonNull String key, @NonNull Class<T> clazz,
@NonNull T defaultValue) {
T result = getEnum(key, clazz);
return result != null ? result : defaultValue;
}
/**
* Inserts a {@link Bundlable} value, replacing any existing value for the given key.
*
* @param key lower camel case alphanumerical identifier
* @param value a {@link Bundlable}, or null
*/
public <T extends Bundlable> void putBundlable(@NonNull String key, @Nullable T value) {
withKeyPrefix(key, () -> {
putBoolean(IS_NULL_KEY, value == null);
if (value != null) {
value.toBundle(this);
}
});
}
/**
* Returns the value associated with the given key, or null if no mapping of the desired type
* exists for the given key. If a non-null "current" instance is provided, then the
* deserialization would be done in place. Otherwise, a new instance will be created using the
* provided factory.
*
* @param key lower camel case alphanumerical identifier
* @param current current value (if available) to perform in-place deserialization, or null
* @param factory a {@link Supplier} capable of providing an instance of a {@link Bundlable} of
* type T. The suggested implementation is to pass a reference to the default
* constructor of that class.
* @param <T> {@link Bundlable} type to be returned.
* @return an instance of type T, or null
*/
@Nullable
public <T extends Bundlable> T getBundlable(@NonNull String key, @Nullable T current,
@NonNull Supplier<T> factory) {
return withKeyPrefix(key, () -> {
if (getBoolean(IS_NULL_KEY)) {
return null;
}
T result = current != null ? current : factory.get();
result.fromBundle(this);
return result;
});
}
/**
* Returns the value associated with the given key, or a default value if no mapping of the
* desired type exists for the given key. If a non-null value is available, then such value
* will be deserialized in-place on the given "current" instance. Otherwise, a default value
* will be generated using the provided factory.
*
* @param key lower camel case alphanumerical identifier
* @param current current value to perform in-place deserialization
* @param factory a {@link Supplier} capable of providing an instance of a {@link Bundlable} of
* type T. The suggested implementation is to pass a reference to the default
* constructor of that class.
* @param <T> {@link Bundlable} type to be returned.
* @return an instance of type T
*/
@NonNull
public <T extends Bundlable> T getBundlableNonNull(@NonNull String key, @NonNull T current,
@NonNull Supplier<T> factory) {
T result = getBundlable(key, current, factory);
return result != null ? result : factory.get();
}
/**
* Inserts a {@link List} of {@link Bundlable} values, replacing any existing value for the
* given key.
*
* @param key lower camel case alphanumerical identifier
* @param values a {@link List} of {@link Bundlable} values, or null
*/
public <T extends Bundlable> void putBundlableList(@NonNull String key,
@Nullable List<T> values) {
withKeyPrefix(key, () -> {
putInt(SIZE_KEY, values != null ? values.size() : NULL_SIZE);
if (values != null) {
int pos = 0;
// Using for-each as the provided list might not implement random access (e.g. it
// might be a linked list).
for (T value : values) {
putBundlable(String.valueOf(pos), value);
pos++;
}
}
});
}
/**
* Returns the value associated with the given key, or null if no mapping of the desired type
* exists for the given key. If a non-null "current" list is provided, then the deserialization
* would be done in place. Otherwise, a new list will be created and items will be instantiated
* using the provided factory.
*
* @param key lower camel case alphanumerical identifier
* @param current current value (if available) to perform in-place deserialization, or null
* @param factory a {@link Supplier} capable of providing an instance of a {@link Bundlable} of
* type T. The suggested implementation is to pass a reference to the default
* constructor of that class.
* @param <T> {@link Bundlable} type to be returned.
* @return a list of instances of type T, or null. The resulting list might contain null
* elements.
*/
@Nullable
public <T extends Bundlable> List<T> getBundlableList(@NonNull String key,
@Nullable List<T> current, @NonNull Supplier<T> factory) {
return withKeyPrefix(key, () -> {
int listSize = getInt(SIZE_KEY);
if (listSize == NULL_SIZE) {
return null;
}
List<T> result = current != null ? current : new ArrayList<>(listSize);
if (result.size() > listSize) {
result.subList(listSize, result.size()).clear();
}
for (int pos = 0; pos < listSize; pos++) {
String subKey = String.valueOf(pos);
if (pos < result.size()) {
result.set(pos, getBundlable(subKey, result.get(pos), factory));
} else {
result.add(getBundlable(String.valueOf(pos),
null /* force the creation of a new instance */,
factory));
}
}
return result;
});
}
/**
* Returns the value associated with the given key, or an empty list if no mapping of the
* desired type exists for the given key. If a non-null "current" list is provided, then the
* deserialization would be done in place. Otherwise, a new list will be created and items will
* be instantiated using the provided factory.
*
* @param key lower camel case alphanumerical identifier
* @param current current value (if available) to perform in-place deserialization, or null
* @param factory a {@link Supplier} capable of providing an instance of a {@link Bundlable} of
* type T. The suggested implementation is to pass a reference to the default
* constructor of that class.
* @param <T> {@link Bundlable} type to be returned.
* @return a list of instances of type T, or an empty list. The resulting list might contain
* null elements.
*/
@NonNull
public <T extends Bundlable> List<T> getBundlableListNonNull(@NonNull String key,
@NonNull List<T> current, @NonNull Supplier<T> factory) {
List<T> result = getBundlableList(key, current, factory);
return result != null ? result : new ArrayList<>();
}
/**
* Executes the given {@link Runnable} in a context where {@link #getMangledKey(String)}
* includes the given key as part of the prefix. Calls to this method can be nested (the
* provided {@link Runnable} can call to this method if needed). This method should be used when
* serializing or deserializing nested objects.
* <p>
* For example: calling to {@link #withKeyPrefix(String, Runnable)} with "foo" as key and
* a {@link Runnable} that calls {@link #getMangledKey(String)} with "bar" as key, will
* cause such {@link #getMangledKey(String)} call to return "foo.bar".
*/
private void withKeyPrefix(@NonNull String key, @NonNull Runnable runnable) {
String originalKeyPrefix = mKeyPrefix;
mKeyPrefix = mKeyPrefix + key + KEY_SEPARATOR;
runnable.run();
mKeyPrefix = originalKeyPrefix;
}
/**
* Similar to {@link #withKeyPrefix(String, Runnable)} but allows returning a value.
*/
private <X> X withKeyPrefix(@NonNull String key, @NonNull Supplier<X> supplier) {
String originalKeyPrefix = mKeyPrefix;
mKeyPrefix = mKeyPrefix + key + KEY_SEPARATOR;
X res = supplier.get();
mKeyPrefix = originalKeyPrefix;
return res;
}
/**
* Returns a composed key based on the given one and the current serialization/deserialization
* key prefix (initially empty). This prefix can be temporarily changed with
* {@link #withKeyPrefix(String, Runnable)} or {@link #withKeyPrefix(String, Supplier)}.
*/
private String getMangledKey(@NonNull String key) {
return mKeyPrefix + key;
}
}