package ru.simplex_software.wicket_springdata.kryo;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.springframework.data.domain.Persistable;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Модель отображения.
*
* @param <K> тип ключей.
* @param <V> тип значений.
*/
public class NewKryoMapModel<K, V> implements IModel<Map<K, V>> {
private final Map<IModel<K>, IModel<V>> modelMap;
/**
* Загружена ли модель.
*/
private boolean attached;
/**
* Создание новой модели отображеия.
*
* @param map исходное отображение.
*/
public NewKryoMapModel(Map<K, V> map) {
this.modelMap = wrap(map);
attached = true;
}
@Override
public Map<K, V> getObject() {
checkLoad();
return unwrap(modelMap);
}
/**
* Открепление модели.
*/
@Override
public void detach() {
modelMap.keySet().forEach(IModel::detach);
modelMap.values().forEach(IModel::detach);
attached = false;
}
/**
* Получение значения по модели ключа.
*
* @param keyModel модель ключа.
* @return значение.
*/
public V get(IModel<K> keyModel) {
return getValueModel(keyModel).getObject();
}
/**
* Получение модели значения по модели ключа.
*
* @param keyModel модель ключа.
* @return модель значения.
*/
public IModel<V> getValueModel(IModel<K> keyModel) {
checkLoad();
return modelMap.get(keyModel);
}
/**
* Получение итератора ключей.
*/
public Iterator<IModel<K>> getKeysIterator() {
checkLoad();
return modelMap.keySet().iterator();
}
/**
* Проверка загрузки и загрузка при необходимости.
*/
private void checkLoad() {
if (!attached) {
modelMap.keySet().forEach(this::loadModel);
modelMap.values().forEach(this::loadModel);
}
attached = true;
}
/**
* Загрузка модели.
*
* @param model модель.
*/
private void loadModel(IModel<?> model) {
if (model instanceof KryoModel) {
((KryoModel<?>) model).load();
}
}
/**
* Оборачивание ключа в модель.
*
* @param key ключ.
* @return модель ключа.
*/
@SuppressWarnings("unchecked")
private IModel<K> wrapKey(K key) {
return (IModel<K>) wrapObject(key);
}
/**
* Оборачивание значения в модель.
*
* @param value значение.
* @return модель значения.
*/
@SuppressWarnings("unchecked")
private IModel<V> wrapValue(V value) {
return (IModel<V>) wrapObject(value);
}
/**
* Оборачивание объекта в модель.
* Объект должен реализовывать интерфейс {@link Persistable} или {@link Serializable}.
*
* @param object объект.
* @return модель объекта.
* @throws IllegalArgumentException если объект не {@link Persistable} и не {@link Serializable}.
* @see Persistable
* @see Serializable
*/
@SuppressWarnings("unchecked")
private IModel<?> wrapObject(Object object) throws IllegalArgumentException {
if (object instanceof Persistable<?>) {
return new KryoModel<>((Persistable<? extends Serializable>) object);
} else if (object instanceof Serializable) {
return Model.of((Serializable) object);
} else throw new IllegalArgumentException("object must be Persistable or Serializable");
}
/**
* Оборачивание ключей и значений отображения в модели.
*
* @param map отображение.
* @return отображение моделей.
*/
private Map<IModel<K>, IModel<V>> wrap(Map<K, V> map) {
Map<IModel<K>, IModel<V>> modelMap = new LinkedHashMap<>();
map.forEach((key, value) -> modelMap.put(wrapKey(key), wrapValue(value)));
return modelMap;
}
/**
* Получение отображения с ключами и значениями из моделей.
*
* @param modelMap отображение моделей.
* @return отображение ключей и значений.
*/
private Map<K, V> unwrap(Map<IModel<K>, IModel<V>> modelMap) {
return modelMap.entrySet().stream()
.collect(Collectors.toMap(
entry -> entry.getKey().getObject(),
entry -> entry.getValue().getObject()));
}
}