package com.fasterxml.jackson.datatype.hppc.ser;

import java.io.IOException;
import java.lang.reflect.Type;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.carrotsearch.hppc.*;
import com.carrotsearch.hppc.predicates.*;

public class HppcContainerSerializers
{
    public final static ContainerSerializerBase<?>[] _primitiveSerializers =
        new ContainerSerializerBase<?>[] {
                new ByteContainerSerializer(),
                new ShortContainerSerializer(),
                new IntContainerSerializer(),
                new LongContainerSerializer(),
                new CharContainerSerializer(),
                new FloatContainerSerializer(),
                new DoubleContainerSerializer(),
                
                new BitSetSerializer()
        };

    
Method called to see if this serializer (or a serializer this serializer knows) should be used for given type; if not, null is returned.
/** * Method called to see if this serializer (or a serializer this serializer * knows) should be used for given type; if not, null is returned. */
public static JsonSerializer<?> getMatchingSerializer(SerializationConfig config, JavaType type) { for (ContainerSerializerBase<?> ser : _primitiveSerializers) { JsonSerializer<?> actual = ser.getSerializer(type); if (actual != null) { return actual; } } return null; } /* /********************************************************************** /* Concrete container implementations; basic integral types /********************************************************************** */
Byte containers are handled similar to byte[], meaning that they are actually serialized as base64-encoded Strings by default

TODO: allow specifying other modes (serialize as array?)

/** * Byte containers are handled similar to byte[], meaning that they are * actually serialized as base64-encoded Strings by default *<p> * TODO: allow specifying other modes (serialize as array?) */
static class ByteContainerSerializer extends ContainerSerializerBase<ByteContainer> { private static final long serialVersionUID = 1L; ByteContainerSerializer() { super(ByteContainer.class, "string"); // really, "binary", but... } @Override public JsonNode getSchema(SerializerProvider provider, Type typeHint) { return createSchemaNode("string", true); } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { // Logical content byte array/stream, but physically a JSON String so: if (visitor != null) visitor.expectStringFormat(typeHint); } @Override public boolean isEmpty(SerializerProvider provider, ByteContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(ByteContainer value) { return value.size() == 1; } @Override public void serialize(ByteContainer value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.setCurrentValue(value); serializeContents(value, gen, provider); } @Override public void serializeWithType(ByteContainer value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { gen.setCurrentValue(value); WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_EMBEDDED_OBJECT)); serializeContents(value, gen, provider); typeSer.writeTypeSuffix(gen, typeIdDef); } @Override protected void serializeContents(final ByteContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeBinary(value.toArray()); } } final static class ShortContainerSerializer extends ContainerSerializerBase<ShortContainer> { private static final long serialVersionUID = 1L; ShortContainerSerializer() { super(ShortContainer.class, "integer"); } @Override public boolean isEmpty(SerializerProvider provider, ShortContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(ShortContainer value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); if (v2 != null) { v2.itemsFormat(JsonFormatTypes.INTEGER); } } } @Override protected void serializeContents(final ShortContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException { if (value instanceof ShortIndexedContainer) { ShortIndexedContainer list = (ShortIndexedContainer) value; for (int i = 0, len = list.size(); i < len; ++i) { gen.writeNumber(list.get(i)); } return; } final ExceptionHolder holder = new ExceptionHolder(); value.forEach(new ShortPredicate() { @Override public boolean apply(short v) { try { gen.writeNumber(v); } catch (IOException e) { holder.assignException(e); return false; } return true; } }); holder.throwHeld(); } }
Handler for HPPC containers that store int values. Specific in that we actually implement separate optimal serializer for indexed type, given how common this type is.
/** * Handler for HPPC containers that store int values. * Specific in that we actually implement separate optimal serializer * for indexed type, given how common this type is. */
static class IntContainerSerializer extends ContainerSerializerBase<IntContainer> { private static final long serialVersionUID = 1L; IntContainerSerializer() { super(IntContainer.class, "integer"); } // Overridden to allow use of more optimized serialized for indexed variant @Override public JsonSerializer<?> getSerializer(JavaType type) { JsonSerializer<?> ser = super.getSerializer(type); if (ser != null) { if (IntIndexedContainer.class.isAssignableFrom(type.getClass())) { return new Indexed(); } } return ser; } @Override public boolean isEmpty(SerializerProvider provider, IntContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(IntContainer value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); if (v2 != null) { v2.itemsFormat(JsonFormatTypes.INTEGER); } } } @Override protected void serializeContents(final IntContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { // doh. Can't throw checked exceptions through; hence need convoluted handling... final ExceptionHolder holder = new ExceptionHolder(); value.forEach(new IntPredicate() { @Override public boolean apply(int v) { try { gen.writeNumber(v); } catch (IOException e) { holder.assignException(e); return false; } return true; } }); holder.throwHeld(); } // Specialized variant to support indexed int container with more efficient accessor static class Indexed extends ContainerSerializerBase<IntIndexedContainer> { private static final long serialVersionUID = 1L; Indexed() { super(IntIndexedContainer.class, "integer"); } @Override public boolean isEmpty(SerializerProvider provider, IntIndexedContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(IntIndexedContainer value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); if (v2 != null) { v2.itemsFormat(JsonFormatTypes.INTEGER); } } } @Override protected void serializeContents(final IntIndexedContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { int[] array; if (value instanceof IntArrayList) { array = ((IntArrayList) value).buffer; } else { array = value.toArray(); } for (int i = 0, len = value.size(); i < len; ++i) { gen.writeNumber(array[i]); } return; } } } final static class LongContainerSerializer extends ContainerSerializerBase<LongContainer> { private static final long serialVersionUID = 1L; LongContainerSerializer() { super(LongContainer.class, "integer"); } @Override public boolean isEmpty(SerializerProvider provider, LongContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(LongContainer value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); if (v2 != null) { v2.itemsFormat(JsonFormatTypes.INTEGER); } } } @Override protected void serializeContents(final LongContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { if (value instanceof LongIndexedContainer) { LongIndexedContainer list = (LongIndexedContainer) value; long[] array; if (value instanceof LongArrayList) { array = ((LongArrayList) value).buffer; } else { array = list.toArray(); } for (int i = 0, len = list.size(); i < len; ++i) { gen.writeNumber(array[i]); } return; } // doh. Can't throw checked exceptions through; hence need convoluted handling... final ExceptionHolder holder = new ExceptionHolder(); value.forEach(new LongPredicate() { @Override public boolean apply(long v) { try { gen.writeNumber(v); } catch (IOException e) { holder.assignException(e); return false; } return true; } }); holder.throwHeld(); } } /* /********************************************************************** /* Concrete container implementations; char /********************************************************************** */
This one is bit tricky: could serialize in multiple ways; for example:
  • String that contains all characters (in order)
  • Array that contains single-character Strings
  • Array that contains numbers that represent character codes
Let's start with the first option
/** * This one is bit tricky: could serialize in multiple ways; * for example: *<ul> * <li>String that contains all characters (in order)</li> * <li>Array that contains single-character Strings</li> * <li>Array that contains numbers that represent character codes</li> *</ul> * * Let's start with the first option */
final static class CharContainerSerializer extends ContainerSerializerBase<CharContainer> { private static final long serialVersionUID = 1L; CharContainerSerializer() { super(CharContainer.class, "string"); } @Override public boolean isEmpty(SerializerProvider provider, CharContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(CharContainer value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { // Logical content byte array/stream, but physically a JSON String so: if (visitor != null) visitor.expectStringFormat(typeHint); } @Override public void serialize(CharContainer value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.setCurrentValue(value); serializeContents(value, gen, provider); } @Override public void serializeWithType(CharContainer value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { gen.setCurrentValue(value); WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_STRING)); serializeContents(value, gen, provider); typeSer.writeTypeSuffix(gen, typeIdDef); } @Override protected void serializeContents(final CharContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException { char[] ch = value.toArray(); gen.writeString(ch, 0, ch.length); } } /* /********************************************************************** /* Concrete container implementations; floating-point types /********************************************************************** */ final static class FloatContainerSerializer extends ContainerSerializerBase<FloatContainer> { private static final long serialVersionUID = 1L; FloatContainerSerializer() { super(FloatContainer.class, "number"); } @Override public boolean isEmpty(SerializerProvider provider, FloatContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(FloatContainer value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); if (v2 != null) { v2.itemsFormat(JsonFormatTypes.NUMBER); } } } @Override protected void serializeContents(final FloatContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { if (value instanceof FloatIndexedContainer) { FloatIndexedContainer list = (FloatIndexedContainer) value; for (int i = 0, len = list.size(); i < len; ++i) { gen.writeNumber(list.get(i)); } return; } // doh. Can't throw checked exceptions through; hence need convoluted handling... final ExceptionHolder holder = new ExceptionHolder(); value.forEach(new FloatPredicate() { @Override public boolean apply(float v) { try { gen.writeNumber(v); } catch (IOException e) { holder.assignException(e); return false; } return true; } }); holder.throwHeld(); } } final static class DoubleContainerSerializer extends ContainerSerializerBase<DoubleContainer> { private static final long serialVersionUID = 1L; DoubleContainerSerializer() { super(DoubleContainer.class, "number"); } @Override public boolean isEmpty(SerializerProvider provider, DoubleContainer value) { return value.isEmpty(); } @Override public boolean hasSingleElement(DoubleContainer value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); if (v2 != null) { v2.itemsFormat(JsonFormatTypes.NUMBER); } } } @Override protected void serializeContents(final DoubleContainer value, final JsonGenerator gen, SerializerProvider provider) throws IOException, JsonGenerationException { if (value instanceof DoubleIndexedContainer) { DoubleIndexedContainer list = (DoubleIndexedContainer) value; for (int i = 0, len = list.size(); i < len; ++i) { gen.writeNumber(list.get(i)); } return; } // doh. Can't throw checked exceptions through; hence need convoluted handling... final ExceptionHolder holder = new ExceptionHolder(); value.forEach(new DoublePredicate() { @Override public boolean apply(double v) { try { gen.writeNumber(v); } catch (IOException e) { holder.assignException(e); return false; } return true; } }); holder.throwHeld(); } } /* /********************************************************************** /* Concrete container implementations, other /********************************************************************** */
The default implementation is not particularly efficient, as it outputs things as an arrays of boolean values. There are many ways to achieve more efficient storage; either by outputting ints/longs, or by using base64 encoding. But for now this will work functionally correctly, and we can optimize better later on, as need be.
/** * The default implementation is not particularly efficient, as it outputs * things as an arrays of boolean values. There are many ways to achieve * more efficient storage; either by outputting ints/longs, or by using * base64 encoding. But for now this will work functionally correctly, * and we can optimize better later on, as need be. */
final static class BitSetSerializer extends ContainerSerializerBase<BitSet> { private static final long serialVersionUID = 1L; BitSetSerializer() { super(BitSet.class, "boolean"); } @Override public boolean isEmpty(SerializerProvider provider, BitSet value) { return value.isEmpty(); } @Override public boolean hasSingleElement(BitSet value) { return value.size() == 1; } @Override public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException { if (visitor != null) { JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint); if (v2 != null) { v2.itemsFormat(JsonFormatTypes.BOOLEAN); } } } @Override protected void serializeContents(final BitSet value, final JsonGenerator gen, SerializerProvider provider) throws IOException { // is size() close enough to the last set bit? if (!value.isEmpty()) { for (int i = 0, len = (int) value.size(); i < len; ++i) { gen.writeBoolean(value.get(i)); } } } } }