/**
* Copyright 2014 Ricardo Padilha
*
* 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 net.dsys.snio.impl.group;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nonnull;
import net.dsys.commons.api.lang.Copier;
import net.dsys.snio.api.buffer.MessageBufferProducer;
import net.dsys.snio.api.group.GroupData;
/**
* @author Ricardo Padilha
*/
final class GroupMessageBufferProducer<T> implements MessageBufferProducer<T> {
private static final int INITIAL_SEQUENCE_VALUE = -1;
@Nonnull
private final Copier<T> copier;
@Nonnull
private final List<MessageBufferProducer<T>> buffers;
private long last;
GroupMessageBufferProducer(@Nonnull final Copier<T> copier,
@Nonnull final Collection<MessageBufferProducer<T>> buffers) {
if (copier == null) {
throw new NullPointerException("copier == null");
}
if (buffers == null) {
throw new NullPointerException("buffers == null");
}
if (buffers.isEmpty()) {
throw new IllegalArgumentException("buffers.isEmpty()");
}
this.copier = copier;
this.buffers = new ArrayList<>(buffers);
this.last = INITIAL_SEQUENCE_VALUE;
}
/**
* {@inheritDoc}
*/
@Override
public long acquire() throws InterruptedException {
long sequence = INITIAL_SEQUENCE_VALUE;
for (final MessageBufferProducer<T> buffer : buffers) {
final long seq = buffer.acquire();
if (sequence == INITIAL_SEQUENCE_VALUE) {
sequence = seq;
} else if (sequence != seq) {
throw new IllegalStateException(
String.format("sequence numbers for MessageBuffers are not matching: %d != %d",
Long.valueOf(seq), Long.valueOf(sequence)));
}
}
return sequence;
}
/**
* {@inheritDoc}
*/
@Override
public long acquire(final int n) throws InterruptedException {
long sequence = INITIAL_SEQUENCE_VALUE;
for (final MessageBufferProducer<T> buffer : buffers) {
final long seq = buffer.acquire();
if (sequence == INITIAL_SEQUENCE_VALUE) {
sequence = seq;
} else {
sequence = Math.min(sequence, seq);
}
}
return sequence;
}
/**
* {@inheritDoc}
*/
@Override
public int remaining() {
int rem = Integer.MAX_VALUE;
for (final MessageBufferProducer<T> buffer : buffers) {
rem = Math.min(rem, buffer.remaining());
}
return rem;
}
/**
* {@inheritDoc}
*/
@Override
public T get(final long sequence) {
return buffers.get(0).get(sequence);
}
/**
* {@inheritDoc}
*/
@Override
public void attach(final long sequence, final Object attachment) {
if (attachment instanceof GroupData) {
final GroupData<?> data = (GroupData<?>) attachment;
if (buffers.size() != data.size()) {
throw new IllegalArgumentException("buffers.size() != data.size()");
}
final int k = buffers.size();
for (int i = 0; i < k; i++) {
buffers.get(i).attach(sequence, data.get(i));
}
} else {
for (final MessageBufferProducer<T> buffer : buffers) {
buffer.attach(sequence, attachment);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void release(final long sequence) throws InterruptedException {
final int k = buffers.size();
for (long s = last + 1; s <= sequence; s++) {
final T in = buffers.get(0).get(s);
for (int i = 1; i < k; i++) {
final T out = buffers.get(i).get(s);
copier.copy(in, out);
}
}
for (final MessageBufferProducer<T> buffer : buffers) {
buffer.release(sequence);
}
last = sequence;
}
@Nonnull
static <T> Builder<T> build() {
return new Builder<>();
}
/**
* @author Ricardo Padilha
*/
static final class Builder<T> {
private final List<MessageBufferProducer<T>> list;
private Copier<T> copier;
Builder() {
this.list = new ArrayList<>();
this.copier = null;
}
public void setCopier(@Nonnull final Copier<T> copier) {
if (copier == null) {
throw new NullPointerException("copier == null");
}
this.copier = copier;
}
public Builder<T> add(@Nonnull final MessageBufferProducer<T> producer) {
if (producer == null) {
throw new NullPointerException("producer == null");
}
list.add(producer);
return this;
}
@Nonnull
public GroupMessageBufferProducer<T> build() {
return new GroupMessageBufferProducer<>(copier, list);
}
}
}