/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.identitymaps;

import java.util.*;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.sessions.AbstractSession;

/**
 * Unit of work specific identity map which avoid additional overhead not required in unit of work,
 * such as locking and synchronization.
 * @author James Sutherland
 * @since TopLink 10.1.3.1
 */
public class UnitOfWorkIdentityMap extends FullIdentityMap {

    protected UnitOfWorkIdentityMap() {
    }

    public UnitOfWorkIdentityMap(int size, ClassDescriptor descriptor, AbstractSession session, boolean isolated) {
        super();
        this.maxSize = size;
        // PERF: Use a HashMap as more efficient than a ConcurrentMap and single threaded.
        this.cacheKeys = new HashMap(size);
        this.descriptor = descriptor;
        this.session = session;
        this.isIsolated = isolated;
    }

    @Override
    public CacheKey createCacheKey(Object primaryKey, Object object, Object writeLockValue, long readTime) {
        return new CacheKey(primaryKey, object, writeLockValue, readTime, true);
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public CacheKey acquireDeferredLock(Object primaryKey, boolean isCacheCheckComplete) {
        CacheKey cacheKey = getCacheKey(primaryKey, false);
        if (cacheKey == null) {
            CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0);
            cacheKey = putCacheKeyIfAbsent(newCacheKey);
            if (cacheKey == null) {
                return newCacheKey;
            }
        }
        return cacheKey;
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public CacheKey acquireLock(Object primaryKey, boolean forMerge, boolean isCacheCheckComplete) {
        CacheKey cacheKey = getCacheKey(primaryKey, forMerge);
        if (cacheKey == null) {
            CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0);
            cacheKey = putCacheKeyIfAbsent(newCacheKey);
            if (cacheKey == null) {
                return newCacheKey;
            }
        }
        return cacheKey;
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public CacheKey acquireLockNoWait(Object primaryKey, boolean forMerge) {
        CacheKey cacheKey = getCacheKey(primaryKey, forMerge);
        if (cacheKey == null) {
            CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0);
            cacheKey = putCacheKeyIfAbsent(newCacheKey);
            if (cacheKey == null) {
                return newCacheKey;
            }
        }
        return cacheKey;
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public CacheKey acquireLockWithWait(Object primaryKey, boolean forMerge, int wait) {
        CacheKey cacheKey = getCacheKey(primaryKey, forMerge);
        if (cacheKey == null) {
            CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0);
            cacheKey = putCacheKeyIfAbsent(newCacheKey);
            if (cacheKey == null) {
                return newCacheKey;
            }
        }
        return cacheKey;
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public CacheKey acquireReadLockOnCacheKey(Object primaryKey) {
        return acquireReadLockOnCacheKeyNoWait(primaryKey);
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public CacheKey acquireReadLockOnCacheKeyNoWait(Object primaryKey) {
        CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0);
        CacheKey cacheKey = getCacheKey(newCacheKey, false);
        if (cacheKey == null) {
            return newCacheKey;
        }
        return cacheKey;
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    protected CacheKey getCacheKeyWithReadLock(Object primaryKey) {
        return getCacheKey(primaryKey, false);
    }

    /**
     * Use hashmap put, as no concurrency in unit of work.
     */
    protected CacheKey putCacheKeyIfAbsent(CacheKey searchKey) {
        searchKey.setOwningMap(this);
        this.cacheKeys.put(searchKey.getKey(), searchKey);
        return null;
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public Object remove(CacheKey cacheKey) {
        if (cacheKey != null) {
            this.cacheKeys.remove(cacheKey.getKey());
            cacheKey.setOwningMap(null);
        } else {
            return null;
        }
        return cacheKey.getObject();
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public void resetCacheKey(CacheKey key, Object object, Object writeLockValue, long readTime) {
        key.setObject(object);
        key.setWriteLockValue(writeLockValue);
        key.setReadTime(readTime);
    }

    /**
     * Avoid acquiring any lock as uow is single threaded.
     */
    @Override
    public void setWriteLockValue(Object primaryKey, Object writeLockValue) {
        CacheKey cacheKey = getCacheKeyForLock(primaryKey);
        if (cacheKey != null) {
            cacheKey.setWriteLockValue(writeLockValue);
        }
    }
}
