<commit_before>/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * 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 com.hazelcast.cache.impl;

import com.hazelcast.cache.CacheNotExistsException;
import com.hazelcast.cache.impl.maxsize.MaxSizeChecker;
import com.hazelcast.cache.impl.maxsize.impl.EntryCountCacheMaxSizeChecker;
import com.hazelcast.cache.impl.record.CacheRecord;
import com.hazelcast.cache.impl.record.SampleableCacheRecordMap;
import com.hazelcast.config.CacheConfig;
import com.hazelcast.config.EvictionConfig;
import com.hazelcast.config.InMemoryFormat;
import com.hazelcast.core.HazelcastInstanceAware;
import com.hazelcast.internal.eviction.EvictionChecker;
import com.hazelcast.internal.eviction.EvictionListener;
import com.hazelcast.internal.eviction.EvictionPolicyEvaluator;
import com.hazelcast.internal.eviction.EvictionPolicyEvaluatorProvider;
import com.hazelcast.internal.eviction.EvictionStrategy;
import com.hazelcast.internal.eviction.EvictionStrategyProvider;
import com.hazelcast.internal.eviction.impl.EvictionConfigHelper;
import com.hazelcast.map.impl.MapEntries;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.spi.EventRegistration;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.impl.eventservice.InternalEventService;
import com.hazelcast.spi.partition.IPartition;
import com.hazelcast.util.Clock;
import com.hazelcast.util.EmptyStatement;
import com.hazelcast.util.ExceptionUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import javax.cache.configuration.Factory;
import javax.cache.expiry.Duration;
import javax.cache.expiry.ExpiryPolicy;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheLoaderException;
import javax.cache.integration.CacheWriter;
import javax.cache.integration.CacheWriterException;
import javax.cache.processor.EntryProcessor;
import java.io.Closeable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static com.hazelcast.cache.impl.CacheEventContextUtil.createBaseEventContext;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheCompleteEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheCreatedEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheExpiredEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheRemovedEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheUpdatedEvent;
import static com.hazelcast.cache.impl.operation.MutableOperation.IGNORE_COMPLETION;
import static com.hazelcast.cache.impl.record.CacheRecordFactory.isExpiredAt;

/**
 * @author sozal 14/10/14
 */
public abstract class AbstractCacheRecordStore<R extends CacheRecord, CRM extends SampleableCacheRecordMap<Data, R>>
        implements ICacheRecordStore, EvictionListener<Data, R> {

    protected static final int DEFAULT_INITIAL_CAPACITY = 256;
    protected static final String SOURCE_NOT_AVAILABLE = "<NA>";

    protected final String name;
    protected final int partitionId;
    protected final int partitionCount;
    protected final NodeEngine nodeEngine;
    protected final AbstractCacheService cacheService;
    protected final CacheConfig cacheConfig;
    protected CRM records;
    protected CacheContext cacheContext;
    protected CacheStatisticsImpl statistics;
    protected CacheLoader cacheLoader;
    protected CacheWriter cacheWriter;
    protected boolean eventsEnabled = true;
    protected boolean eventsBatchingEnabled;
    protected ExpiryPolicy defaultExpiryPolicy;
    protected final EvictionConfig evictionConfig;
    protected final Map<CacheEventType, Set<CacheEventData>> batchEvent = new HashMap<CacheEventType, Set<CacheEventData>>();
    protected final MaxSizeChecker maxSizeChecker;
    protected final EvictionPolicyEvaluator<Data, R> evictionPolicyEvaluator;
    protected final EvictionChecker evictionChecker;
    protected final EvictionStrategy<Data, R, CRM> evictionStrategy;
    protected final boolean wanReplicationEnabled;
    protected final boolean disablePerEntryInvalidationEvents;
    protected boolean primary;

    //CHECKSTYLE:OFF
    public AbstractCacheRecordStore(String name, int partitionId, NodeEngine nodeEngine,
                                    AbstractCacheService cacheService) {
        this.name = name;
        this.partitionId = partitionId;
        this.nodeEngine = nodeEngine;
        this.partitionCount = nodeEngine.getPartitionService().getPartitionCount();
        this.cacheService = cacheService;
        this.cacheConfig = cacheService.getCacheConfig(name);
        if (cacheConfig == null) {
            throw new CacheNotExistsException("Cache " + name + " is already destroyed or not created yet, on "
                    + nodeEngine.getLocalMember());
        }
        evictionConfig = cacheConfig.getEvictionConfig();
        if (evictionConfig == null) {
            throw new IllegalStateException("Eviction config cannot be null!");
        }
        wanReplicationEnabled = cacheService.isWanReplicationEnabled(name);
        disablePerEntryInvalidationEvents = cacheConfig.isDisablePerEntryInvalidationEvents();
        if (cacheConfig.isStatisticsEnabled()) {
            statistics = cacheService.createCacheStatIfAbsent(name);
        }
        if (cacheConfig.getCacheLoaderFactory() != null) {
            Factory<CacheLoader> cacheLoaderFactory = cacheConfig.getCacheLoaderFactory();
            injectDependencies(cacheLoaderFactory);
            cacheLoader = cacheLoaderFactory.create();
            injectDependencies(cacheLoader);
        }
        if (cacheConfig.getCacheWriterFactory() != null) {
            Factory<CacheWriter> cacheWriterFactory = cacheConfig.getCacheWriterFactory();
            injectDependencies(cacheWriterFactory);
            cacheWriter = cacheWriterFactory.create();
            injectDependencies(cacheWriter);
        }
        if (cacheConfig.getExpiryPolicyFactory() != null) {
            Factory<ExpiryPolicy> expiryPolicyFactory = cacheConfig.getExpiryPolicyFactory();
            injectDependencies(expiryPolicyFactory);
            defaultExpiryPolicy = expiryPolicyFactory.create();
            injectDependencies(defaultExpiryPolicy);
        } else {
            throw new IllegalStateException("Expiry policy factory cannot be null!");
        }

        cacheContext = cacheService.getOrCreateCacheContext(name);
        records = createRecordCacheMap();
        maxSizeChecker = createCacheMaxSizeChecker(evictionConfig.getSize(), evictionConfig.getMaximumSizePolicy());
        evictionPolicyEvaluator = createEvictionPolicyEvaluator(evictionConfig);
        evictionChecker = createEvictionChecker(evictionConfig);
        evictionStrategy = createEvictionStrategy(evictionConfig);

        injectDependencies(evictionPolicyEvaluator.getEvictionPolicyComparator());
        registerResourceIfItIsClosable(cacheWriter);
        registerResourceIfItIsClosable(cacheLoader);
        registerResourceIfItIsClosable(defaultExpiryPolicy);
        init();
    }
    //CHECKSTYLE:ON

    private boolean isPrimary() {
        IPartition partition = nodeEngine.getPartitionService().getPartition(partitionId, false);
        Address owner = partition.getOwnerOrNull();
        Address thisAddress = nodeEngine.getThisAddress();
        return owner != null && owner.equals(thisAddress);
    }

    private void injectDependencies(Object obj) {
        if (obj instanceof HazelcastInstanceAware) {
            ((HazelcastInstanceAware) obj).setHazelcastInstance(nodeEngine.getHazelcastInstance());
        }
    }

    private void registerResourceIfItIsClosable(Object resource) {
        if (resource instanceof Closeable) {
            cacheService.addCacheResource(name, (Closeable) resource);
        }
    }

    @Override
    public void init() {
        primary = isPrimary();
        records.setEntryCounting(primary);
    }

    protected boolean isReadThrough() {
        return cacheConfig.isReadThrough();
    }

    protected boolean isWriteThrough() {
        return cacheConfig.isWriteThrough();
    }

    protected boolean isStatisticsEnabled() {
        return statistics != null;
    }

    protected abstract CRM createRecordCacheMap();
    protected abstract CacheEntryProcessorEntry createCacheEntryProcessorEntry(Data key, R record,
                                                                               long now, int completionId);
    protected abstract R createRecord(Object value, long creationTime, long expiryTime);
    protected abstract Data valueToData(Object value);
    protected abstract Object dataToValue(Data data);
    protected abstract Object recordToValue(R record);
    protected abstract Data recordToData(R record);
    protected abstract Data toHeapData(Object obj);

    protected MaxSizeChecker createCacheMaxSizeChecker(int size, EvictionConfig.MaxSizePolicy maxSizePolicy) {
        if (maxSizePolicy == null) {
            throw new IllegalArgumentException("Max-Size policy cannot be null");
        }
        if (maxSizePolicy == EvictionConfig.MaxSizePolicy.ENTRY_COUNT) {
            return new EntryCountCacheMaxSizeChecker(size, records, partitionCount);
        }
        return null;
    }

    protected EvictionPolicyEvaluator<Data, R> createEvictionPolicyEvaluator(EvictionConfig evictionConfig) {
        EvictionConfigHelper.checkEvictionConfig(evictionConfig);
        return EvictionPolicyEvaluatorProvider
                .getEvictionPolicyEvaluator(evictionConfig, nodeEngine.getConfigClassLoader());
    }

    protected EvictionChecker createEvictionChecker(EvictionConfig cacheEvictionConfig) {
        return new MaxSizeEvictionChecker();
    }

    protected EvictionStrategy<Data, R, CRM> createEvictionStrategy(EvictionConfig cacheEvictionConfig) {
        return EvictionStrategyProvider.getEvictionStrategy(cacheEvictionConfig);
    }

    protected boolean isEvictionEnabled() {
        return evictionStrategy != null && evictionPolicyEvaluator != null;
    }

    protected boolean isEventsEnabled() {
        return eventsEnabled && (cacheContext.getCacheEntryListenerCount() > 0 || wanReplicationEnabled);
    }

    protected boolean isInvalidationEnabled() {
        return primary && cacheContext.getInvalidationListenerCount() > 0;
    }

    @Override
    public int evictIfRequired() {
        int evictedCount = 0;
        if (isEvictionEnabled()) {
            evictedCount = evictionStrategy.evict(records, evictionPolicyEvaluator, evictionChecker, this);
            if (isStatisticsEnabled() && evictedCount > 0) {
                statistics.increaseCacheEvictions(evictedCount);
            }
        }
        return evictedCount;
    }

    protected Data toData(Object obj) {
        if (obj instanceof Data) {
            return (Data) obj;
        } else if (obj instanceof CacheRecord) {
            return recordToData((R) obj);
        } else {
            return valueToData(obj);
        }
    }

    protected Object toValue(Object obj) {
        if (obj instanceof Data) {
            return dataToValue((Data) obj);
        } else if (obj instanceof CacheRecord) {
            return recordToValue((R) obj);
        } else {
            return obj;
        }
    }

    protected Object toStorageValue(Object obj) {
        if (obj instanceof Data) {
            if (cacheConfig.getInMemoryFormat() == InMemoryFormat.OBJECT) {
                return dataToValue((Data) obj);
            } else {
                return obj;
            }
        } else if (obj instanceof CacheRecord) {
            return recordToValue((R) obj);
        } else {
            return obj;
        }
    }

    public Data toEventData(Object obj) {
        return isEventsEnabled() ? toHeapData(obj) : null;
    }

    private long getAdjustedExpireTime(Duration duration, long now) {
        return duration.getDurationAmount() != 0 ? duration.getAdjustedTime(now) : CacheRecord.TIME_NOT_AVAILABLE;
    }

    protected ExpiryPolicy getExpiryPolicy(ExpiryPolicy expiryPolicy) {
        return expiryPolicy != null ? expiryPolicy : defaultExpiryPolicy;
    }

    protected boolean processExpiredEntry(Data key, R record, long now) {
        return processExpiredEntry(key, record, now, SOURCE_NOT_AVAILABLE);
    }
<commit_msg>Revert adjusted expire time calculation back because of fail on EE<commit_after>/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * 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 com.hazelcast.cache.impl;

import com.hazelcast.cache.CacheNotExistsException;
import com.hazelcast.cache.impl.maxsize.MaxSizeChecker;
import com.hazelcast.cache.impl.maxsize.impl.EntryCountCacheMaxSizeChecker;
import com.hazelcast.cache.impl.record.CacheRecord;
import com.hazelcast.cache.impl.record.SampleableCacheRecordMap;
import com.hazelcast.config.CacheConfig;
import com.hazelcast.config.EvictionConfig;
import com.hazelcast.config.InMemoryFormat;
import com.hazelcast.core.HazelcastInstanceAware;
import com.hazelcast.internal.eviction.EvictionChecker;
import com.hazelcast.internal.eviction.EvictionListener;
import com.hazelcast.internal.eviction.EvictionPolicyEvaluator;
import com.hazelcast.internal.eviction.EvictionPolicyEvaluatorProvider;
import com.hazelcast.internal.eviction.EvictionStrategy;
import com.hazelcast.internal.eviction.EvictionStrategyProvider;
import com.hazelcast.internal.eviction.impl.EvictionConfigHelper;
import com.hazelcast.map.impl.MapEntries;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.spi.EventRegistration;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.impl.eventservice.InternalEventService;
import com.hazelcast.spi.partition.IPartition;
import com.hazelcast.util.Clock;
import com.hazelcast.util.EmptyStatement;
import com.hazelcast.util.ExceptionUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import javax.cache.configuration.Factory;
import javax.cache.expiry.Duration;
import javax.cache.expiry.ExpiryPolicy;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheLoaderException;
import javax.cache.integration.CacheWriter;
import javax.cache.integration.CacheWriterException;
import javax.cache.processor.EntryProcessor;
import java.io.Closeable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static com.hazelcast.cache.impl.CacheEventContextUtil.createBaseEventContext;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheCompleteEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheCreatedEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheExpiredEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheRemovedEvent;
import static com.hazelcast.cache.impl.CacheEventContextUtil.createCacheUpdatedEvent;
import static com.hazelcast.cache.impl.operation.MutableOperation.IGNORE_COMPLETION;
import static com.hazelcast.cache.impl.record.CacheRecordFactory.isExpiredAt;

/**
 * @author sozal 14/10/14
 */
public abstract class AbstractCacheRecordStore<R extends CacheRecord, CRM extends SampleableCacheRecordMap<Data, R>>
        implements ICacheRecordStore, EvictionListener<Data, R> {

    protected static final int DEFAULT_INITIAL_CAPACITY = 256;
    protected static final String SOURCE_NOT_AVAILABLE = "<NA>";

    protected final String name;
    protected final int partitionId;
    protected final int partitionCount;
    protected final NodeEngine nodeEngine;
    protected final AbstractCacheService cacheService;
    protected final CacheConfig cacheConfig;
    protected CRM records;
    protected CacheContext cacheContext;
    protected CacheStatisticsImpl statistics;
    protected CacheLoader cacheLoader;
    protected CacheWriter cacheWriter;
    protected boolean eventsEnabled = true;
    protected boolean eventsBatchingEnabled;
    protected ExpiryPolicy defaultExpiryPolicy;
    protected final EvictionConfig evictionConfig;
    protected final Map<CacheEventType, Set<CacheEventData>> batchEvent = new HashMap<CacheEventType, Set<CacheEventData>>();
    protected final MaxSizeChecker maxSizeChecker;
    protected final EvictionPolicyEvaluator<Data, R> evictionPolicyEvaluator;
    protected final EvictionChecker evictionChecker;
    protected final EvictionStrategy<Data, R, CRM> evictionStrategy;
    protected final boolean wanReplicationEnabled;
    protected final boolean disablePerEntryInvalidationEvents;
    protected boolean primary;

    //CHECKSTYLE:OFF
    public AbstractCacheRecordStore(String name, int partitionId, NodeEngine nodeEngine,
                                    AbstractCacheService cacheService) {
        this.name = name;
        this.partitionId = partitionId;
        this.nodeEngine = nodeEngine;
        this.partitionCount = nodeEngine.getPartitionService().getPartitionCount();
        this.cacheService = cacheService;
        this.cacheConfig = cacheService.getCacheConfig(name);
        if (cacheConfig == null) {
            throw new CacheNotExistsException("Cache " + name + " is already destroyed or not created yet, on "
                    + nodeEngine.getLocalMember());
        }
        evictionConfig = cacheConfig.getEvictionConfig();
        if (evictionConfig == null) {
            throw new IllegalStateException("Eviction config cannot be null!");
        }
        wanReplicationEnabled = cacheService.isWanReplicationEnabled(name);
        disablePerEntryInvalidationEvents = cacheConfig.isDisablePerEntryInvalidationEvents();
        if (cacheConfig.isStatisticsEnabled()) {
            statistics = cacheService.createCacheStatIfAbsent(name);
        }
        if (cacheConfig.getCacheLoaderFactory() != null) {
            Factory<CacheLoader> cacheLoaderFactory = cacheConfig.getCacheLoaderFactory();
            injectDependencies(cacheLoaderFactory);
            cacheLoader = cacheLoaderFactory.create();
            injectDependencies(cacheLoader);
        }
        if (cacheConfig.getCacheWriterFactory() != null) {
            Factory<CacheWriter> cacheWriterFactory = cacheConfig.getCacheWriterFactory();
            injectDependencies(cacheWriterFactory);
            cacheWriter = cacheWriterFactory.create();
            injectDependencies(cacheWriter);
        }
        if (cacheConfig.getExpiryPolicyFactory() != null) {
            Factory<ExpiryPolicy> expiryPolicyFactory = cacheConfig.getExpiryPolicyFactory();
            injectDependencies(expiryPolicyFactory);
            defaultExpiryPolicy = expiryPolicyFactory.create();
            injectDependencies(defaultExpiryPolicy);
        } else {
            throw new IllegalStateException("Expiry policy factory cannot be null!");
        }

        cacheContext = cacheService.getOrCreateCacheContext(name);
        records = createRecordCacheMap();
        maxSizeChecker = createCacheMaxSizeChecker(evictionConfig.getSize(), evictionConfig.getMaximumSizePolicy());
        evictionPolicyEvaluator = createEvictionPolicyEvaluator(evictionConfig);
        evictionChecker = createEvictionChecker(evictionConfig);
        evictionStrategy = createEvictionStrategy(evictionConfig);

        injectDependencies(evictionPolicyEvaluator.getEvictionPolicyComparator());
        registerResourceIfItIsClosable(cacheWriter);
        registerResourceIfItIsClosable(cacheLoader);
        registerResourceIfItIsClosable(defaultExpiryPolicy);
        init();
    }
    //CHECKSTYLE:ON

    private boolean isPrimary() {
        IPartition partition = nodeEngine.getPartitionService().getPartition(partitionId, false);
        Address owner = partition.getOwnerOrNull();
        Address thisAddress = nodeEngine.getThisAddress();
        return owner != null && owner.equals(thisAddress);
    }

    private void injectDependencies(Object obj) {
        if (obj instanceof HazelcastInstanceAware) {
            ((HazelcastInstanceAware) obj).setHazelcastInstance(nodeEngine.getHazelcastInstance());
        }
    }

    private void registerResourceIfItIsClosable(Object resource) {
        if (resource instanceof Closeable) {
            cacheService.addCacheResource(name, (Closeable) resource);
        }
    }

    @Override
    public void init() {
        primary = isPrimary();
        records.setEntryCounting(primary);
    }

    protected boolean isReadThrough() {
        return cacheConfig.isReadThrough();
    }

    protected boolean isWriteThrough() {
        return cacheConfig.isWriteThrough();
    }

    protected boolean isStatisticsEnabled() {
        return statistics != null;
    }

    protected abstract CRM createRecordCacheMap();
    protected abstract CacheEntryProcessorEntry createCacheEntryProcessorEntry(Data key, R record,
                                                                               long now, int completionId);
    protected abstract R createRecord(Object value, long creationTime, long expiryTime);
    protected abstract Data valueToData(Object value);
    protected abstract Object dataToValue(Data data);
    protected abstract Object recordToValue(R record);
    protected abstract Data recordToData(R record);
    protected abstract Data toHeapData(Object obj);

    protected MaxSizeChecker createCacheMaxSizeChecker(int size, EvictionConfig.MaxSizePolicy maxSizePolicy) {
        if (maxSizePolicy == null) {
            throw new IllegalArgumentException("Max-Size policy cannot be null");
        }
        if (maxSizePolicy == EvictionConfig.MaxSizePolicy.ENTRY_COUNT) {
            return new EntryCountCacheMaxSizeChecker(size, records, partitionCount);
        }
        return null;
    }

    protected EvictionPolicyEvaluator<Data, R> createEvictionPolicyEvaluator(EvictionConfig evictionConfig) {
        EvictionConfigHelper.checkEvictionConfig(evictionConfig);
        return EvictionPolicyEvaluatorProvider
                .getEvictionPolicyEvaluator(evictionConfig, nodeEngine.getConfigClassLoader());
    }

    protected EvictionChecker createEvictionChecker(EvictionConfig cacheEvictionConfig) {
        return new MaxSizeEvictionChecker();
    }

    protected EvictionStrategy<Data, R, CRM> createEvictionStrategy(EvictionConfig cacheEvictionConfig) {
        return EvictionStrategyProvider.getEvictionStrategy(cacheEvictionConfig);
    }

    protected boolean isEvictionEnabled() {
        return evictionStrategy != null && evictionPolicyEvaluator != null;
    }

    protected boolean isEventsEnabled() {
        return eventsEnabled && (cacheContext.getCacheEntryListenerCount() > 0 || wanReplicationEnabled);
    }

    protected boolean isInvalidationEnabled() {
        return primary && cacheContext.getInvalidationListenerCount() > 0;
    }

    @Override
    public int evictIfRequired() {
        int evictedCount = 0;
        if (isEvictionEnabled()) {
            evictedCount = evictionStrategy.evict(records, evictionPolicyEvaluator, evictionChecker, this);
            if (isStatisticsEnabled() && evictedCount > 0) {
                statistics.increaseCacheEvictions(evictedCount);
            }
        }
        return evictedCount;
    }

    protected Data toData(Object obj) {
        if (obj instanceof Data) {
            return (Data) obj;
        } else if (obj instanceof CacheRecord) {
            return recordToData((R) obj);
        } else {
            return valueToData(obj);
        }
    }

    protected Object toValue(Object obj) {
        if (obj instanceof Data) {
            return dataToValue((Data) obj);
        } else if (obj instanceof CacheRecord) {
            return recordToValue((R) obj);
        } else {
            return obj;
        }
    }

    protected Object toStorageValue(Object obj) {
        if (obj instanceof Data) {
            if (cacheConfig.getInMemoryFormat() == InMemoryFormat.OBJECT) {
                return dataToValue((Data) obj);
            } else {
                return obj;
            }
        } else if (obj instanceof CacheRecord) {
            return recordToValue((R) obj);
        } else {
            return obj;
        }
    }

    public Data toEventData(Object obj) {
        return isEventsEnabled() ? toHeapData(obj) : null;
    }

    private long getAdjustedExpireTime(Duration duration, long now) {
        // TODO Reverted back because of fail on EE but it should be like
        // `duration.getDurationAmount() != 0 ? duration.getAdjustedTime(now) : CacheRecord.TIME_NOT_AVAILABLE;`
        return duration.getAdjustedTime(now);
    }

    protected ExpiryPolicy getExpiryPolicy(ExpiryPolicy expiryPolicy) {
        return expiryPolicy != null ? expiryPolicy : defaultExpiryPolicy;
    }

    protected boolean processExpiredEntry(Data key, R record, long now) {
        return processExpiredEntry(key, record, now, SOURCE_NOT_AVAILABLE);
    }

{'fraction_non_alphanumeric': 0.07743709190881667, 'fraction_numerical': 0.0019855664592004275, 'mean_word_length': 3.6060499472388323, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4943100', 'n_tokens_mistral': 6877, 'n_tokens_neox': 6748, 'n_words': 1532}