All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.cache.impl.EncoderCache Maven / Gradle / Ivy

package org.infinispan.cache.impl;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.security.auth.Subject;

import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.CacheCollection;
import org.infinispan.CacheSet;
import org.infinispan.CacheStream;
import org.infinispan.commands.read.AbstractCloseableIteratorCollection;
import org.infinispan.commons.dataconversion.Encoder;
import org.infinispan.commons.dataconversion.Wrapper;
import org.infinispan.commons.util.CloseableIterator;
import org.infinispan.commons.util.CloseableIteratorMapper;
import org.infinispan.commons.util.CloseableSpliterator;
import org.infinispan.commons.util.CloseableSpliteratorMapper;
import org.infinispan.compat.BiFunctionMapper;
import org.infinispan.compat.FunctionMapper;
import org.infinispan.container.InternalEntryFactory;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.container.entries.ForwardingCacheEntry;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.context.Flag;
import org.infinispan.encoding.DataConversion;
import org.infinispan.factories.ComponentRegistry;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.metadata.Metadata;
import org.infinispan.notifications.cachelistener.ListenerHolder;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilter;
import org.infinispan.util.AbstractDelegatingCacheStream;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;

/**
 * Cache decoration that makes use of the {@link Encoder} and {@link Wrapper} to convert between storage value and
 * read/write value.
 *
 * @since 9.1
 */
public class EncoderCache extends AbstractDelegatingAdvancedCache {

   private static Log log = LogFactory.getLog(EncoderCache.class);

   private final DataConversion keyDataConversion;
   private final DataConversion valueDataConversion;

   private InternalEntryFactory entryFactory;
   private ComponentRegistry componentRegistry;

   private final Function decodedValueForRead = this::valueFromStorage;

   public EncoderCache(AdvancedCache cache, DataConversion keyDataConversion, DataConversion valueDataConversion) {
      super(cache, c -> new EncoderCache<>(c, keyDataConversion, valueDataConversion));
      this.keyDataConversion = keyDataConversion;
      this.valueDataConversion = valueDataConversion;
   }

   private Set encodeKeysForWrite(Set keys) {
      if (needsEncoding(keys)) {
         return keys.stream().map(this::keyToStorage).collect(Collectors.toCollection(LinkedHashSet::new));
      }
      return keys;
   }

   private boolean needsEncoding(Collection keys) {
      return keys.stream().anyMatch(k -> !k.equals(keyToStorage(k)));
   }

   private Collection encodeKeysForWrite(Collection keys) {
      if (needsEncoding(keys)) {
         return keys.stream().map(this::keyToStorage).collect(Collectors.toCollection(ArrayList::new));
      }
      return keys;
   }

   public K keyToStorage(Object key) {
      return (K) keyDataConversion.toStorage(key);
   }

   public V valueToStorage(Object value) {
      return (V) valueDataConversion.toStorage(value);
   }

   public K keyFromStorage(Object key) {
      return (K) keyDataConversion.fromStorage(key);
   }

   public V valueFromStorage(Object value) {
      return (V) valueDataConversion.fromStorage(value);
   }

   @Inject
   public void wireRealCache(ComponentRegistry registry, InternalEntryFactory entryFactory) {
      this.entryFactory = entryFactory;
      this.componentRegistry = registry;
      registry.wireDependencies(keyDataConversion);
      registry.wireDependencies(valueDataConversion);
      registry.wireDependencies(cache);
   }

   private Map encodeMapForWrite(Map map) {
      Map newMap = new HashMap<>(map.size());
      map.forEach((k, v) -> newMap.put(keyToStorage(k), valueToStorage(v)));
      return newMap;
   }

   private Map decodeMapForRead(Map map) {
      Map newMap = new LinkedHashMap<>(map.size());
      map.forEach((k, v) -> newMap.put(keyFromStorage(k), valueFromStorage(v)));
      return newMap;
   }

   private CacheEntry convertEntry(K newKey, V newValue, CacheEntry entry) {
      if (entry instanceof InternalCacheEntry) {
         return entryFactory.create(newKey, newValue, (InternalCacheEntry) entry);
      } else {
         return entryFactory.create(newKey, newValue, entry.getMetadata().version(), entry.getCreated(),
               entry.getLifespan(), entry.getLastUsed(), entry.getMaxIdle());
      }
   }

   private BiFunction convertFunction(
         BiFunction remappingFunction) {
      return (k, v) -> valueToStorage(remappingFunction.apply(keyFromStorage(k), valueFromStorage(v)));
   }

   private Map> decodeEntryMapForRead(Map> map) {
      Map> entryMap = new HashMap<>(map.size());
      map.values().forEach(v -> {
         K originalKey = v.getKey();
         K unwrappedKey = keyFromStorage(originalKey);
         V originalValue = v.getValue();
         V unwrappedValue = valueFromStorage(originalValue);
         CacheEntry entryToPut;
         if (unwrappedKey != originalKey || unwrappedValue != originalValue) {
            entryToPut = convertEntry(unwrappedKey, unwrappedValue, v);
         } else {
            entryToPut = v;
         }
         entryMap.put(unwrappedKey, entryToPut);
      });
      return entryMap;
   }

   private class EncodedCacheStream extends AbstractDelegatingCacheStream {

      public EncodedCacheStream(CacheStream stream) {
         super(stream);
      }

      @Override
      public AbstractDelegatingCacheStream filterKeys(Set keys) {
         return super.filterKeys(encodeKeysForWrite(keys));
      }
   }

   private class EncodedKeySet extends AbstractCloseableIteratorCollection implements CacheSet {
      private final CacheSet actualCollection;
      private final EncoderKeyMapper keyMapper = new EncoderKeyMapper(keyDataConversion);

      EncodedKeySet(Cache cache, CacheSet actualCollection) {
         super(cache);
         this.actualCollection = actualCollection;
      }

      @Override
      public CacheStream stream() {
         return new EncodedCacheStream<>(actualCollection.stream().map(keyMapper));
      }

      @Override
      public CacheStream parallelStream() {
         return new EncodedCacheStream<>(actualCollection.parallelStream().map(keyMapper));
      }

      @Override
      public CloseableIterator iterator() {
         return new CloseableIteratorMapper<>(actualCollection.iterator(), EncoderCache.this::keyFromStorage);
      }

      @Override
      public CloseableSpliterator spliterator() {
         return new CloseableSpliteratorMapper<>(actualCollection.spliterator(), EncoderCache.this::keyFromStorage);
      }

      @Override
      public boolean contains(Object o) {
         return actualCollection.contains(keyToStorage(o));
      }

      @Override
      public boolean remove(Object o) {
         return actualCollection.remove(keyToStorage(o));
      }
   }

   @Override
   public void putForExternalRead(K key, V value) {
      super.putForExternalRead(keyToStorage(key), valueToStorage(value));
   }

   @Override
   public void putForExternalRead(K key, V value, long lifespan, TimeUnit unit) {
      super.putForExternalRead(keyToStorage(key), valueToStorage(value), lifespan, unit);
   }

   @Override
   public void putForExternalRead(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
      super.putForExternalRead(key, value, lifespan, lifespanUnit, maxIdle, maxIdleUnit);
   }

   @Override
   public void evict(K key) {
      super.evict(keyToStorage(key));
   }

   @Override
   public V put(K key, V value, long lifespan, TimeUnit unit) {
      V ret = super.put(keyToStorage(key), valueToStorage(value), lifespan, unit);
      return valueFromStorage(ret);
   }

   @Override
   public DataConversion getKeyDataConversion() {
      return keyDataConversion;
   }

   @Override
   public DataConversion getValueDataConversion() {
      return valueDataConversion;
   }

   @Override
   protected void set(K key, V value) {
      super.set(keyToStorage(key), valueToStorage(value));
   }

   @Override
   public V putIfAbsent(K key, V value, long lifespan, TimeUnit unit) {
      V v = super.putIfAbsent(keyToStorage(key), valueToStorage(value), lifespan, unit);
      return valueFromStorage(v);
   }

   @Override
   public void putAll(Map map, long lifespan, TimeUnit unit) {
      super.putAll(encodeMapForWrite(map), lifespan, unit);
   }

   @Override
   public V replace(K key, V value, long lifespan, TimeUnit unit) {
      V ret = super.replace(keyToStorage(key), valueToStorage(value), lifespan, unit);
      return valueFromStorage(ret);
   }

   @Override
   public boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit unit) {
      return super.replace(keyToStorage(key), valueToStorage(oldValue), valueToStorage(value), lifespan, unit);
   }

   @Override
   public V put(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
      V ret = super.put(keyToStorage(key), valueToStorage(value), lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
      return valueFromStorage(ret);
   }

   @Override
   public V putIfAbsent(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
      V ret = super.putIfAbsent(keyToStorage(key), valueToStorage(value), lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
      return valueFromStorage(ret);
   }

   @Override
   public void putAll(Map map, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
      super.putAll(encodeMapForWrite(map), lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
   }


   @Override
   public V replace(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
      V ret = super.replace(keyToStorage(key), valueToStorage(value), lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
      return valueFromStorage(ret);
   }

   @Override
   public boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
      return super.replace(keyToStorage(key), valueToStorage(oldValue), valueToStorage(value), lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
   }

   @Override
   public void replaceAll(BiFunction function) {
      super.replaceAll(convertFunction(function));
   }

   @Override
   public CompletableFuture putAsync(K key, V value) {
      return super.putAsync(keyToStorage(key), valueToStorage(value)).thenApply(decodedValueForRead);
   }

   @Override
   public CompletableFuture putAsync(K key, V value, long lifespan, TimeUnit unit) {
      return super.putAsync(keyToStorage(key), valueToStorage(value), lifespan, unit).thenApply(decodedValueForRead);
   }

   @Override
   public CompletableFuture putAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
      return super.putAsync(keyToStorage(key), valueToStorage(value), lifespan, lifespanUnit, maxIdle, maxIdleUnit).thenApply(decodedValueForRead);
   }

   @Override
   public CompletableFuture putAllAsync(Map data) {
      return super.putAllAsync(encodeMapForWrite(data));
   }

   @Override
   public CompletableFuture putAllAsync(Map data, long lifespan, TimeUnit unit) {
      return super.putAllAsync(encodeMapForWrite(data), lifespan, unit);
   }

   @Override
   public CompletableFuture putAllAsync(Map data, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
      return super.putAllAsync(encodeMapForWrite(data), lifespan, lifespanUnit, maxIdle, maxIdleUnit);
   }

   @Override
   public CompletableFuture putIfAbsentAsync(K key, V value) {
      return super.putIfAbsentAsync(keyToStorage(key), valueToStorage(value)).thenApply(decodedValueForRead);
   }

   @Override
   public CompletableFuture putIfAbsentAsync(K key, V value, long lifespan, TimeUnit unit) {
      return super.putIfAbsentAsync(keyToStorage(key), valueToStorage(value), lifespan, unit).thenApply(decodedValueForRead);
   }

   @Override
   public boolean lock(K... keys) {
      K[] encoded = (K[]) Arrays.stream(keys).map(this::keyToStorage).toArray();
      return super.lock(encoded);
   }

   @Override
   public boolean lock(Collection keys) {
      return super.lock(encodeKeysForWrite(keys));
   }

   @Override
   public CompletableFuture putIfAbsentAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
      return super.putIfAbsentAsync(keyToStorage(key), valueToStorage(value), lifespan, lifespanUnit, maxIdle, maxIdleUnit).thenApply(decodedValueForRead);
   }

   @Override
   public CompletableFuture removeAsync(Object key) {
      return super.removeAsync(keyToStorage(key)).thenApply(decodedValueForRead);
   }

   @Override
   public CompletableFuture removeAsync(Object key, Object value) {
      return super.removeAsync(keyToStorage(key), valueToStorage(value));
   }

   @Override
   public CompletableFuture replaceAsync(K key, V value) {
      return super.replaceAsync(keyToStorage(key), valueToStorage(value)).thenApply(decodedValueForRead);
   }

   @Override
   public CompletableFuture replaceAsync(K key, V value, long lifespan, TimeUnit unit) {
      return super.replaceAsync(keyToStorage(key), valueToStorage(value), lifespan, unit).thenApply(decodedValueForRead);
   }

   @Override
   public Map getAll(Set keys) {
      Map ret = super.getAll(encodeKeysForWrite(keys));
      return decodeMapForRead(ret);
   }

   @Override
   public CacheEntry getCacheEntry(Object key) {
      K keyToStorage = keyToStorage(key);
      CacheEntry returned = super.getCacheEntry(keyToStorage);
      if (returned != null) {
         V originalValue = returned.getValue();
         V valueFromStorage = valueFromStorage(originalValue);
         if (keyToStorage != key || valueFromStorage != originalValue) {
            return convertEntry((K) key, valueFromStorage, returned);
         }
      }
      return returned;
   }

   @Override
   public CompletableFuture replaceAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
      return super.replaceAsync(keyToStorage(key), valueToStorage(value), lifespan, lifespanUnit, maxIdle, maxIdleUnit)
            .thenApply(decodedValueForRead);
   }

   @Override
   public Map> getAllCacheEntries(Set keys) {
      Map> returned = super.getAllCacheEntries(encodeKeysForWrite(keys));
      return decodeEntryMapForRead(returned);
   }

   @Override
   public Map getGroup(String groupName) {
      Map ret = super.getGroup(groupName);
      return decodeMapForRead(ret);
   }

   @Override
   public CompletableFuture replaceAsync(K key, V oldValue, V newValue) {
      return super.replaceAsync(keyToStorage(key), valueToStorage(oldValue), valueToStorage(newValue));
   }

   @Override
   public CompletableFuture replaceAsync(K key, V oldValue, V newValue, long lifespan, TimeUnit unit) {
      return super.replaceAsync(keyToStorage(key), valueToStorage(oldValue), valueToStorage(newValue), lifespan, unit);
   }

   @Override
   public V put(K key, V value, Metadata metadata) {
      V ret = super.put(keyToStorage(key), valueToStorage(value), metadata);
      return valueFromStorage(ret);
   }

   @Override
   public V replace(K key, V value, Metadata metadata) {
      V ret = super.replace(keyToStorage(key), valueToStorage(value), metadata);
      return valueFromStorage(ret);
   }

   @Override
   public CompletableFuture replaceAsync(K key, V oldValue, V newValue, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
      return super.replaceAsync(keyToStorage(key), valueToStorage(oldValue), valueToStorage(newValue), lifespan, lifespanUnit, maxIdle, maxIdleUnit);
   }

   @Override
   public boolean replace(K key, V oldValue, V value, Metadata metadata) {
      return super.replace(keyToStorage(key), valueToStorage(oldValue), valueToStorage(value), metadata);
   }

   @Override
   public V putIfAbsent(K key, V value, Metadata metadata) {
      V ret = super.putIfAbsent(keyToStorage(key), valueToStorage(value), metadata);
      return valueFromStorage(ret);
   }

   @Override
   public CompletableFuture putAsync(K key, V value, Metadata metadata) {
      return super.putAsync(keyToStorage(key), valueToStorage(value), metadata).thenApply(decodedValueForRead);
   }

   @Override
   public void putForExternalRead(K key, V value, Metadata metadata) {
      super.putForExternalRead(keyToStorage(key), valueToStorage(value), metadata);
   }

   @Override
   public void putAll(Map map, Metadata metadata) {
      super.putAll(encodeMapForWrite(map), metadata);
   }

   @Override
   public CacheSet> cacheEntrySet() {
      return new EncoderEntrySet(this, super.cacheEntrySet());
   }

   @Override
   public void removeExpired(K key, V value, Long lifespan) {
      super.removeExpired(keyToStorage(key), valueToStorage(value), lifespan);
   }

   @Override
   public V putIfAbsent(K key, V value) {
      V ret = super.putIfAbsent(keyToStorage(key), valueToStorage(value));
      return valueFromStorage(ret);
   }

   private void lookupEncoderWrapper() {
      ComponentStatus status = cache.getAdvancedCache().getComponentRegistry().getStatus();
      if (!status.equals(ComponentStatus.STOPPING) && !status.equals(ComponentStatus.TERMINATED)) {
         componentRegistry.wireDependencies(keyDataConversion);
         componentRegistry.wireDependencies(valueDataConversion);
      }
   }

   private void initState(EncoderCache encoderCache, EncoderCache template) {
      encoderCache.entryFactory = template.entryFactory;
      encoderCache.componentRegistry = template.componentRegistry;
      encoderCache.lookupEncoderWrapper();
   }

   @Override
   public AdvancedCache lockAs(Object lockOwner) {
      AdvancedCache returned = super.lockAs(lockOwner);
      if (returned != this && returned instanceof EncoderCache) {
         initState((EncoderCache) returned, this);
      }
      return returned;
   }

   @Override
   public AdvancedCache withEncoding(Class keyEncoderClass, Class valueEncoderClass) {
      checkSubclass(keyEncoderClass, Encoder.class);
      checkSubclass(valueEncoderClass, Encoder.class);
      DataConversion newKeyDataConversion = keyDataConversion.withEncoding(keyEncoderClass);
      DataConversion newValueDataConversion = valueDataConversion.withEncoding(valueEncoderClass);
      EncoderCache encoderCache = new EncoderCache<>(cache, newKeyDataConversion, newValueDataConversion);
      initState(encoderCache, this);
      return encoderCache;
   }

   @Override
   public AdvancedCache withEncoding(Class encoderClass) {
      checkSubclass(encoderClass, Encoder.class);
      DataConversion newKeyDataConversion = keyDataConversion.withEncoding(encoderClass);
      DataConversion newValueDataConversion = valueDataConversion.withEncoding(encoderClass);
      EncoderCache encoderCache = new EncoderCache<>(cache, newKeyDataConversion, newValueDataConversion);
      initState(encoderCache, this);
      return encoderCache;
   }

   private void checkSubclass(Class configured, Class required) {
      if (!required.isAssignableFrom(configured)) {
         throw log.invalidEncodingClass(configured, required);
      }
   }

   @Override
   public AdvancedCache withWrapping(Class keyWrapperClass, Class valueWrapperClass) {
      checkSubclass(keyWrapperClass, Wrapper.class);
      checkSubclass(valueWrapperClass, Wrapper.class);
      DataConversion newKeyDataConversion = keyDataConversion.withWrapping(keyWrapperClass);
      DataConversion newValueDataConversion = valueDataConversion.withWrapping(valueWrapperClass);
      EncoderCache encoderCache = new EncoderCache<>(cache, newKeyDataConversion, newValueDataConversion);
      initState(encoderCache, this);
      return encoderCache;
   }

   @Override
   public AdvancedCache withWrapping(Class wrapper) {
      return withWrapping(wrapper, wrapper);
   }

   @Override
   public AdvancedCache withFlags(Flag... flags) {
      AdvancedCache returned = super.withFlags(flags);
      if (returned != this && returned instanceof EncoderCache) {
         initState((EncoderCache) returned, this);
      }
      return returned;
   }

   @Override
   public AdvancedCache withSubject(Subject subject) {
      AdvancedCache returned = super.withSubject(subject);
      if (returned != this && returned instanceof EncoderCache) {
         initState((EncoderCache) returned, this);
      }
      return returned;
   }

   @Override
   public AdvancedCache with(ClassLoader classLoader) {
      AdvancedCache returned = super.with(classLoader);
      if (returned != this && returned instanceof EncoderCache) {
         initState((EncoderCache) returned, this);
      }
      return returned;
   }

   @Override
   public boolean remove(Object key, Object value) {
      return super.remove(keyToStorage(key), valueToStorage(value));
   }

   @Override
   public boolean replace(K key, V oldValue, V newValue) {
      return super.replace(keyToStorage(key), valueToStorage(oldValue), valueToStorage(newValue));
   }

   @Override
   public V replace(K key, V value) {
      V ret = super.replace(keyToStorage(key), valueToStorage(value));
      return valueFromStorage(ret);
   }

   @Override
   public boolean containsKey(Object key) {
      return super.containsKey(keyToStorage(key));
   }

   @Override
   public boolean containsValue(Object value) {
      return super.containsValue(valueToStorage(value));
   }

   @Override
   public V compute(K key, BiFunction remappingFunction) {
      Object returned = super.compute(keyToStorage(key),
            new BiFunctionMapper(remappingFunction, keyDataConversion, valueDataConversion));
      return valueFromStorage(returned);
   }

   @Override
   public V computeIfPresent(K key, BiFunction remappingFunction) {
      Object returned = super.computeIfPresent(keyToStorage(key),
            new BiFunctionMapper(remappingFunction, keyDataConversion, valueDataConversion));
      return valueFromStorage(returned);
   }

   @Override
   public V computeIfAbsent(K key, Function mappingFunction) {
      Object ret = super.computeIfAbsent(keyToStorage(key),
            new FunctionMapper(mappingFunction, keyDataConversion, valueDataConversion));
      return valueFromStorage(ret);
   }

   @Override
   public V get(Object key) {
      V v = super.get(keyToStorage(key));
      return valueFromStorage(v);
   }

   @Override
   public V getOrDefault(Object key, V defaultValue) {
      V returned = super.getOrDefault(keyToStorage(key), defaultValue);
      if (returned == defaultValue) {
         return returned;
      }
      return valueFromStorage(returned);
   }

   @Override
   public V put(K key, V value) {
      V ret = super.put(keyToStorage(key), valueToStorage(value));
      if (ret == null) {
         return null;
      }
      return valueFromStorage(ret);
   }

   @Override
   public V remove(Object key) {
      V ret = super.remove(keyToStorage(key));
      return valueFromStorage(ret);
   }

   @Override
   public void putAll(Map t) {
      super.putAll(encodeMapForWrite(t));
   }

   @Override
   public V merge(K key, V value, BiFunction remappingFunction) {
      Object returned = super.merge(keyToStorage(key), valueToStorage(value),
            new BiFunctionMapper(remappingFunction, keyDataConversion, valueDataConversion));
      return valueFromStorage(returned);
   }

   @Override
   public void forEach(BiConsumer action) {
      super.forEach((k, v) -> {
         K newK = keyFromStorage(k);
         V newV = valueFromStorage(v);
         action.accept(newK, newV);
      });
   }

   @Override
   public CacheSet keySet() {
      return new EncodedKeySet(this, super.keySet());
   }


   private class EncoderIterator implements CloseableIterator> {
      private final CloseableIterator> iterator;
      private final InternalEntryFactory entryFactory;

      EncoderIterator(CloseableIterator> iterator, InternalEntryFactory entryFactory) {
         this.iterator = iterator;
         this.entryFactory = entryFactory;
      }

      @Override
      public void close() {
         iterator.close();
      }

      @Override
      public boolean hasNext() {
         return iterator.hasNext();
      }

      @Override
      public CacheEntry next() {
         CacheEntry entry = iterator.next();
         return new EntryWrapper<>(entry, convert(entry));
      }

      private CacheEntry convert(CacheEntry entry) {
         A newKey = (A) keyFromStorage(entry.getKey());
         B newValue = (B) valueFromStorage(entry.getValue());

         // If either value changed then make a copy
         if (newKey != entry.getKey() || newValue != entry.getValue()) {
            if (entry instanceof InternalCacheEntry) {
               return entryFactory.create(newKey, newValue, (InternalCacheEntry) entry);
            }
            return entryFactory.create(newKey, newValue, entry.getMetadata());
         }
         return entry;
      }

      @Override
      public void remove() {
         iterator.remove();
      }
   }

   private class EncoderEntrySet extends AbstractCloseableIteratorCollection, K, V> implements CacheSet> {
      private CacheSet> actualCollection;
      private EncoderEntryMapper entryMapper;


      EncoderEntrySet(Cache cache, CacheSet> actualCollection) {
         super(cache);
         this.entryMapper = new EncoderEntryMapper(keyDataConversion, valueDataConversion);
         this.actualCollection = actualCollection;
      }

      @Override
      public CacheStream> stream() {
         return new EncodedCacheStream<>(actualCollection.stream().map(entryMapper));
      }

      @Override
      public CacheStream> parallelStream() {
         return new EncodedCacheStream<>(actualCollection.parallelStream().map(entryMapper));
      }

      @Override
      public CloseableIterator> iterator() {
         return new EncoderIterator<>(actualCollection.iterator(), entryFactory);
      }

      @Override
      public CloseableSpliterator> spliterator() {
         return new CloseableSpliteratorMapper<>(actualCollection.spliterator(),
               entry -> {
                  K key = entry.getKey();
                  K keyFromStorage = keyFromStorage(key);
                  V value = entry.getValue();
                  V valueFromStorage = valueFromStorage(value);
                  if (keyFromStorage != key || valueFromStorage != value) {
                     return convertEntry(keyFromStorage, valueFromStorage, entry);
                  }
                  return entry;
               });
      }

      @Override
      public boolean contains(Object o) {
         Map.Entry entry = toEntry(o);
         if (entry != null) {
            return actualCollection.contains(entry);
         }
         return false;
      }

      @Override
      public boolean remove(Object o) {
         Map.Entry entry = toEntry(o);
         if (entry != null) {
            return actualCollection.remove(entry);
         }
         return false;
      }

      Map.Entry toEntry(Object o) {
         if (o instanceof Map.Entry) {
            Map.Entry entry = (Map.Entry) o;
            K key = entry.getKey();
            K newKey = keyToStorage(key);
            V value = entry.getValue();
            V newValue = valueToStorage(value);
            if (key != newKey || value != newValue) {
               if (o instanceof CacheEntry) {
                  CacheEntry returned = (CacheEntry) o;
                  return convertEntry(newKey, newValue, returned);
               } else {
                  return entryFactory.create(newKey, newValue, (Metadata) null);
               }
            }
            return entry;
         }
         return null;
      }
   }

   private class EntryWrapper extends ForwardingCacheEntry {
      private final CacheEntry previousEntry;
      private final CacheEntry entry;

      EntryWrapper(CacheEntry previousEntry, CacheEntry entry) {
         this.previousEntry = previousEntry;
         this.entry = entry;
      }

      @Override
      protected CacheEntry delegate() {
         return entry;
      }

      @Override
      public B setValue(B value) {
         previousEntry.setValue((B) valueToStorage(value));
         return super.setValue(value);
      }
   }

   @Override
   public CacheSet> entrySet() {
      return cast(new EncoderEntrySet(this, cast(super.cacheEntrySet())));

   }

   private > CacheSet cast(CacheSet set) {
      return (CacheSet) set;
   }


   private class EncoderValuesCollection extends AbstractCloseableIteratorCollection implements CacheCollection {
      private final CacheCollection actualCollection;
      final EncoderValueMapper valueMapper = new EncoderValueMapper(valueDataConversion);

      EncoderValuesCollection(Cache cache, CacheCollection actualCollection) {
         super(cache);
         this.actualCollection = actualCollection;
      }

      @Override
      public CacheStream stream() {
         return new EncodedCacheStream<>(actualCollection.stream().map(valueMapper));
      }

      @Override
      public CacheStream parallelStream() {
         return new EncodedCacheStream<>(actualCollection.parallelStream().map(valueMapper));
      }

      @Override
      public CloseableIterator iterator() {
         return new CloseableIteratorMapper<>(actualCollection.iterator(), EncoderCache.this::valueFromStorage);
      }

      @Override
      public CloseableSpliterator spliterator() {
         return new CloseableSpliteratorMapper<>(actualCollection.spliterator(), EncoderCache.this::valueFromStorage);
      }

      @Override
      public boolean contains(Object o) {
         return actualCollection.contains(valueToStorage(o));
      }

      @Override
      public boolean remove(Object o) {
         return actualCollection.remove(valueToStorage(o));
      }
   }

   @Override
   public CacheCollection values() {
      return new EncoderValuesCollection(this, super.values());
   }

   @Override
   public CompletableFuture getAsync(K key) {
      return super.getAsync(keyToStorage(key)).thenApply(decodedValueForRead);
   }

   @Override
   public void addListener(Object listener) {
      ListenerHolder listenerHolder = new ListenerHolder(listener, keyDataConversion, valueDataConversion);
      Cache unwrapped = super.unwrapCache(this.cache);
      if (unwrapped instanceof CacheImpl) {
         ((CacheImpl) unwrapped).addListener(listenerHolder);
      } else {
         super.addListener(listener);
      }
   }

   @Override
   public  void addListener(Object listener, CacheEventFilter filter,
                               CacheEventConverter converter) {
      ListenerHolder listenerHolder = new ListenerHolder(listener, keyDataConversion, valueDataConversion);
      Cache unwrapped = super.unwrapCache(this.cache);
      if (unwrapped instanceof CacheImpl) {
         ((CacheImpl) unwrapped).addListener(listenerHolder, filter, converter);
      } else {
         super.addListener(listener);
      }

   }

   @Override
   public  void addFilteredListener(Object listener,
                                       CacheEventFilter filter,
                                       CacheEventConverter converter,
                                       Set> filterAnnotations) {
      ListenerHolder listenerHolder = new ListenerHolder(listener, keyDataConversion, valueDataConversion);
      Cache unwrapped = super.unwrapCache(this.cache);
      if (unwrapped instanceof CacheImpl) {
         ((CacheImpl) unwrapped).addFilteredListener(listenerHolder, filter, converter, filterAnnotations);
      } else {
         super.addFilteredListener(listener, filter, converter, filterAnnotations);
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy