/*
* Copyright (C) 2012, 2016 higherfrequencytrading.com
* Copyright (C) 2016 Roman Leventov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.chronicle.hash.serialization;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.wire.WireIn;
import net.openhft.chronicle.wire.WireOut;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static net.openhft.chronicle.hash.serialization.StatefulCopyable.copyIfNeeded;
/**
* Marshaller of {@code List<T>}. Uses {@link ArrayList} as the list implementation to deserialize
* into.
*
* <p>This marshaller supports multimap emulation on top of Chronicle Map, that is possible but
* inefficient. See <a href="https://github.com/OpenHFT/Chronicle-Map#chronicle-map-is-not">the
* README section</a>.
*
* <p>Usage: <pre>{@code
* ChronicleMap<String, List<Integer>> regNumbers = ChronicleMap
* .of(String.class, (Class<List<Integer>>) (Class) List.class)
* .averageKey("John Smith")
* .valueMarshaller(ListMarshaller.of(IntegerMarshaller.INSTANCE))
* .averageValue(ImmutableList.of(1, 2, 3))
* .entries(10_000)
* .create();
* }</pre>
*
* <p>Look for pre-defined element marshallers in {@link
* net.openhft.chronicle.hash.serialization.impl} package. This package is not included into
* Javadocs, but present in Chronicle Map distribution. If there are no existing marshallers for
* your {@code List} element type, define {@link BytesReader} and {@link BytesWriter} yourself.
*
* @param <T> the element type of serialized Lists
* @see SetMarshaller
* @see MapMarshaller
*/
public final class ListMarshaller<T>
implements BytesReader<List<T>>, BytesWriter<List<T>>, StatefulCopyable<ListMarshaller<T>> {
/**
* Returns a {@code ListMarshaller} which uses the given list elements' serializers.
*
* @param elementReader list elements' reader
* @param elementWriter list elements' writer
* @param <T> type of list elements
* @return a {@code ListMarshaller} which uses the given list elements' serializers
*/
public static <T> ListMarshaller<T> of(
BytesReader<T> elementReader, BytesWriter<? super T> elementWriter) {
return new ListMarshaller<>(elementReader, elementWriter);
}
/**
* Returns a {@code ListMarshaller} which uses the given marshaller as both reader and writer of
* list elements. Example: <pre><code>
* ChronicleMap
* .of(String.class,{@code (Class<List<Integer>>)} ((Class) List.class))
* .valueMarshaller(ListMarshaller.of(IntegerMarshaller.INSTANCE))
* ...</code></pre>
* @param elementMarshaller list elements' marshaller
* @param <T> type of list elements
* @param <M> type of list elements' marshaller
* @return a {@code ListMarshaller} which uses the given list elements' marshaller
*/
public static <T, M extends BytesReader<T> & BytesWriter<? super T>> ListMarshaller<T> of(
M elementMarshaller) {
return of(elementMarshaller, elementMarshaller);
}
// Config fields
private BytesReader<T> elementReader;
private BytesWriter<? super T> elementWriter;
/**
* Constructs a {@code ListMarshaller} with the given list elements' serializers.
*
* <p>Use static factory {@link #of(BytesReader, BytesWriter)} instead of this constructor
* directly.
*
* @param elementReader list elements' reader
* @param elementWriter list elements' writer
*/
public ListMarshaller(BytesReader<T> elementReader, BytesWriter<? super T> elementWriter) {
this.elementReader = elementReader;
this.elementWriter = elementWriter;
}
@NotNull
@Override
public List<T> read(Bytes in, @Nullable List<T> using) {
int size = in.readInt();
if (using == null) {
using = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
using.add(null);
}
} else if (using.size() < size) {
while (using.size() < size) {
using.add(null);
}
} else if (using.size() > size) {
using.subList(size, using.size()).clear();
}
for (int i = 0; i < size; i++) {
using.set(i, elementReader.read(in, using.get(i)));
}
return using;
}
@Override
public void write(Bytes out, @NotNull List<T> toWrite) {
out.writeInt(toWrite.size());
// indexed loop to avoid garbage creation
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < toWrite.size(); i++) {
elementWriter.write(out, toWrite.get(i));
}
}
@Override
public ListMarshaller<T> copy() {
if (elementReader instanceof StatefulCopyable ||
elementWriter instanceof StatefulCopyable) {
return new ListMarshaller<>(copyIfNeeded(elementReader), copyIfNeeded(elementWriter));
} else {
return this;
}
}
@Override
public void readMarshallable(@NotNull WireIn wireIn) {
elementReader = wireIn.read(() -> "elementReader").typedMarshallable();
elementWriter = wireIn.read(() -> "elementWriter").typedMarshallable();
}
@Override
public void writeMarshallable(@NotNull WireOut wireOut) {
wireOut.write(() -> "elementReader").typedMarshallable(elementReader);
wireOut.write(() -> "elementWriter").typedMarshallable(elementWriter);
}
}