package ru.nts.commons.collections;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Обертка над Map, позволяющая безопасно работать с разными типами данных.
* Поддерживает автоматическое преобразование типов при получении значений, а также
* добавление значений, примитивных типов и Enum'ов.
* Также реализует стандартные методы Map.
* <br>
* <b>ВАЖНО:</b> Даже при использовании потокобезопасной внутренней карты, консистентность данных при использовании кастомных методов не гарантируется.
* Все манипуляции производятся с извлеченным на момент обращения значением и если другой поток изменит данные в этот момент, то это не будет учтено.
* <br>
* В случае, если используется стандартные методы Map, то потокобезопасность будет напрямую зависеть от типа внутренней карты.
*
*
* @author Aristo
*/
@Slf4j
public class StatsSet implements Map<String, Object> {
private final @NotNull Map<String, Object> internalMap;
/**
* Создает новый экземпляр {@link StatsSet}, используя фабрику для создания внутренней карты.
*
* @param mapFactory фабрика для создания внутренней карты
*/
public StatsSet(@NotNull Supplier<Map<String, Object>> mapFactory) {
this(mapFactory.get());
}
/**
* Создает новый экземпляр {@link StatsSet} с заданной внутренней картой.
*
* @param map внутренняя карта
*/
private StatsSet(@NotNull Map<String, Object> map) {
internalMap = map;
}
/**
* Создает новый экземпляр {@link StatsSet} и оборачивает заданную карту.
*
* @param map карта, которую нужно обернуть
* @return новый экземпляр {@link StatsSet}
*/
public static StatsSet wrap(@NotNull Map<String, Object> map) {
return new StatsSet(map);
}
/**
* Парсит строку параметров по заданным разделителям и создает {@link StatsSet}.
* Например: (key1)(spliter2)(value1)(spliter1)(key2)(spliter2)(value2)(spliter1)(keyN)(spliter2)(valueN)
*
* @param params строка параметров
* @param splitter1 разделитель между парами ключ-значение
* @param splitter2 разделитель между ключом и значением
* @param mapFactory фабрика для создания внутренней карты
* @return новый экземпляр {@link StatsSet} с данными из строки параметров
*/
public static StatsSet parseParams(@Nullable String params, @NotNull String splitter1, @NotNull String splitter2, @NotNull Supplier<Map<String, Object>> mapFactory) {
final Map<String, Object> map = mapFactory.get();
if (params != null) {
try {
final String[] args = params.split(splitter1);
for (String split : args) {
map.put(split.split(splitter2)[0], split.split(splitter2)[1]);
}
} catch (Exception e) {
log.error("Can't parse params: {}", params, e);
}
}
return StatsSet.wrap(map);
}
/**
* Возвращает логическое значение по ключу.
*
* @param key ключ
* @return логическое значение
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано к логическому типу
*/
public boolean getBooleanOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Boolean b -> b;
case String s -> Boolean.parseBoolean(s);
case null -> throw new IllegalArgumentException("Boolean value required, but not found: " + key);
default -> throw new IllegalArgumentException("Boolean value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает логическое значение по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return логическое значение или значение по умолчанию
*/
public boolean getBooleanOrDefault(@NotNull String key, boolean defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Boolean b -> b;
case String s -> Boolean.parseBoolean(s);
case null, default -> defaultValue;
};
}
/**
* Возвращает байтовое значение по ключу.
*
* @param key ключ
* @return байтовое значение
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано к байтовому типу
*/
public byte getByteOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.byteValue();
case String s when NumberUtils.isParsable(s) -> Byte.parseByte(s);
case null -> throw new IllegalArgumentException("Byte value required, but not found: " + key);
default -> throw new IllegalArgumentException("Byte value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает байтовое значение по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return байтовое значение или значение по умолчанию
*/
public byte getByteOrDefault(@NotNull String key, byte defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.byteValue();
case String s when NumberUtils.isParsable(s) -> Byte.parseByte(s);
case null, default -> defaultValue;
};
}
/**
* Возвращает значение типа short по ключу.
*
* @param key ключ
* @return значение типа short
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано к типу short
*/
public short getShortOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.shortValue();
case String s when NumberUtils.isParsable(s) -> Short.parseShort(s);
case null -> throw new IllegalArgumentException("Short value required, but not found: " + key);
default -> throw new IllegalArgumentException("Short value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает значение типа short по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return значение типа short или значение по умолчанию
*/
public short getShortOrDefault(@NotNull String key, short defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.shortValue();
case String s when NumberUtils.isParsable(s) -> Short.parseShort(s);
case null, default -> defaultValue;
};
}
/**
* Возвращает целочисленное значение по ключу.
*
* @param key ключ
* @return целочисленное значение
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано к целочисленному типу
*/
public int getIntOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.intValue();
case String s when NumberUtils.isParsable(s) -> Integer.parseInt(s);
case null -> throw new IllegalArgumentException("Integer value required, but not found: " + key);
default -> throw new IllegalArgumentException("Integer value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает целочисленное значение по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return целочисленное значение или значение по умолчанию
*/
public int getIntOrDefault(@NotNull String key, int defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.intValue();
case String s when NumberUtils.isParsable(s) -> Integer.parseInt(s);
case null, default -> defaultValue;
};
}
/**
* Возвращает значение типа long по ключу.
*
* @param key ключ
* @return значение типа long
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано к типу long
*/
public long getLongOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.longValue();
case String s when NumberUtils.isParsable(s) -> Long.parseLong(s);
case null -> throw new IllegalArgumentException("Long value required, but not found: " + key);
default -> throw new IllegalArgumentException("Long value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает значение типа long по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return значение типа long или значение по умолчанию
*/
public long getLongOrDefault(@NotNull String key, long defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.longValue();
case String s when NumberUtils.isParsable(s) -> Long.parseLong(s);
case null, default -> defaultValue;
};
}
/**
* Возвращает значение типа float по ключу.
*
* @param key ключ
* @return значение типа float
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано к типу float
*/
public float getFloatOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.floatValue();
case String s when NumberUtils.isParsable(s) -> Float.parseFloat(s);
case null -> throw new IllegalArgumentException("Float value required, but not found: " + key);
default -> throw new IllegalArgumentException("Float value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает значение типа float по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return значение типа float или значение по умолчанию
*/
public float getFloatOrDefault(@NotNull String key, float defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.floatValue();
case String s when NumberUtils.isParsable(s) -> Float.parseFloat(s);
case null, default -> defaultValue;
};
}
/**
* Возвращает значение типа double по ключу.
*
* @param key ключ
* @return значение типа double
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано к типу double
*/
public double getDoubleOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.doubleValue();
case String s when NumberUtils.isParsable(s) -> Double.parseDouble(s);
case null -> throw new IllegalArgumentException("Double value required, but not found: " + key);
default -> throw new IllegalArgumentException("Double value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает значение типа double по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return значение типа double или значение по умолчанию
*/
public double getDoubleOrDefault(@NotNull String key, double defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Number n -> n.doubleValue();
case String s when NumberUtils.isParsable(s) -> Double.parseDouble(s);
case null, default -> defaultValue;
};
}
/**
* Возвращает строковое значение по ключу.
*
* @param key ключ
* @return строковое значение
* @throws IllegalArgumentException если значение не найдено
*/
public @NotNull String getStringOrThrow(@NotNull String key) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case null -> throw new IllegalArgumentException("String value required, but not found: " + key);
case String s -> s;
default -> String.valueOf(val);
};
}
/**
* Возвращает строковое значение по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return строковое значение или значение по умолчанию
*/
public @Nullable String getStringOrDefault(@NotNull String key, @Nullable String defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case null -> defaultValue;
case String s -> s;
default -> String.valueOf(val);
};
}
/**
* Возвращает значение перечисления по ключу.
*
* @param key ключ
* @param enumClass класс перечисления
* @param <T> тип перечисления
* @return значение перечисления
* @throws IllegalArgumentException если значение не найдено, не может быть преобразовано к типу перечисления или класс не является перечислением
*/
public <T extends Enum<T>> @NotNull T getEnumOrThrow(@NotNull String key, @NotNull Class<T> enumClass) throws IllegalArgumentException {
if (!enumClass.isEnum()) {
throw new IllegalArgumentException("Invalid enum class: " + enumClass);
}
final Object val = internalMap.get(key);
return switch (val) {
case Object o when enumClass.isInstance(o) -> enumClass.cast(o);
case String s -> Enum.valueOf(enumClass, s);
case null, default -> throw new IllegalArgumentException("Enum value of type " + enumClass.getName() + " required, but not found: " + key);
};
}
/**
* Возвращает значение перечисления по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param enumClass класс перечисления
* @param defaultValue значение по умолчанию
* @param <T> тип перечисления
* @return значение перечисления или значение по умолчанию
* @throws IllegalArgumentException если класс не является перечислением
*/
public @Nullable <T extends Enum<T>> T getEnumOrDefault(@NotNull String key, @NotNull Class<T> enumClass, @Nullable T defaultValue, @Nullable String start, @Nullable String end) {
if (!enumClass.isEnum()) {
throw new IllegalArgumentException("Invalid enum class: " + enumClass);
}
final Object val = internalMap.get(key);
return switch (val) {
case Object o when enumClass.isInstance(o) -> enumClass.cast(o);
case String s -> Enum.valueOf(enumClass, cleanse(s, start, end));
case null, default -> defaultValue;
};
}
/**
* Возвращает необработанный объект по ключу.
*
* @param key ключ
* @return необработанный объект или null, если ключ не найден
*/
public @Nullable Object getRawObject(@NotNull String key) {
return internalMap.get(key);
}
/**
* Возвращает объект по ключу, приведенный к заданному типу.
*
* @param key ключ
* @param type класс, к которому нужно привести объект
* @param <A> тип объекта
* @return объект приведенный к заданному типу
* @throws IllegalArgumentException если значение не найдено или не может быть приведено к заданному типу
*/
public <A> @NotNull A getObjectOrThrow(@NotNull String key, @NotNull Class<A> type) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case Object o when type.isInstance(o) -> type.cast(o);
case null -> throw new IllegalArgumentException("Object value required, but not found: " + key);
default -> throw new IllegalArgumentException("Object value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает объект по ключу, приведенный к заданному типу, или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param type класс, к которому нужно привести объект
* @param defaultValue значение по умолчанию
* @param <A> тип объекта
* @return объект, приведенный к заданному типу, или значение по умолчанию
*/
public <A> @Nullable A getObjectOrDefault(@NotNull String key, @NotNull Class<A> type, @Nullable A defaultValue) {
final Object val = internalMap.get(key);
return switch (val) {
case Object o when type.isInstance(o) -> type.cast(o);
case null, default -> defaultValue;
};
}
/**
* Возвращает массив логических значений по ключу.
*
* @param key ключ
* @param splitOn разделитель для парсинга строки в массив
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив логических значений
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив логических значений
*/
public boolean @NotNull [] getParsedBooleanArrayOrThrow(@NotNull String key, @NotNull String splitOn, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case boolean[] v -> v;
case Boolean[] v -> ArrayUtils.toPrimitive(v);
case Boolean v -> new boolean[]{v};
case String s -> ArrayUtils.toPrimitive(Arrays.stream(cleanse(s, start, end).split(splitOn)).map(Boolean::parseBoolean).toArray(Boolean[]::new));
case null -> throw new IllegalArgumentException("Boolean value required, but not found: " + key);
default -> throw new IllegalArgumentException("Boolean value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает массив символов по ключу.
*
* @param key ключ
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив символов
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив символов
*/
public char @NotNull [] getParsedCharArrayOrThrow(@NotNull String key, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case char[] v -> v;
case Character[] v -> ArrayUtils.toPrimitive(v);
case Character v -> new char[]{v};
case String s -> cleanse(s, start, end).toCharArray();
case null -> throw new IllegalArgumentException("Character value required, but not found: " + key);
default -> throw new IllegalArgumentException("Character value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает массив байтов по ключу.
*
* @param key ключ
* @param splitOn разделитель для парсинга строки в массив
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив байтов
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив байтов
*/
public byte @NotNull [] getParsedByteArrayOrThrow(@NotNull String key, @NotNull String splitOn, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case byte[] v -> v;
case Byte[] v -> ArrayUtils.toPrimitive(v);
case Byte v -> new byte[]{v};
case String s -> ArrayUtils.toPrimitive(Arrays.stream(cleanse(s, start, end).split(splitOn)).map(Byte::parseByte).toArray(Byte[]::new));
case null -> throw new IllegalArgumentException("Byte value required, but not found: " + key);
default -> throw new IllegalArgumentException("Byte value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает массив значений типа short по ключу.
*
* @param key ключ
* @param splitOn разделитель для парсинга строки в массив
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив значений типа short
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив значений типа short
*/
public short @NotNull [] getParsedShortArrayOrThrow(@NotNull String key, @NotNull String splitOn, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case short[] a -> a;
case Short[] a -> ArrayUtils.toPrimitive(a);
case Short v -> new short[]{v};
case String s -> ArrayUtils.toPrimitive(Arrays.stream(cleanse(s, start, end).split(splitOn)).map(Short::parseShort).toArray(Short[]::new));
case null -> throw new IllegalArgumentException("Short value required, but not found: " + key);
default -> throw new IllegalArgumentException("Short value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает массив целых чисел по ключу.
*
* @param key ключ
* @param splitOn разделитель для парсинга строки в массив
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив целых чисел
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив целых чисел
*/
public int @NotNull [] getParsedIntArrayOrThrow(@NotNull String key, @NotNull String splitOn, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case int[] a -> a;
case Integer[] a -> ArrayUtils.toPrimitive(a);
case Integer v -> new int[]{v};
case String s -> Arrays.stream(cleanse(s, start, end).split(splitOn)).mapToInt(Integer::parseInt).toArray();
case null -> throw new IllegalArgumentException("Integer value required, but not found: " + key);
default -> throw new IllegalArgumentException("Integer value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает массив значений типа long по ключу.
*
* @param key ключ
* @param splitOn разделитель для парсинга строки в массив
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив значений типа long
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив значений типа long
*/
public long @NotNull [] getParsedLongArrayOrThrow(@NotNull String key, @NotNull String splitOn, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case long[] a -> a;
case Long[] a -> ArrayUtils.toPrimitive(a);
case Long v -> new long[]{v};
case String s -> Arrays.stream(cleanse(s, start, end).split(splitOn)).mapToLong(Long::parseLong).toArray();
case null -> throw new IllegalArgumentException("Long value required, but not found: " + key);
default -> throw new IllegalArgumentException("Long value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает массив значений типа float по ключу.
*
* @param key ключ
* @param splitOn разделитель для парсинга строки в массив
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив значений типа float
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив значений типа float
*/
public float @NotNull [] getParsedFloatArrayOrThrow(@NotNull String key, @NotNull String splitOn, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case float[] a -> a;
case Float[] a -> ArrayUtils.toPrimitive(a);
case Float v -> new float[]{v};
case String s -> ArrayUtils.toPrimitive(Arrays.stream(cleanse(s, start, end).split(splitOn)).map(Float::parseFloat).toArray(Float[]::new));
case null -> throw new IllegalArgumentException("Float value required, but not found: " + key);
default -> throw new IllegalArgumentException("Float value required, but not specified: " + key + " = " + val);
};
}
/**
* Возвращает массив значений типа double по ключу.
*
* @param key ключ
* @param splitOn разделитель для парсинга строки в массив
* @param start начальный символ для очистки строки
* @param end конечный символ для очистки строки
* @return массив значений типа double
* @throws IllegalArgumentException если значение не найдено или не может быть преобразовано в массив значений типа double
*/
public double @NotNull [] getParsedDoubleArrayOrThrow(@NotNull String key, @NotNull String splitOn, @Nullable String start, @Nullable String end) throws IllegalArgumentException {
final Object val = internalMap.get(key);
return switch (val) {
case double[] a -> a;
case Double[] a -> ArrayUtils.toPrimitive(a);
case Double v -> new double[]{v};
case String s -> Arrays.stream(cleanse(s, start, end).split(splitOn)).mapToDouble(Double::parseDouble).toArray();
case null -> throw new IllegalArgumentException("Double value required, but not found: " + key);
default -> throw new IllegalArgumentException("Double value required, but not specified: " + key + " = " + val);
};
}
/**
* Помещает объект в карту по ключу, если значение не null.
*
* @param key ключ
* @param value значение
*/
public void putObject(@NotNull String key, @Nullable Object value) {
if (value == null) {
return;
}
internalMap.put(key, value);
}
/**
* Помещает логическое значение в карту по ключу.
*
* @param key ключ
* @param value значение
*/
public void putBoolean(@NotNull String key, boolean value) {
internalMap.put(key, value ? Boolean.TRUE : Boolean.FALSE);
}
/**
* Помещает байтовое значение в карту по ключу.
*
* @param key ключ
* @param value значение
*/
public void putByte(@NotNull String key, byte value) {
internalMap.put(key, value);
}
/**
* Помещает значение типа short в карту по ключу.
*
* @param key ключ
* @param value значение
*/
public void putShort(@NotNull String key, short value) {
internalMap.put(key, value);
}
/**
* Помещает целочисленное значение в карту по ключу.
*
* @param key ключ
* @param value значение
*/
public void putInt(@NotNull String key, int value) {
internalMap.put(key, value);
}
/**
* Помещает значение типа long в карту по ключу.
*
* @param key ключ
* @param value значение
*/
public void putLong(@NotNull String key, long value) {
internalMap.put(key, value);
}
/**
* Помещает значение типа float в карту по ключу.
*
* @param key ключ
* @param value значение
*/
public void putFloat(@NotNull String key, float value) {
internalMap.put(key, value);
}
/**
* Помещает значение типа double в карту по ключу.
*
* @param key ключ
* @param value значение
*/
public void putDouble(@NotNull String key, double value) {
internalMap.put(key, value);
}
/**
* Помещает строковое значение в карту по ключу, если значение не null.
*
* @param key ключ
* @param value значение
*/
public void putString(@NotNull String key, @Nullable String value) {
if (value == null) {
return;
}
internalMap.put(key, value);
}
/**
* Помещает значение перечисления в карту по ключу, если значение не null.
*
* @param key ключ
* @param value значение
*/
public void putEnum(@NotNull String key, @Nullable Enum<?> value) {
if (value == null) {
return;
}
internalMap.put(key, value);
}
/**
* Возвращает строковое представление карты.
*
* @return строковое представление карты
*/
@Override
public String toString() {
return "MultiTypesMap{wrapped=" + internalMap + "}";
}
/**
* Возвращает набор ключей карты.
*
* @return набор ключей карты
*/
@Override
public @NotNull Set<String> keySet() {
return internalMap.keySet();
}
/**
* Возвращает коллекцию значений карты.
*
* @return коллекция значений карты
*/
@Override
public @NotNull Collection<Object> values() {
return internalMap.values();
}
/**
* Возвращает набор записей карты.
*
* @return набор записей карты
*/
@Override
public @NotNull Set<Entry<String, Object>> entrySet() {
return internalMap.entrySet();
}
/**
* Очищает карту.
*/
@Override
public void clear() {
internalMap.clear();
}
/**
* Возвращает размер карты.
*
* @return размер карты
*/
@Override
public int size() {
return internalMap.size();
}
/**
* Проверяет, является ли карта пустой.
*
* @return true, если карта пуста, иначе false
*/
@Override
public boolean isEmpty() {
return internalMap.isEmpty();
}
/**
* Проверяет, содержит ли карта ключ.
*
* @param key ключ
* @return true, если карта содержит ключ, иначе false
*/
@Override
public boolean containsKey(Object key) {
return internalMap.containsKey(key);
}
/**
* Проверяет, содержит ли карта значение.
*
* @param value значение
* @return true, если карта содержит значение, иначе false
*/
@Override
public boolean containsValue(Object value) {
return internalMap.containsValue(value);
}
/**
* Возвращает значение по ключу.
*
* @param key ключ
* @return значение по ключу или null, если ключ не найден
*/
@Override
public @Nullable Object get(Object key) {
return internalMap.get(key);
}
/**
* Помещает значение в карту по ключу.
*
* @param key ключ
* @param value значение
* @return предыдущее значение по ключу или null, если ключ не был найден
*/
@Override
public @Nullable Object put(String key, Object value) {
return internalMap.put(key, value);
}
/**
* Копирует все записи из другой карты в текущую.
*
* @param map карта, из которой нужно скопировать записи
*/
@Override
public void putAll(@NotNull Map<? extends String, ?> map) {
if (Objects.equals(internalMap, map) || map.isEmpty()) {
return;
}
internalMap.putAll(map);
}
/**
* Удаляет запись по ключу.
*
* @param key ключ
* @return удаленное значение или null, если ключ не был найден
*/
@Override
public Object remove(Object key) {
return internalMap.remove(key);
}
/**
* Возвращает значение по ключу или значение по умолчанию, если ключ не найден.
*
* @param key ключ
* @param defaultValue значение по умолчанию
* @return значение по ключу или значение по умолчанию
*/
@Override
public Object getOrDefault(Object key, Object defaultValue) {
return internalMap.getOrDefault(key, defaultValue);
}
/**
* Выполняет заданное действие для каждой записи в карте.
*
* @param action действие, которое нужно выполнить
*/
@Override
public void forEach(BiConsumer<? super String, ? super Object> action) {
internalMap.forEach(action);
}
/**
* Заменяет все значения в карте результатами вызова заданной функции.
*
* @param function функция, которую нужно применить к значениям
*/
@Override
public void replaceAll(BiFunction<? super String, ? super Object, ?> function) {
internalMap.replaceAll(function);
}
/**
* Помещает значение в карту по ключу, если ключ отсутствует.
*
* @param key ключ
* @param value значение
* @return предыдущее значение по ключу или null, если ключ не был найден
*/
@Override
public @Nullable Object putIfAbsent(String key, Object value) {
return internalMap.putIfAbsent(key, value);
}
/**
* Удаляет запись по ключу и значению.
*
* @param key ключ
* @param value значение
* @return true, если запись была удалена, иначе false
*/
@Override
public boolean remove(Object key, Object value) {
return internalMap.remove(key, value);
}
/**
* Заменяет значение по ключу
*
* @param key ключ
* @param oldValue старое значение
* @param newValue новое значение
* @return true, если значение было заменено, иначе false
*/
@Override
public boolean replace(String key, Object oldValue, Object newValue) {
return internalMap.replace(key, oldValue, newValue);
}
/**
* Заменяет значение по ключу.
*
* @param key ключ
* @param value значение
* @return предыдущее значение по ключу или null, если ключ не был найден
*/
@Override
public @Nullable Object replace(String key, Object value) {
return internalMap.replace(key, value);
}
/**
* Вычисляет значение для ключа, если ключ отсутствует, используя заданную функцию.
*
* @param key ключ
* @param mappingFunction функция для вычисления значения
* @return вычисленное значение или null, если функция вернула null
*/
@Override
public Object computeIfAbsent(String key, @NotNull Function<? super String, ?> mappingFunction) {
return internalMap.computeIfAbsent(key, mappingFunction);
}
/**
* Вычисляет новое значение для ключа, если ключ присутствует, используя заданную функцию.
*
* @param key ключ
* @param remappingFunction функция для вычисления нового значения
* @return новое значение или null, если функция вернула null
*/
@Override
public Object computeIfPresent(String key, @NotNull BiFunction<? super String, ? super Object, ?> remappingFunction) {
return internalMap.computeIfPresent(key, remappingFunction);
}
/**
* Вычисляет новое значение для ключа, используя заданную функцию.
*
* @param key ключ
* @param remappingFunction функция для вычисления нового значения
* @return новое значение или null, если функция вернула null
*/
@Override
public Object compute(String key, @NotNull BiFunction<? super String, ? super @Nullable Object, ?> remappingFunction) {
return internalMap.compute(key, remappingFunction);
}
/**
* Объединяет значение по ключу с заданным значением, используя заданную функцию.
*
* @param key ключ
* @param value значение
* @param remappingFunction функция для объединения значений
* @return новое значение или null, если функция вернула null
*/
@Override
public Object merge(String key, @NotNull Object value, @NotNull BiFunction<? super Object, ? super Object, ?> remappingFunction) {
return internalMap.merge(key, value, remappingFunction);
}
/**
* Удаляет начальные и конечные подстроки из строки, если они присутствуют.
*
* @param string строка
* @param start начальная подстрока
* @param end конечная подстрока
* @return очищенная строка
*/
private static String cleanse(String string, String start, String end) {
if (string != null && start != null && end != null) {
if (string.startsWith(start) && string.endsWith(end)) {
string = string.substring(start.length(), string.length() - end.length());
}
}
return string;
}
}