io.permazen.util.AbstractMultiNavigableSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of permazen-util Show documentation
Show all versions of permazen-util Show documentation
Common utility classes used by Permazen.
The newest version!
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package io.permazen.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableSet;
import java.util.Objects;
/**
* Support superclass for {@link NavigableSet} implementations that join together multiple other {@link NavigableSet}s
* having equivalent {@link Comparator}s and for which {@link #size} is an expensive operation.
*
* @param element type
*/
abstract class AbstractMultiNavigableSet extends AbstractNavigableSet {
protected final ArrayList extends NavigableSet> list;
protected final Comparator super E> comparator;
/**
* Convenience constructor for the case where there are no lower or upper bounds.
*
* @param sets sets to be combined; assumed to not contain any {@link EmptyNavigableSet}s
* @throws IllegalArgumentException if the {@link NavigableSet}s in {@code sets} do not have equal {@link Comparator}s
*/
protected AbstractMultiNavigableSet(Collection extends NavigableSet> sets) {
this(sets, AbstractMultiNavigableSet.getComparator(sets), new Bounds<>());
}
/**
* Primary constructor.
*
* @param sets sets to be combined; assumed to not contain any {@link EmptyNavigableSet}s
* @param comparator common comparator
* @param bounds range restriction
* @throws IllegalArgumentException if the {@link NavigableSet}s in {@code sets} do not have equal {@link Comparator}s
* @throws IllegalArgumentException if {@code sets}, or any {@link NavigableSet} therein, is null
* @throws IllegalArgumentException if {@code bounds} is null
*/
protected AbstractMultiNavigableSet(Collection extends NavigableSet> sets,
Comparator super E> comparator, Bounds bounds) {
super(bounds);
Preconditions.checkArgument(sets != null, "null sets");
this.list = Lists.newArrayList(sets);
Preconditions.checkArgument(!this.list.contains(null), "null set");
this.comparator = comparator;
}
@Override
public Comparator super E> comparator() {
return this.comparator;
}
/**
* Get and verify a common {@link Comparator} (possibly null).
*/
private static Comparator super E> getComparator(Collection extends NavigableSet> sets) {
// Empty sets should have already been filtered out
assert sets.stream().noneMatch(EmptyNavigableSet.class::isInstance);
// Get the first comparator
final Iterator extends NavigableSet> i = sets.iterator();
if (!i.hasNext())
return null;
final Comparator super E> comparator = AbstractMultiNavigableSet.normalize(i.next().comparator());
// Verify remaining comparators are equal to it
while (i.hasNext()) {
final Comparator super E> comparator2 = AbstractMultiNavigableSet.normalize(i.next().comparator());
if (!Objects.equals(comparator2, comparator)) {
throw new IllegalArgumentException(String.format(
"sets have unequal comparators: %s != %s", comparator, comparator2));
}
}
// Done
return comparator;
}
// Convert comparators that sort naturally to null for comparison purposes.
private static Comparator normalize(Comparator comparator) {
if (comparator instanceof NaturalSortAware && ((NaturalSortAware)comparator).sortsNaturally())
return null;
return comparator;
}
@Override
protected final NavigableSet createSubSet(boolean reverse, Bounds newBounds) {
// Apply bounds to all sets
final Comparator super E> nonNullComparator = NavigableSets.getComparator(this.comparator(), false);
final ArrayList> newList = new ArrayList<>(this.list.size());
for (NavigableSet set : this.list) {
// Reverse set if needed
if (reverse)
set = set.descendingSet();
// Apply lower bound
if (newBounds.getLowerBoundType() != BoundType.NONE) {
try {
set = set.tailSet(newBounds.getLowerBound(), newBounds.getLowerBoundType().isInclusive());
} catch (IllegalArgumentException e) {
// Bound is out of range; it must be either too low or too high
if (set.isEmpty())
set = new EmptyNavigableSet<>(this.comparator());
else {
final int diff = nonNullComparator.compare(newBounds.getLowerBound(), set.last());
if (diff > 0)
set = new EmptyNavigableSet<>(this.comparator());
else if (diff == 0) {
if (newBounds.getLowerBoundType().isInclusive())
throw e; // this indicates faulty logic in the underlying set
set = new EmptyNavigableSet<>(this.comparator());
}
}
}
}
// Apply upper bound
if (newBounds.getUpperBoundType() != BoundType.NONE) {
try {
set = set.headSet(newBounds.getUpperBound(), newBounds.getUpperBoundType().isInclusive());
} catch (IllegalArgumentException e) {
// Bound is out of range; it must be either too low or too high
if (set.isEmpty())
set = new EmptyNavigableSet<>(this.comparator());
else {
final int diff = nonNullComparator.compare(newBounds.getUpperBound(), set.first());
if (diff < 0)
set = new EmptyNavigableSet<>(this.comparator());
else if (diff == 0) {
if (newBounds.getUpperBoundType().isInclusive())
throw e; // this indicates faulty logic in the underlying set
set = new EmptyNavigableSet<>(this.comparator());
}
}
}
}
// Add restricted set
newList.add(set);
}
// Create sub-set
return this.createSubSet(reverse, newBounds, newList);
}
/**
* Create a (possibly reversed) view of this instance with (possibly) tighter lower and/or upper bounds.
* The {@code newBounds} are consistent with the new ordering (i.e., reversed if {@code reverse} is true)
* and have already been range-checked against {@linkplain #bounds this instance's current bounds}.
*
* @param reverse whether the new set's ordering should be reversed relative to this instance's ordering
* @param newBounds new bounds
* @param list list of this instance's nested sets, restricted to {@code newBounds}
* @throws IllegalArgumentException if {@code newBounds} is null
*/
protected abstract NavigableSet createSubSet(boolean reverse, Bounds newBounds, List> list);
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy