
com.bakdata.deduplication.fusion.ConflictResolutions Maven / Gradle / Ivy
/*
* The MIT License
*
* Copyright (c) 2018 bakdata GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package com.bakdata.deduplication.fusion;
import com.bakdata.util.FunctionalClass;
import com.bakdata.util.ObjectUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import lombok.Value;
import lombok.experimental.UtilityClass;
import lombok.experimental.Wither;
@UtilityClass
public class ConflictResolutions {
public static Merge.MergeBuilder merge(final Supplier ctor) {
return Merge.builder(ctor);
}
public static Merge.MergeBuilder merge(final Class clazz) {
return Merge.builder(clazz);
}
@Value
public static class Merge implements ConflictResolution {
private final Supplier ctor;
private final List> fieldMerges;
@SuppressWarnings("unchecked")
static MergeBuilder builder(final Supplier ctor) {
return new MergeBuilder<>(ctor, FunctionalClass.from((Class) ctor.get().getClass()));
}
static MergeBuilder builder(final Class clazz) {
final FunctionalClass f = FunctionalClass.from(clazz);
return new MergeBuilder<>(f.getConstructor(), f);
}
@Override
public List> resolvePartially(final List> annotatedValues, final FusionContext context) {
final R r = this.ctor.get();
for (final FieldMerge, R> fieldMerge : this.fieldMerges) {
fieldMerge.mergeInto(r, annotatedValues, context);
}
return List.of(AnnotatedValue.calculated(r));
}
@Value
private static class FieldMerge {
Function getter;
BiConsumer setter;
@Wither
ConflictResolution resolution;
void mergeInto(final R r, final Collection> annotatedValues, final FusionContext context) {
final List> fieldValues = annotatedValues.stream()
.map(ar -> ar.withValue(this.getter.apply(ar.getValue())))
.filter(ar -> ObjectUtils.isNonEmpty(ar.getValue()))
.collect(Collectors.toList());
context.safeExecute(() -> {
final Optional resolvedValue = this.resolution.resolve(fieldValues, context);
resolvedValue.ifPresent(v -> this.setter.accept(r, v));
});
}
}
@Value
public static class MergeBuilder {
Supplier ctor;
FunctionalClass clazz;
List> fieldMerges = new ArrayList<>();
public FieldMergeBuilder field(final Function getter, final BiConsumer setter) {
return new FieldMergeBuilder<>(this, getter, setter);
}
public FieldMergeBuilder field(final FunctionalClass.Field field) {
final Function getter = field.getGetter();
final BiConsumer setter = field.getSetter();
return this.field(getter, setter);
}
public FieldMergeBuilder field(final String name) {
final FunctionalClass.Field field = this.clazz.field(name);
return this.field(field);
}
void replaceLast(final FieldMerge, R> fieldMerge) {
this.fieldMerges.set(this.fieldMerges.size() - 1, fieldMerge);
}
@SuppressWarnings("squid:S1452")
FieldMerge, R> getLast() {
if (this.fieldMerges.isEmpty()) {
throw new IllegalStateException();
}
return this.fieldMerges.get(this.fieldMerges.size() - 1);
}
public ConflictResolution build() {
return new Merge<>(this.ctor, this.fieldMerges);
}
private void add(final FieldMerge, R> fieldMerge) {
this.fieldMerges.add(fieldMerge);
}
}
@Value
public static class FieldMergeBuilder {
MergeBuilder mergeBuilder;
Function getter;
BiConsumer setter;
public AdditionalFieldMergeBuilder with(final ConflictResolution resolution) {
return new AdditionalFieldMergeBuilder<>(this.convertingWith(resolution));
}
@SafeVarargs
public final AdditionalFieldMergeBuilder with(final ConflictResolution resolution, final ConflictResolution... resolutions) {
return this.with(Arrays.stream(resolutions).reduce(resolution, ConflictResolution::andThen));
}
public IllTypedFieldMergeBuilder convertingWith(final ConflictResolution resolution) {
return new IllTypedFieldMergeBuilder<>(this, resolution);
}
public AdditionalFieldMergeBuilder corresponding(final ResolutionTag> tag) {
return this.with(CommonConflictResolutions.corresponding(tag));
}
@SuppressWarnings("unchecked")
public AdditionalFieldMergeBuilder correspondingToPrevious() {
final var last = this.mergeBuilder.getLast();
final ResolutionTag tag;
// auto tag previous merge if it is not tagged already
if (last.getResolution() instanceof CommonConflictResolutions.TaggedResolution) {
tag = ((CommonConflictResolutions.TaggedResolution, ?>) last.getResolution()).getResolutionTag();
} else {
final var fieldMerges = this.mergeBuilder.getFieldMerges();
tag = new ResolutionTag<>("tag-" + System.identityHashCode(fieldMerges) + "-" + fieldMerges.size());
this.mergeBuilder.replaceLast(last.withResolution(
CommonConflictResolutions.saveAs(last.getResolution(), tag)));
}
return this.corresponding(tag);
}
void finish(final ConflictResolution resolution) {
this.mergeBuilder.add(new FieldMerge<>(this.getter, this.setter, resolution));
}
}
@Value
public static class IllTypedFieldMergeBuilder {
FieldMergeBuilder fieldMergeBuilder;
ConflictResolution resolution;
public IllTypedFieldMergeBuilder then(final ConflictResolution resolution) {
return new IllTypedFieldMergeBuilder<>(this.fieldMergeBuilder, this.resolution.andThen(resolution));
}
public AdditionalFieldMergeBuilder convertingBack(final ConflictResolution resolution) {
return new AdditionalFieldMergeBuilder<>(this.then(resolution));
}
MergeBuilder getMergeBuilder() {
return this.getFieldMergeBuilder().getMergeBuilder();
}
}
@Value
public static class AdditionalFieldMergeBuilder {
IllTypedFieldMergeBuilder inner;
public FieldMergeBuilder field(final Function getter, final BiConsumer setter) {
this.inner.getFieldMergeBuilder().finish(this.inner.getResolution());
return new FieldMergeBuilder<>(this.inner.getMergeBuilder(), getter, setter);
}
public FieldMergeBuilder field(final FunctionalClass.Field field) {
final Function getter = field.getGetter();
final BiConsumer setter = field.getSetter();
return this.field(getter, setter);
}
public FieldMergeBuilder field(final String name) {
final FunctionalClass clazz = this.inner.getMergeBuilder().getClazz();
final FunctionalClass.Field field = clazz.field(name);
return this.field(field);
}
public IllTypedFieldMergeBuilder convertingWith(final ConflictResolution resolution) {
return this.inner.then(resolution);
}
public AdditionalFieldMergeBuilder then(final ConflictResolution resolution) {
return new AdditionalFieldMergeBuilder<>(this.inner.then(resolution));
}
public ConflictResolution build() {
this.inner.getFieldMergeBuilder().finish(this.inner.getResolution());
return this.inner.getMergeBuilder().build();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy