package org.javers.core.diff;
import org.javers.common.collections.Lists;
import org.javers.common.exception.JaversException;
import org.javers.core.diff.changetype.PropertyChange;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import static org.javers.common.exception.JaversExceptionCode.AFFECTED_CDO_IS_NOT_AVAILABLE;
import static org.javers.common.validation.Validate.argumentIsNotNull;
/**
* Diff is a set of (atomic) changes between two graphs of objects.
* <br><br>
*
* Typically it is used to capture and trace changes made by user on his domain data.
* In this case diff is done between previous and current state of a bunch of domain objects.
* <br><br>
*
* @author bartosz walacik
*/
public class Diff implements Serializable {
private final List<Change> changes;
Diff(List<Change> changes) {
this.changes = changes;
}
/**
* Selects new, removed or changed objects
*
* @throws JaversException AFFECTED_CDO_IS_NOT_AVAILABLE if diff is restored from a repository
*/
public <C extends Change> List getObjectsByChangeType(final Class<C> type) {
argumentIsNotNull(type);
return Lists.transform(getChangesByType(type),
input -> input.getAffectedObject().<JaversException>orElseThrow(() -> new JaversException(AFFECTED_CDO_IS_NOT_AVAILABLE)));
}
/**
* Selects objects
* with changed property for given property name
*
* @throws JaversException AFFECTED_CDO_IS_NOT_AVAILABLE if diff is restored from repository,
*/
public List getObjectsWithChangedProperty(String propertyName){
argumentIsNotNull(propertyName);
return Lists.transform(getPropertyChanges(propertyName),
input -> input.getAffectedObject().<JaversException>orElseThrow(() -> new JaversException(AFFECTED_CDO_IS_NOT_AVAILABLE)));
}
/**
* Full list of changes
*
* @return unmodifiable list
*/
public List<Change> getChanges() {
return Collections.unmodifiableList(changes);
}
/**
* Changes that satisfies given filter condition
*/
public List<Change> getChanges(Predicate<Change> predicate) {
return Lists.positiveFilter(changes, predicate);
}
public <C extends Change> List<C> getChangesByType(final Class<C> type) {
argumentIsNotNull(type);
return (List)getChanges(input -> type.isAssignableFrom(input.getClass()));
}
/**
* Selects property changes for given property name
*/
public List<PropertyChange> getPropertyChanges(final String propertyName) {
argumentIsNotNull(propertyName);
return (List)getChanges(input -> input instanceof PropertyChange && ((PropertyChange)input).getPropertyName().equals(propertyName));
}
public boolean hasChanges() {
return !changes.isEmpty();
}
/**
* Prints to String list of changes within this Diff
*/
public final String prettyPrint(){
return toString();
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("Diff:\n");
int i=1;
for (Change change : changes){
b.append((i++)+". "+change+"\n");
}
return b.toString();
}
public String changesSummary(){
StringBuilder b = new StringBuilder();
b.append("changes - ");
for (Map.Entry<Class<? extends Change>, Integer> e : countByType().entrySet()){
b.append(e.getKey().getSimpleName()+ ":"+e.getValue()+" ");
}
return b.toString().trim();
}
public Map<Class<? extends Change>, Integer> countByType(){
Map<Class<? extends Change>, Integer> result = new HashMap<>();
for(Change change : changes) {
Class<? extends Change> key = change.getClass();
if (result.containsKey(change.getClass())){
result.put(key, (result.get(key))+1);
}else{
result.put(key, 1);
}
}
return result;
}
}