Skip to content

Instantly share code, notes, and snippets.

@dwelch2344
Last active September 11, 2015 17:32
Show Gist options
  • Save dwelch2344/bcba54f73c3f18fcc818 to your computer and use it in GitHub Desktop.
Save dwelch2344/bcba54f73c3f18fcc818 to your computer and use it in GitHub Desktop.
public class ExampleController {
@Inject
private StepRepo stepRepo;
@Inject
private StepChildRepo childRepo;
@RequestMapping(value = "/{stepId}/options/", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody Object putOptions(@RequestBody OptionList beans, @PathVariable Long stepId){
Assert.notNull(beans);
Step parent = stepRepo.findOne(stepId);
Assert.notNull(parent, "Could not find parent step by id " + stepId);
MergeUtils.merge(parent.getOptions(), beans)
.create(b -> {
Step step = stepRepo.findOne(b.getStep());
Assert.notNull(step, "Could not find option-step by id " + b.getStep());
StepOption option = new StepOption(b.getOption(), step);
option = parent.addOption(option);
option = optionRepo.saveAndFlush(option);
return Optional.of(option);
})
.update((option, b) -> {
option.setOption(b.getOption());
optionRepo.saveAndFlush(option);
return Optional.of(option);
})
.delete(option -> {
parent.removeOption(option);
optionRepo.delete(option);
// return empty so our results don't contain the deleted ones
return Optional.empty();
})
.execute();
Step saved = stepRepo.findOne(stepId);
StepFullProjection projection = projectionFactory.createProjection(saved, StepFullProjection.class);
return ResponseEntity.ok(projection);
}
}
import com.google.common.collect.Lists;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.hateoas.Identifiable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.Serializable;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Created by dave on 9/10/15.
*/
public class MergeUtils {
public static <
E extends Identifiable<K>,
B extends Identifiable<K>,
K extends Serializable
>
MergeProcessor<E, B, K> merge(Collection<E> entities, Collection<B> beans){
return diff(entities, beans).processor();
}
public static <
E extends Identifiable<K>,
B extends Identifiable<K>,
K extends Serializable
>
MergeResult<E, B, K> diff(Collection<E> entities, Collection<B> beans){
Map<K, B> modified = beans.stream()
.filter(b -> b.getId() != null)
.collect(Collectors.toMap(
b -> b.getId(), b -> b));
Set<K> modifiedIds = modified.keySet();
List<K> removedIds = entities.stream()
.filter(e -> !modifiedIds.contains(e.getId()))
.map(e -> e.getId())
.collect(Collectors.toList());
MergeResult<E, B, K> result = new MergeResult();
entities.forEach(e -> {
K id = e.getId();
if (modifiedIds.contains(id)) {
result.modified.add(id, new MutablePair<>(e, modified.get(id)));
} else if (removedIds.contains(id)) {
result.removed.add(e);
} else {
throw new IllegalStateException("Could not determine the status for entity " + id);
}
});
beans.stream()
.filter(b -> b.getId() == null)
.forEach(result.created::add);
return result;
}
public static class MergeResult<E, B, K> {
private List<B> created = Lists.newArrayList();
private MultiValueMap<K, Pair<E, B>> modified = new LinkedMultiValueMap<>();
private List<E> removed = Lists.newArrayList();
public List<B> getCreated() {
return Lists.newArrayList(created);
}
public MultiValueMap<K, Pair<E, B>> getModified() {
return new LinkedMultiValueMap<>(modified);
}
public List<E> getRemoved() {
return Lists.newArrayList(removed);
}
public MergeProcessor<E, B, K> processor(){
return new MergeProcessor(this);
}
}
public static class MergeProcessor<E, B, K> {
private final MergeResult<E, B, K> merge;
private Function<B, Optional<E>> create;
private BiFunction<E, B, Optional<E>> update;
private Function<E, Optional<E>> delete;
public <E, B, K> MergeProcessor(MergeResult merge) {
this.merge = merge;
}
public MergeProcessor<E, B, K> create(Function<B, Optional<E>> create){
this.create = create;
return this;
}
public MergeProcessor<E, B, K> update(BiFunction<E, B, Optional<E>> update){
this.update = update;
return this;
}
public MergeProcessor<E, B, K> delete(Function<E, Optional<E>> delete){
this.delete = delete;
return this;
}
public Stream<E> execute(){
List<E> result = Lists.newArrayList();
if( create != null ) {
merge.created.stream()
.map(create)
.forEach( unwrapOptional(result) );
}
if( update != null ) {
merge.modified.values().stream()
.forEach(l -> l.stream()
.map(p -> update.apply(p.getLeft(), p.getRight()) )
.forEach( unwrapOptional(result) )
);
}
if( delete != null ){
merge.removed.stream()
.map(delete)
.forEach( unwrapOptional(result) );
}
return result.stream();
}
private <T> Consumer<Optional<T>> unwrapOptional(Collection<T> results){
return (Optional<T> o) -> {
if( o != null ){
o.ifPresent(results::add);
}
};
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment