class GridCacheVersionConflictContext<K, V> {
    /** Old entry. */
    @GridToStringInclude
    private final GridCacheVersionedEntry<K, V> oldEntry;

    /** New entry. */
    @GridToStringInclude
    private final GridCacheVersionedEntry<K, V> newEntry;

    /** Object context. */
    private final CacheObjectValueContext ctx;

    /** Current state. */
    private State state;

    /** Current merge value. */
    @GridToStringExclude
    private V mergeVal;

    /** TTL. */
    private long ttl;

    /** Expire time. */
    private long expireTime;

    /** Manual resolve flag. */
    private boolean manualResolve;

    /**
     * Constructor.
     *
     * @param oldEntry Old entry.
     * @param newEntry New entry.
     */
    public GridCacheVersionConflictContext(CacheObjectValueContext ctx, GridCacheVersionedEntry<K, V> oldEntry,
        GridCacheVersionedEntry<K, V> newEntry) {
        assert oldEntry != null && newEntry != null;
        assert oldEntry.ttl() >= 0 && newEntry.ttl() >= 0;

        this.oldEntry = oldEntry;
        this.newEntry = newEntry;
        this.ctx = ctx;

        // Set initial state.
        useNew();
    }

    /**
     * Gets old (existing) cache entry.
     *
     * @return Old (existing) cache entry.
     */
    public GridCacheVersionedEntry<K, V> oldEntry() {
        return oldEntry;
    }

    /**
     * Gets new cache entry.
     *
     * @return New cache entry.
     */
    public GridCacheVersionedEntry<K, V> newEntry() {
        return newEntry;
    }

    /**
     * Gets cache object context.
     *
     * @return Cache object context.
     */
    public CacheObjectValueContext valueContext() {
        return ctx;
    }

    /**
     * Force cache to ignore new entry and leave old (existing) entry unchanged.
     */
    public void useOld() {
        state = State.USE_OLD;
    }

    /**
     * Force cache to apply new entry overwriting old (existing) entry.
     */
    public void useNew() {
        state = State.USE_NEW;

        ttl = newEntry.ttl();
    }

    /**
     * Force cache to use neither old, nor new, but some other value passed as argument. In this case old
     * value will be replaced with merge value and update will be considered as local.
     * <p>
     * Also in case of merge you have to specify new TTL and expire time explicitly. For unlimited TTL use {@code 0}.
     *
     * @param mergeVal Merge value or {@code null} to force remove.
     * @param ttl Time to live in milliseconds (must be non-negative).
     * @param expireTime Expire time.
     */
    public void merge(@Nullable V mergeVal, long ttl, long expireTime) {
        if (ttl < 0)
            throw new IllegalArgumentException("TTL must be non-negative: " + ttl);

        state = State.MERGE;

        this.mergeVal = mergeVal;
        this.ttl = ttl;
        this.expireTime = expireTime;
    }

    /**
     * @return {@code True} in case old value should be used.
     */
    public boolean isUseOld() {
        return state == State.USE_OLD;
    }

    /**
     * @return {@code True} in case new value should be used.
     */
    public boolean isUseNew() {
        return state == State.USE_NEW;
    }

    /**
     * @return {@code True} in case merge is to be performed.
     */
    public boolean isMerge() {
        return state == State.MERGE;
    }

    /**
     * Set manual resolve class.
     */
    public void manualResolve() {
        this.manualResolve = true;
    }

    /**
     * @return Manual resolve flag.
     */
    public boolean isManualResolve() {
        return manualResolve;
    }

    /**
     * @return Value to merge (if any).
     */
    @Nullable public V mergeValue() {
        return mergeVal;
    }

    /**
     * @return TTL.
     */
    public long ttl() {
        return ttl;
    }

    /**
     * @return Expire time.
     */
    public long expireTime() {
        return isUseNew() ? newEntry.expireTime() : isUseOld() ? oldEntry.expireTime() : expireTime;
    }

    /** {@inheritDoc} */
    @Override public String toString() {<FILL_FUNCTION_BODY>}

    /**
     * State.
     */
    private enum State {
        /** Use old. */
        USE_OLD,

        /** Use new. */
        USE_NEW,

        /** Merge. */
        MERGE
    }
}

        return state == State.MERGE ?
            S.toString(GridCacheVersionConflictContext.class, this, "mergeValue", mergeVal, true) :
            S.toString(GridCacheVersionConflictContext.class, this);
    
<no_super_class>