com.ocadotechnology.indexedcache.DefaultOptionalOneToOneIndex Maven / Gradle / Ivy
/*
* 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.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.annotation.CheckForNull;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.ocadotechnology.id.Identified;
/** BiMap-based partial injection index.
*
* Enforces both one-to-one mappings:
* if a key for a value exists, it is present at most once, and
* if a value exists, it is present at most once.
*
*
* Implementation Note:
*
* This class implements update as a remove followed by an add.
* That avoids using class "C" equals method to compare old against new
* (to decide if a value has changed),
* (but still uses it to enforce that a value is associated with one key).
*/
public final class DefaultOptionalOneToOneIndex> extends AbstractOptionalOneToOneIndex {
private final BiMap indexValues = HashBiMap.create();
private final Function super C, Optional> indexingFunction;
private transient ImmutableMap snapshot; //Null if the previous snapshot has been invalidated by an update
public DefaultOptionalOneToOneIndex(Function super C, Optional> indexingFunction) {
this(null, indexingFunction);
}
public DefaultOptionalOneToOneIndex(@CheckForNull String name, Function super C, Optional> indexingFunction) {
super(name);
this.indexingFunction = indexingFunction;
}
@Override
public C getOrNull(R r) {
return indexValues.get(r);
}
@Override
public Optional get(R r) {
return Optional.ofNullable(indexValues.get(r));
}
@Override
public Optional getKeyFor(C c) {
return indexingFunction.apply(c);
}
@Override
public boolean containsKey(R r) {
return indexValues.containsKey(r);
}
@Override
public Stream streamKeys() {
return indexValues.keySet().stream();
}
@Override
public Stream streamValues() {
return indexValues.values().stream();
}
@Override
public boolean isEmpty() {
return indexValues.isEmpty();
}
@Override
protected void remove(C object) {
indexingFunction.apply(object).ifPresent(val -> {
indexValues.remove(val);
snapshot = null;
});
}
@Override
protected void add(C object) throws IndexUpdateException {
Optional optionalR = indexingFunction.apply(object);
if (optionalR.isPresent()) {
R r = optionalR.get();
C oldValue = indexValues.put(r, object);
if (oldValue != null) {
indexValues.put(r, oldValue);
throw new IndexUpdateException(
name != null ? name : indexingFunction.getClass().getSimpleName(),
"Error updating %s: Trying to add [%s] to OptionalOneToOneIndex, but oldValue [%s] already exists at index [%s]",
formattedName,
object,
oldValue,
r
);
}
snapshot = null;
}
}
@Override
public ImmutableMap snapshot() {
if (snapshot == null) {
snapshot = ImmutableMap.copyOf(indexValues);
}
return snapshot;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy