package org.javers.repository.inmemory;
import org.javers.common.collections.Lists;
import java.util.Optional;
import org.javers.common.validation.Validate;
import org.javers.core.commit.Commit;
import org.javers.core.commit.CommitId;
import org.javers.core.json.JsonConverter;
import org.javers.core.metamodel.object.CdoSnapshot;
import org.javers.core.metamodel.object.GlobalId;
import org.javers.core.metamodel.object.InstanceId;
import org.javers.core.metamodel.object.ValueObjectId;
import org.javers.core.metamodel.type.EntityType;
import org.javers.core.metamodel.type.ManagedType;
import org.javers.repository.api.JaversRepository;
import org.javers.repository.api.QueryParams;
import org.javers.repository.api.QueryParamsBuilder;
import org.javers.repository.api.SnapshotIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.Collections.unmodifiableList;
/**
* Fake impl of JaversRepository
*
* @author bartosz walacik
*/
public class InMemoryRepository implements JaversRepository {
private static final Logger logger = LoggerFactory.getLogger(InMemoryRepository.class);
private Map<GlobalId, LinkedList<CdoSnapshot>> snapshots = new ConcurrentHashMap<>();
private CommitId head;
public InMemoryRepository() {
}
@Override
public List<CdoSnapshot> getValueObjectStateHistory(final EntityType ownerEntity, final String path, QueryParams queryParams) {
Validate.argumentsAreNotNull(ownerEntity, path, queryParams);
List<CdoSnapshot> result = Lists.positiveFilter(getAll(), input -> {
if (!(input.getGlobalId() instanceof ValueObjectId)) {
return false;
}
ValueObjectId id = (ValueObjectId) input.getGlobalId();
return id.hasOwnerOfType(ownerEntity)
&& id.getFragment().equals(path);
});
return applyQueryParams(result, queryParams);
}
@Override
public List<CdoSnapshot> getStateHistory(GlobalId globalId, QueryParams queryParams) {
Validate.argumentsAreNotNull(globalId, queryParams);
List<CdoSnapshot> filtered = new ArrayList<>();
for (CdoSnapshot snapshot : getAll()) {
if (snapshot.getGlobalId().equals(globalId)) {
filtered.add(snapshot);
}
if (queryParams.isAggregate() && isParent(globalId, snapshot.getGlobalId())){
filtered.add(snapshot);
}
}
return applyQueryParams(filtered, queryParams);
}
private boolean isParent(GlobalId parentCandidate, GlobalId childCandidate) {
if (! (parentCandidate instanceof InstanceId && childCandidate instanceof ValueObjectId)){
return false;
}
InstanceId parent = (InstanceId)parentCandidate;
ValueObjectId child = (ValueObjectId)childCandidate;
return child.getOwnerId().equals(parent);
}
@Override
public List<CdoSnapshot> getStateHistory(Set<ManagedType> givenClasses, QueryParams queryParams) {
Validate.argumentsAreNotNull(givenClasses, queryParams);
List<CdoSnapshot> filtered = new ArrayList<>();
for (CdoSnapshot snapshot : getAll()) {
for (ManagedType givenClass : givenClasses) {
if (snapshot.getGlobalId().isTypeOf(givenClass)) {
filtered.add(snapshot);
}
if (queryParams.isAggregate() && isParent(givenClass, snapshot.getGlobalId())){
filtered.add(snapshot);
}
}
}
return applyQueryParams(filtered, queryParams);
}
private boolean isParent(ManagedType parentCandidate, GlobalId childCandidate) {
if (! (parentCandidate instanceof EntityType && childCandidate instanceof ValueObjectId)){
return false;
}
EntityType parent = (EntityType)parentCandidate;
ValueObjectId child = (ValueObjectId)childCandidate;
return child.getOwnerId().getTypeName().equals(parent.getName());
}
private QueryParams getQueryParamsWithIncreasedLimit(QueryParams queryParams) {
return QueryParamsBuilder.initializeWith(queryParams)
.limit(queryParams.limit() * 10)
.build();
}
private List<CdoSnapshot> applyQueryParams(List<CdoSnapshot> snapshots, final QueryParams queryParams){
if (queryParams.commitIds().size() > 0) {
snapshots = filterSnapshotsByCommitIds(snapshots, queryParams.commitIds());
}
if (queryParams.version().isPresent()) {
snapshots = filterSnapshotsByVersion(snapshots, queryParams.version().get());
}
if (queryParams.author().isPresent()) {
snapshots = filterSnapshotsByAuthor(snapshots, queryParams.author().get());
}
if (queryParams.hasDates()) {
snapshots = filterSnapshotsByCommitDate(snapshots, queryParams);
}
if (queryParams.changedProperty().isPresent()){
snapshots = filterByPropertyName(snapshots, queryParams.changedProperty().get());
}
snapshots = filterSnapshotsByCommitProperties(snapshots, queryParams.commitProperties());
return trimResultsToRequestedSlice(snapshots, queryParams.skip(), queryParams.limit());
}
private List<CdoSnapshot> filterSnapshotsByCommitIds(List<CdoSnapshot> snapshots, final Set<CommitId> commitIds) {
return Lists.positiveFilter(snapshots, snapshot -> commitIds.contains(snapshot.getCommitId()));
}
private List<CdoSnapshot> filterSnapshotsByVersion(List<CdoSnapshot> snapshots, final Long version) {
return Lists.positiveFilter(snapshots, snapshot -> version == snapshot.getVersion());
}
private List<CdoSnapshot> filterSnapshotsByAuthor(List<CdoSnapshot> snapshots, final String author) {
return Lists.positiveFilter(snapshots, snapshot -> author.equals(snapshot.getCommitMetadata().getAuthor()));
}
private List<CdoSnapshot> filterSnapshotsByCommitDate(List<CdoSnapshot> snapshots, final QueryParams queryParams) {
return Lists.positiveFilter(snapshots, snapshot -> queryParams.isDateInRange(snapshot.getCommitMetadata().getCommitDate()));
}
private List<CdoSnapshot> filterSnapshotsByCommitProperties(List<CdoSnapshot> snapshots, final Map<String, String> commitProperties) {
return Lists.positiveFilter(snapshots, snapshot ->
commitProperties.entrySet().stream().allMatch(commitProperty -> {
Map<String, String> actualCommitProperties = snapshot.getCommitMetadata().getProperties();
return actualCommitProperties.containsKey(commitProperty.getKey()) &&
actualCommitProperties.get(commitProperty.getKey()).equals(commitProperty.getValue());
})
);
}
private List<CdoSnapshot> trimResultsToRequestedSlice(List<CdoSnapshot> snapshots, int from, int size) {
int fromIndex = Math.min(from, snapshots.size());
int toIndex = Math.min(from + size, snapshots.size());
return new ArrayList<>(snapshots.subList(fromIndex, toIndex));
}
@Override
public Optional<CdoSnapshot> getLatest(GlobalId globalId) {
Validate.argumentsAreNotNull(globalId);
if (snapshots.containsKey(globalId)) {
LinkedList<CdoSnapshot> states = snapshots.get(globalId);
return Optional.of(states.peek());
}
return Optional.empty();
}
@Override
public List<CdoSnapshot> getSnapshots(QueryParams queryParams) {
Validate.argumentIsNotNull(queryParams);
return applyQueryParams(getAll(), queryParams);
}
@Override
public List<CdoSnapshot> getSnapshots(Collection<SnapshotIdentifier> snapshotIdentifiers) {
return Lists.transform(getPersistedIdentifiers(snapshotIdentifiers), snapshotIdentifier -> {
List<CdoSnapshot> objectSnapshots = snapshots.get(snapshotIdentifier.getGlobalId());
return objectSnapshots.get(objectSnapshots.size() - ((int)snapshotIdentifier.getVersion()));
});
}
private List<SnapshotIdentifier> getPersistedIdentifiers(Collection<SnapshotIdentifier> snapshotIdentifiers) {
return Lists.positiveFilter(new ArrayList<>(snapshotIdentifiers), snapshotIdentifier -> snapshots.containsKey(snapshotIdentifier.getGlobalId()) &&
snapshotIdentifier.getVersion() <= snapshots.get(snapshotIdentifier.getGlobalId()).size());
}
@Override
public void persist(Commit commit) {
Validate.argumentsAreNotNull(commit);
List<CdoSnapshot> snapshots = commit.getSnapshots();
for (CdoSnapshot s : snapshots){
persist(s);
}
logger.debug("{} snapshot(s) persisted", snapshots.size());
head = commit.getId();
}
@Override
public CommitId getHeadId() {
return head;
}
@Override
public void setJsonConverter(JsonConverter jsonConverter) {
}
private List<CdoSnapshot> filterByPropertyName(List<CdoSnapshot> snapshots, final String propertyName){
return Lists.positiveFilter(snapshots, input -> input.hasChangeAt(propertyName));
}
private List<CdoSnapshot> getAll(){
List<CdoSnapshot> all = new ArrayList<>();
for (LinkedList<CdoSnapshot> snapshotsList : snapshots.values()) {
all.addAll(snapshotsList);
}
Collections.sort(all, (o1, o2) -> o2.getCommitId().compareTo(o1.getCommitId()));
return all;
}
private synchronized void persist(CdoSnapshot snapshot) {
LinkedList<CdoSnapshot> states = snapshots.get(snapshot.getGlobalId());
if (states == null){
states = new LinkedList<>();
snapshots.put(snapshot.getGlobalId(), states);
}
states.push(snapshot);
}
@Override
public void ensureSchema() {
}
}