common-utils/src/main/java/org/warp/commonutils/range/MappedRanges.java
2020-06-12 18:36:36 +02:00

122 lines
4.6 KiB
Java

package org.warp.commonutils.range;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.Comparator;
import java.util.function.Function;
public class MappedRanges<T> {
private final Object2ObjectMap<Range, T> ranges;
public MappedRanges(int start, int end, T value) {
if (start > end) {
throw new IndexOutOfBoundsException();
}
this.ranges = new Object2ObjectOpenHashMap<>();
ranges.put(new Range(start, end), value);
}
public void deleteRange(final int start, final int end, Function<T, T> replaceWhenSplitting, Function<T, T> cloneWhenSplitting) {
if (start > end) {
throw new IndexOutOfBoundsException();
}
Object2ObjectOpenHashMap<Range, T> rangesToAdd = new Object2ObjectOpenHashMap<>();
ObjectOpenHashSet<Range> rangesToDelete = new ObjectOpenHashSet<>();
ranges.forEach((range, value) -> {
if (range.start <= end && range.end >= start) {
if (range.start >= start && range.end <= end) {
// delete the range
rangesToDelete.add(range);
} else if (range.start <= start && range.end >= end) {
// cut the hole
rangesToDelete.add(range);
rangesToAdd.put(new Range(range.start, start), value);
rangesToAdd.put(new Range(end, range.end), cloneWhenSplitting.apply(value));
} else if (range.start <= start && range.end <= end && range.end > start) {
// shrink the right border
rangesToDelete.add(range);
rangesToAdd.put(new Range(range.start, start), value);
} else if (range.start >= start && range.end >= end && range.start < end) {
// shrink the left border
rangesToDelete.add(range);
rangesToAdd.put(new Range(end, range.end), value);
}
}
});
for (Range range : rangesToDelete) {
ranges.remove(range);
}
rangesToAdd.forEach((range, value) -> {
if (canAddRange(range)) {
ranges.put(range, replaceWhenSplitting.apply(value));
}
});
}
public void transformRange(int start, int end, Function<T, T> replaceWhenOverlapping, Function<T, T> cloneWhenSplitting) {
if (start > end) {
throw new IndexOutOfBoundsException();
}
Object2ObjectOpenHashMap<Range, T> rangesToTransform = new Object2ObjectOpenHashMap<>();
Object2ObjectOpenHashMap<Range, T> rangesToAdd = new Object2ObjectOpenHashMap<>();
ObjectOpenHashSet<Range> rangesToRemove = new ObjectOpenHashSet<>();
ranges.forEach((range, value) -> {
if (range.start <= end && range.end >= start) {
if (range.start >= start && range.end <= end) {
// transform the range
rangesToTransform.put(range, replaceWhenOverlapping.apply(value));
} else if (range.start <= start && range.end >= end) {
// transform the middle
rangesToRemove.add(range);
rangesToAdd.put(new Range(range.start, start), value);
rangesToTransform.put(new Range(start, end), replaceWhenOverlapping.apply(cloneWhenSplitting.apply(value)));
rangesToAdd.put(new Range(end, range.end), cloneWhenSplitting.apply(value));
} else if (range.start <= start && range.end <= end && range.end > start) {
// transform the right
rangesToRemove.add(range);
rangesToAdd.put(new Range(range.start, start), value);
rangesToTransform.put(new Range(start, range.end), replaceWhenOverlapping.apply(cloneWhenSplitting.apply(value)));
} else if (range.start >= start && range.end >= end && range.start < end) {
// transform the left
rangesToRemove.add(range);
rangesToTransform.put(new Range(range.start, end), replaceWhenOverlapping.apply(cloneWhenSplitting.apply(value)));
rangesToAdd.put(new Range(end, range.end), value);
} else {
// do not transform
}
}
});
rangesToRemove.forEach((range) -> {
ranges.remove(range);
});
rangesToAdd.forEach((range, value) -> {
if (canAddRange(range)) {
ranges.put(range, value);
}
});
rangesToTransform.forEach((range, value) -> {
ranges.put(range, value);
});
}
private boolean canAddRange(UnmodifiableRange range) {
return range.getStart() != range.getEnd();
}
private boolean canAddRange(Range range) {
return range.getStart() != range.getEnd();
}
public Object2ObjectMap<UnmodifiableRange, T> getRanges() {
Object2ObjectSortedMap<UnmodifiableRange, T> a = new Object2ObjectRBTreeMap<>(Comparator.comparingLong(UnmodifiableRange::getStart));
ranges.forEach((range, value) -> a.put(range.unmodifiableClone(), value));
return Object2ObjectMaps.unmodifiable(a);
}
}