com.palantir.common.base.AbstractBatchingVisitable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlasdb-commons Show documentation
Show all versions of atlasdb-commons Show documentation
Palantir open source project
/*
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.palantir.common.base;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.palantir.logsafe.Preconditions;
import com.palantir.logsafe.exceptions.SafeIllegalStateException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This abstract class will implement the required methods in {@link BatchingVisitable}
* and will also implement the requires batchSize guarantee (only the last page is allowed to be
* smaller than the batch size).
*/
public abstract class AbstractBatchingVisitable implements BatchingVisitable {
@Override
public final boolean batchAccept(int batchSize, AbortingVisitor super List, K> v)
throws K {
Preconditions.checkArgument(batchSize > 0);
if (v instanceof ConsistentVisitor) {
@SuppressWarnings("unchecked")
AbortingVisitor, K> v2 = (AbortingVisitor, K>) v;
ConsistentVisitor consistentVisitor = (ConsistentVisitor) v2;
Preconditions.checkState(
consistentVisitor.visitorAlwaysReturnedTrue, "passed a visitor that has already said stop");
batchAcceptSizeHint(batchSize, consistentVisitor);
return consistentVisitor.visitorAlwaysReturnedTrue;
}
AbstractBatchingVisitable.ConsistentVisitor consistentVisitor =
AbstractBatchingVisitable.ConsistentVisitor.create(batchSize, v);
batchAcceptSizeHint(batchSize, consistentVisitor);
if (consistentVisitor.visitorAlwaysReturnedTrue && !consistentVisitor.buffer.isEmpty()) {
Preconditions.checkState(consistentVisitor.buffer.size() < batchSize);
return v.visit(Collections.unmodifiableList(consistentVisitor.buffer));
} else {
return consistentVisitor.visitorAlwaysReturnedTrue;
}
}
/**
* The batch size passed to this method is purely a hint.
* The underlying impl can batch up pages however it wants
* and pass them to the visitor. Batch size consistency is already taken care of by the
* {@link ConsistentVisitor}.
*/
protected abstract void batchAcceptSizeHint(int batchSizeHint, ConsistentVisitor v)
throws K;
protected static final class ConsistentVisitor implements AbortingVisitor, K> {
final int batchSize;
final AbortingVisitor super List, K> v;
List buffer = new ArrayList<>();
boolean visitorAlwaysReturnedTrue = true;
private ConsistentVisitor(int batchSize, AbortingVisitor super List, K> av) {
Preconditions.checkArgument(batchSize > 0);
this.batchSize = batchSize;
this.v = Preconditions.checkNotNull(av);
}
static ConsistentVisitor create(
int batchSize, AbortingVisitor super List, K> v) {
return new ConsistentVisitor(batchSize, v);
}
public boolean visitOne(T t) throws K {
return visit(ImmutableList.of(t));
}
@Override
public boolean visit(List list) throws K {
if (!visitorAlwaysReturnedTrue) {
throw new SafeIllegalStateException("Cannot keep visiting if visitor returns false.");
}
if (buffer.isEmpty() && list.size() == batchSize) {
// Special case: We have exactly one batch.
return visitBufferWithDelegate(Collections.unmodifiableList(list));
}
buffer.addAll(list);
if (buffer.size() < batchSize) {
return true;
}
return processBufferBatches();
}
private boolean visitBufferWithDelegate(List list) throws K {
boolean ret = v.visit(list);
visitorAlwaysReturnedTrue &= ret;
return ret;
}
private boolean processBufferBatches() throws K {
List> batches = Lists.partition(buffer, batchSize);
for (List batch : batches) {
if (batch.size() != batchSize) {
continue;
}
if (!visitBufferWithDelegate(Collections.unmodifiableList(batch))) {
return false;
}
}
List lastBatch = batches.get(batches.size() - 1);
if (lastBatch.size() == batchSize) {
buffer = new ArrayList<>();
} else {
buffer = new ArrayList<>(lastBatch);
}
return true;
}
}
}