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

com.ocadotechnology.indexedcache.OneToOneJoinView Maven / Gradle / Ivy

There is a newer version: 16.6.21
Show newest version
/*
 * Copyright © 2017-2023 Ocado (Ocava)
 *
 * 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.ocadotechnology.indexedcache;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.google.common.base.Preconditions;
import com.ocadotechnology.id.Identified;
import com.ocadotechnology.id.Identity;
import com.ocadotechnology.wrappers.Pair;

public class OneToOneJoinView, A_ID, B extends Identified, B_ID, Z> {

    private final OptionalOneToOneIndex zAOneToOneIndex;
    private final OptionalOneToOneIndex zBOneToOneIndex;

    private final Map, Pair> aIdToPair = new HashMap<>();
    private final Map, Pair> bIdToPair = new HashMap<>();

    private final List>> stateChangeListeners = new ArrayList<>();
    private final List>> stateRemovedListeners = new ArrayList<>();
    private final List>> stateAddedListeners = new ArrayList<>();

    public OneToOneJoinView(StateChangeListenable aCache, StateChangeListenable bCache, OptionalOneToOneIndex zAOneToOneIndex, OptionalOneToOneIndex zBOneToOneIndex) {
        this.zAOneToOneIndex = zAOneToOneIndex;
        this.zBOneToOneIndex = zBOneToOneIndex;

        aCache.registerStateChangeListener(this::aHasChanged);
        bCache.registerStateChangeListener(this::bHasChanged);

        aCache.stream().forEach(this::aWasAdded);
    }

    private void aHasChanged(A previous, A updated) {
        Pair oldMapping = previous != null ? aWasRemoved(previous) : null;
        Pair updatedMapping = updated != null ? aWasAdded(updated) : null;
        updateStateChangeListeners(oldMapping, updatedMapping);
    }

    private void bHasChanged(B previous, B updated) {
        Pair oldMapping = previous != null ? bWasRemoved(previous) : null;
        Pair updatedMapping = updated != null ? bWasAdded(updated) : null;
        updateStateChangeListeners(oldMapping, updatedMapping);
    }

    private Pair aWasRemoved(A a) {
        Preconditions.checkNotNull(a);
        Pair removed = aIdToPair.remove(a.getId());
        if (removed != null) {
            bIdToPair.remove(removed.b.getId());
        }
        return removed;
    }

    private Pair bWasRemoved(B b) {
        Preconditions.checkNotNull(b);
        Pair removed = bIdToPair.remove(b.getId());
        if (removed != null) {
            aIdToPair.remove(removed.a.getId());
        }
        return removed;
    }

    private Pair aWasAdded(A a) {
        return zAOneToOneIndex.getKeyFor(a)
                .flatMap(zBOneToOneIndex::get)
                .map(b -> add(a, b))
                .orElse(null);
    }

    private Pair bWasAdded(B b) {
        return zBOneToOneIndex.getKeyFor(b)
                .flatMap(zAOneToOneIndex::get)
                .map(a -> add(a, b))
                .orElse(null);
    }

    private Pair add(A a, B b) {
        Pair newPair = Pair.of(a, b);
        Pair putA = aIdToPair.put(a.getId(), newPair);
        Pair putB = bIdToPair.put(b.getId(), newPair);
        Preconditions.checkState(putA == null, "Trying to add new pair [%s] to the OneToOneJoinView, but [%s] already exists in pair [%s].", newPair, a, putA);
        Preconditions.checkState(putB == null, "Trying to add new pair [%s] to the OneToOneJoinView, but [%s] already exists in pair [%s].", newPair, b, putB);
        return newPair;
    }

    private void updateStateChangeListeners(Pair old, Pair updated) {
        if (updated == null) {
            if (old != null) {
                stateRemovedListeners.forEach(l -> l.stateRemoved(old));
            }
            return;
        }
        if (old == null) {
            stateAddedListeners.forEach(l -> l.stateAdded(updated));
            return;
        }
        stateChangeListeners.forEach(l -> l.stateChanged(old, updated));
    }

    public Optional getA(Identity bId) {
        return Optional.ofNullable(bIdToPair.get(bId)).map(p -> p.a);
    }

    public Optional getB(Identity aId) {
        return Optional.ofNullable(aIdToPair.get(aId)).map(p -> p.b);
    }

    public void registerStateChangeListener(CacheStateChangeListener> listener) {
        stateChangeListeners.add(listener);
    }

    public void registerStateAddedListener(CacheStateAddedListener> stateAddedListener) {
        stateAddedListeners.add(stateAddedListener);
    }

    public void registerStateRemovedListener(CacheStateRemovedListener> stateRemovedListener) {
        stateRemovedListeners.add(stateRemovedListener);
    }
}