RepositoryImpl.java
package io.github.jonloucks.contracts.impl;
import io.github.jonloucks.contracts.api.*;
import java.util.*;
import static io.github.jonloucks.contracts.api.Checks.contractCheck;
import static io.github.jonloucks.contracts.api.Checks.promisorCheck;
import static java.util.Optional.ofNullable;
/**
* Implementation for {@link io.github.jonloucks.contracts.api.Repository}
* @see io.github.jonloucks.contracts.api.Repository
*/
final class RepositoryImpl implements Repository {
@Override
public AutoClose open() {
if (openState.transitionToOpen()) {
storedContracts.values().forEach(StorageImpl::bind);
check();
return this::close;
}
return ()->{};
}
@Override
public <T> AutoClose store(Contract<T> contract, Promisor<T> promisor) {
final Contract<T> validContract = contractCheck(contract);
final Promisor<T> validPromisor = promisorCheck(promisor);
if (storedContracts.containsKey(validContract)) {
throw new ContractException( "The contract " + validContract + " is already promised.");
}
final StorageImpl<T> storage = new StorageImpl<>(contracts,validContract, validPromisor);
storedContracts.put(validContract, storage);
if (openState.isOpen()) {
storage.bind();
}
return () -> {
if (storedContracts.remove(validContract, storage)) {
storage.close();
}
};
}
@Override
public void check() {
requiredContracts.forEach(contract -> {
if (!contracts.isBound(contract)) {
throw new ContractException( "The contract " + contract + " is required.");
}
});
}
@Override
public <T> void require(Contract<T> contract) {
final Contract<T> validContract = contractCheck(contract);
requiredContracts.add(validContract);
}
RepositoryImpl(Contracts contracts) {
this.contracts = contracts;
}
private void close() {
if (openState.transitionToClosed()) {
storedContracts.values().forEach(StorageImpl::close);
storedContracts.clear();
}
}
/**
* Using LinkedHashMap to retain insertion order
*/
private final Map<Contract<?>, StorageImpl<?>> storedContracts = new LinkedHashMap<>();
private static final class StorageImpl<T> implements AutoClose {
StorageImpl(Contracts contracts, Contract<T> contract, Promisor<T> promisor) {
this.contracts = contracts;
this.contract = contract;
this.promisor = promisor;
}
private void bind() {
if (contract.isReplaceable() || !contracts.isBound(contract)) {
close();
closeBinding = contracts.bind(contract, promisor);
}
}
@Override
public void close() {
ofNullable(closeBinding).ifPresent(close -> {
this.closeBinding = null;
close.close();
});
}
private final Contract<T> contract;
private final Promisor<T> promisor;
private final Contracts contracts;
private AutoClose closeBinding;
}
private final Contracts contracts;
private final IdempotentImpl openState = new IdempotentImpl();
private final Set<Contract<?>> requiredContracts = new HashSet<>();
}