blob: d5b768154eeb7a8da8d73d255673c70fce6eeaa4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.tinkerpop.gremlin.structure.io.gryo;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.tinkerpop.gremlin.process.computer.GraphFilter;
import org.apache.tinkerpop.gremlin.process.computer.MapReduce;
import org.apache.tinkerpop.gremlin.process.computer.util.MapMemory;
import org.apache.tinkerpop.gremlin.process.traversal.Contains;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.FoldStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupCountStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupStepV3d0;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.OrderGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.TreeStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.ProfileStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_LP_O_P_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_LP_O_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_O_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_O_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.LP_O_OB_P_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.LP_O_OB_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.O_OB_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.O_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.ImmutableMetrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.io.IoRegistry;
import org.apache.tinkerpop.gremlin.structure.io.Mapper;
import org.apache.tinkerpop.gremlin.structure.io.gryo.kryoshim.SerializerShim;
import org.apache.tinkerpop.gremlin.structure.io.gryo.kryoshim.shaded.ShadedSerializerAdapter;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedPath;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceEdge;
import org.apache.tinkerpop.gremlin.structure.util.reference.ReferencePath;
import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceProperty;
import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex;
import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexProperty;
import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph;
import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphSerializer;
import org.apache.tinkerpop.shaded.kryo.ClassResolver;
import org.apache.tinkerpop.shaded.kryo.Kryo;
import org.apache.tinkerpop.shaded.kryo.KryoSerializable;
import org.apache.tinkerpop.shaded.kryo.Serializer;
import org.apache.tinkerpop.shaded.kryo.serializers.JavaSerializer;
import org.apache.tinkerpop.shaded.kryo.util.DefaultStreamFactory;
import org.apache.tinkerpop.shaded.kryo.util.MapReferenceResolver;
import org.javatuples.Pair;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* A {@link Mapper} implementation for Kryo. This implementation requires that all classes to be serialized by
* Kryo are registered to it.
* <p/>
* {@link Graph} implementations providing an {@link IoRegistry} should register their custom classes and/or
* serializers in one of three ways:
* <p/>
* <ol>
* <li>Register just the custom class with a {@code null} {@link Serializer} implementation</li>
* <li>Register the custom class with a {@link Serializer} implementation</li>
* <li>
* Register the custom class with a {@code Function<Kryo, Serializer>} for those cases where the
* {@link Serializer} requires the {@link Kryo} instance to get constructed.
* </li>
* </ol>
* <p/>
* For example:
* <pre>
* {@code
* public class MyGraphIoRegistry extends AbstractIoRegistry {
* public MyGraphIoRegistry() {
* register(GryoIo.class, MyGraphIdClass.class, new MyGraphIdSerializer());
* }
* }
* }
* </pre>
*
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public final class GryoMapper implements Mapper<Kryo> {
public static final byte[] GIO = "gio".getBytes();
public static final byte[] HEADER = Arrays.copyOf(GIO, 16);
private final List<TypeRegistration<?>> typeRegistrations;
private final boolean registrationRequired;
private final boolean referenceTracking;
private final Supplier<ClassResolver> classResolver;
private GryoMapper(final Builder builder) {
this.typeRegistrations = builder.typeRegistrations;
validate();
this.registrationRequired = builder.registrationRequired;
this.referenceTracking = builder.referenceTracking;
this.classResolver = builder.classResolver;
}
@Override
public Kryo createMapper() {
final Kryo kryo = new Kryo(classResolver.get(), new MapReferenceResolver(), new DefaultStreamFactory());
kryo.addDefaultSerializer(Map.Entry.class, new EntrySerializer());
kryo.setRegistrationRequired(registrationRequired);
kryo.setReferences(referenceTracking);
for (TypeRegistration tr : typeRegistrations)
tr.registerWith(kryo);
return kryo;
}
public List<Class> getRegisteredClasses() {
return this.typeRegistrations.stream().map(TypeRegistration::getTargetClass).collect(Collectors.toList());
}
public List<TypeRegistration<?>> getTypeRegistrations() {
return typeRegistrations;
}
public static Builder build() {
return new Builder();
}
private void validate() {
final Set<Integer> duplicates = new HashSet<>();
final Set<Integer> ids = new HashSet<>();
typeRegistrations.forEach(t -> {
if (!ids.contains(t.getId()))
ids.add(t.getId());
else
duplicates.add(t.getId());
});
if (duplicates.size() > 0)
throw new IllegalStateException("There are duplicate kryo identifiers in use: " + duplicates);
}
/**
* A builder to construct a {@link GryoMapper} instance.
*/
public final static class Builder implements Mapper.Builder<Builder> {
/**
* Map with one entry that is used so that it is possible to get the class of LinkedHashMap.Entry.
*/
private static final LinkedHashMap m = new LinkedHashMap() {{
put("junk", "dummy");
}};
private static final Class LINKED_HASH_MAP_ENTRY_CLASS = m.entrySet().iterator().next().getClass();
/**
* The {@code HashMap$Node} class comes into serialization play when a {@code Map.entrySet()} is
* serialized.
*/
private static final Class HASH_MAP_NODE;
static {
// have to instantiate this via reflection because it is a private inner class of HashMap
final String className = HashMap.class.getName() + "$Node";
try {
HASH_MAP_NODE = Class.forName(className);
} catch (Exception ex) {
throw new RuntimeException("Could not access " + className, ex);
}
}
/**
* Note that the following are pre-registered boolean, Boolean, byte, Byte, char, Character, double, Double,
* int, Integer, float, Float, long, Long, short, Short, String, void.
*/
private final List<TypeRegistration<?>> typeRegistrations = new ArrayList<TypeRegistration<?>>() {{
add(GryoTypeReg.of(byte[].class, 25));
add(GryoTypeReg.of(char[].class, 26));
add(GryoTypeReg.of(short[].class, 27));
add(GryoTypeReg.of(int[].class, 28));
add(GryoTypeReg.of(long[].class, 29));
add(GryoTypeReg.of(float[].class, 30));
add(GryoTypeReg.of(double[].class, 31));
add(GryoTypeReg.of(String[].class, 32));
add(GryoTypeReg.of(Object[].class, 33));
add(GryoTypeReg.of(ArrayList.class, 10));
add(GryoTypeReg.of(BigInteger.class, 34));
add(GryoTypeReg.of(BigDecimal.class, 35));
add(GryoTypeReg.of(Calendar.class, 39));
add(GryoTypeReg.of(Class.class, 41));
add(GryoTypeReg.of(Collection.class, 37));
add(GryoTypeReg.of(Collections.EMPTY_LIST.getClass(), 51));
add(GryoTypeReg.of(Collections.EMPTY_MAP.getClass(), 52));
add(GryoTypeReg.of(Collections.EMPTY_SET.getClass(), 53));
add(GryoTypeReg.of(Collections.singleton(null).getClass(), 54));
add(GryoTypeReg.of(Collections.singletonList(null).getClass(), 24));
add(GryoTypeReg.of(Collections.singletonMap(null, null).getClass(), 23));
add(GryoTypeReg.of(Contains.class, 49));
add(GryoTypeReg.of(Currency.class, 40));
add(GryoTypeReg.of(Date.class, 38));
add(GryoTypeReg.of(Direction.class, 12));
add(GryoTypeReg.of(DetachedEdge.class, 21));
add(GryoTypeReg.of(DetachedVertexProperty.class, 20));
add(GryoTypeReg.of(DetachedProperty.class, 18));
add(GryoTypeReg.of(DetachedVertex.class, 19));
add(GryoTypeReg.of(DetachedPath.class, 60));
// skip 14
add(GryoTypeReg.of(EnumSet.class, 46));
add(GryoTypeReg.of(HashMap.class, 11));
add(GryoTypeReg.of(HashMap.Entry.class, 16));
add(GryoTypeReg.of(HASH_MAP_NODE, 92));
add(GryoTypeReg.of(KryoSerializable.class, 36));
add(GryoTypeReg.of(LinkedHashMap.class, 47));
add(GryoTypeReg.of(LinkedHashSet.class, 71));
add(GryoTypeReg.of(LinkedList.class, 116));
add(GryoTypeReg.of(LINKED_HASH_MAP_ENTRY_CLASS, 15));
add(GryoTypeReg.of(Locale.class, 22));
add(GryoTypeReg.of(StringBuffer.class, 43));
add(GryoTypeReg.of(StringBuilder.class, 44));
add(GryoTypeReg.of(T.class, 48));
add(GryoTypeReg.of(TimeZone.class, 42));
add(GryoTypeReg.of(TreeMap.class, 45));
add(GryoTypeReg.of(TreeSet.class, 50));
add(GryoTypeReg.of(UUID.class, 17, new UUIDSerializer()));
add(GryoTypeReg.of(URI.class, 72, new URISerializer()));
add(GryoTypeReg.of(VertexTerminator.class, 13));
add(GryoTypeReg.of(AbstractMap.SimpleEntry.class, 120));
add(GryoTypeReg.of(AbstractMap.SimpleImmutableEntry.class, 121)); // ***LAST ID***
add(GryoTypeReg.of(ReferenceEdge.class, 81));
add(GryoTypeReg.of(ReferenceVertexProperty.class, 82));
add(GryoTypeReg.of(ReferenceProperty.class, 83));
add(GryoTypeReg.of(ReferenceVertex.class, 84));
add(GryoTypeReg.of(ReferencePath.class, 85));
add(GryoTypeReg.of(StarGraph.class, 86, new StarGraphSerializer(Direction.BOTH, new GraphFilter())));
add(GryoTypeReg.of(Edge.class, 65, new GryoSerializers.EdgeSerializer()));
add(GryoTypeReg.of(Vertex.class, 66, new GryoSerializers.VertexSerializer()));
add(GryoTypeReg.of(Property.class, 67, new GryoSerializers.PropertySerializer()));
add(GryoTypeReg.of(VertexProperty.class, 68, new GryoSerializers.VertexPropertySerializer()));
add(GryoTypeReg.of(Path.class, 59, new GryoSerializers.PathSerializer()));
// skip 55
add(GryoTypeReg.of(B_O_Traverser.class, 75));
add(GryoTypeReg.of(O_Traverser.class, 76));
add(GryoTypeReg.of(B_LP_O_P_S_SE_SL_Traverser.class, 77));
add(GryoTypeReg.of(B_O_S_SE_SL_Traverser.class, 78));
add(GryoTypeReg.of(B_LP_O_S_SE_SL_Traverser.class, 87));
add(GryoTypeReg.of(O_OB_S_SE_SL_Traverser.class, 89));
add(GryoTypeReg.of(LP_O_OB_S_SE_SL_Traverser.class, 90));
add(GryoTypeReg.of(LP_O_OB_P_S_SE_SL_Traverser.class, 91));
add(GryoTypeReg.of(TraverserSet.class, 58));
add(GryoTypeReg.of(Tree.class, 61));
add(GryoTypeReg.of(HashSet.class, 62));
add(GryoTypeReg.of(BulkSet.class, 64));
add(GryoTypeReg.of(MutableMetrics.class, 69));
add(GryoTypeReg.of(ImmutableMetrics.class, 115));
add(GryoTypeReg.of(DefaultTraversalMetrics.class, 70));
add(GryoTypeReg.of(MapMemory.class, 73));
add(GryoTypeReg.of(MapReduce.NullObject.class, 74));
add(GryoTypeReg.of(AtomicLong.class, 79));
add(GryoTypeReg.of(Pair.class, 88, new PairSerializer()));
add(GryoTypeReg.of(TraversalExplanation.class, 106, new JavaSerializer()));
add(GryoTypeReg.of(Duration.class, 93, new JavaTimeSerializers.DurationSerializer()));
add(GryoTypeReg.of(Instant.class, 94, new JavaTimeSerializers.InstantSerializer()));
add(GryoTypeReg.of(LocalDate.class, 95, new JavaTimeSerializers.LocalDateSerializer()));
add(GryoTypeReg.of(LocalDateTime.class, 96, new JavaTimeSerializers.LocalDateTimeSerializer()));
add(GryoTypeReg.of(LocalTime.class, 97, new JavaTimeSerializers.LocalTimeSerializer()));
add(GryoTypeReg.of(MonthDay.class, 98, new JavaTimeSerializers.MonthDaySerializer()));
add(GryoTypeReg.of(OffsetDateTime.class, 99, new JavaTimeSerializers.OffsetDateTimeSerializer()));
add(GryoTypeReg.of(OffsetTime.class, 100, new JavaTimeSerializers.OffsetTimeSerializer()));
add(GryoTypeReg.of(Period.class, 101, new JavaTimeSerializers.PeriodSerializer()));
add(GryoTypeReg.of(Year.class, 102, new JavaTimeSerializers.YearSerializer()));
add(GryoTypeReg.of(YearMonth.class, 103, new JavaTimeSerializers.YearMonthSerializer()));
add(GryoTypeReg.of(ZonedDateTime.class, 104, new JavaTimeSerializers.ZonedDateTimeSerializer()));
add(GryoTypeReg.of(ZoneOffset.class, 105, new JavaTimeSerializers.ZoneOffsetSerializer()));
add(GryoTypeReg.of(Operator.class, 107));
add(GryoTypeReg.of(FoldStep.FoldBiOperator.class, 108));
add(GryoTypeReg.of(GroupCountStep.GroupCountBiOperator.class, 109));
add(GryoTypeReg.of(GroupStep.GroupBiOperator.class, 117, new JavaSerializer())); // because they contain traversals
add(GryoTypeReg.of(MeanGlobalStep.MeanGlobalBiOperator.class, 110));
add(GryoTypeReg.of(MeanGlobalStep.MeanNumber.class, 111));
add(GryoTypeReg.of(TreeStep.TreeBiOperator.class, 112));
add(GryoTypeReg.of(GroupStepV3d0.GroupBiOperatorV3d0.class, 113));
add(GryoTypeReg.of(RangeGlobalStep.RangeBiOperator.class, 114));
add(GryoTypeReg.of(OrderGlobalStep.OrderBiOperator.class, 118, new JavaSerializer())); // because they contain traversals
add(GryoTypeReg.of(ProfileStep.ProfileBiOperator.class, 119));
}};
private final List<IoRegistry> registries = new ArrayList<>();
/**
* Starts numbering classes for Gryo serialization at 65536 to leave room for future usage by TinkerPop.
*/
private final AtomicInteger currentSerializationId = new AtomicInteger(65536);
private boolean registrationRequired = true;
private boolean referenceTracking = true;
private Supplier<ClassResolver> classResolver = GryoClassResolver::new;
private Builder() {
// Validate the default registrations
// For justification of these default registration rules, see TinkerPopKryoRegistrator
for (TypeRegistration<?> tr : typeRegistrations) {
if (tr.hasSerializer() /* no serializer is acceptable */ &&
null == tr.getSerializerShim() /* a shim serializer is acceptable */ &&
!(tr.getShadedSerializer() instanceof JavaSerializer) /* shaded JavaSerializer is acceptable */) {
// everything else is invalid
final String msg = String.format("The default GryoMapper type registration %s is invalid. " +
"It must supply either an implementation of %s or %s, but supplies neither. " +
"This is probably a bug in GryoMapper's default serialization class registrations.", tr,
SerializerShim.class.getCanonicalName(), JavaSerializer.class.getCanonicalName());
throw new IllegalStateException(msg);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public Builder addRegistry(final IoRegistry registry) {
if (null == registry) throw new IllegalArgumentException("The registry cannot be null");
this.registries.add(registry);
return this;
}
/**
* Provides a custom Kryo {@code ClassResolver} to be supplied to a {@code Kryo} instance. If this value is
* not supplied then it will default to the {@link GryoClassResolver}. To ensure compatibility with Gryo it
* is highly recommended that objects passed to this method extend that class.
* <p/>
* If the {@code ClassResolver} implementation share state, then the {@link Supplier} should typically create
* new instances when requested, as the {@link Supplier} will be called for each {@link Kryo} instance created.
*/
public Builder classResolver(final Supplier<ClassResolver> classResolverSupplier) {
if (null == classResolverSupplier)
throw new IllegalArgumentException("The classResolverSupplier cannot be null");
this.classResolver = classResolverSupplier;
return this;
}
/**
* Register custom classes to serializes with gryo using default serialization. Note that calling this method
* for a class that is already registered will override that registration.
*/
public Builder addCustom(final Class... custom) {
if (custom != null && custom.length > 0) {
for (Class c : custom) {
addOrOverrideRegistration(c, id -> GryoTypeReg.of(c, id));
}
}
return this;
}
/**
* Register custom class to serialize with a custom serialization class. Note that calling this method for
* a class that is already registered will override that registration.
*/
public Builder addCustom(final Class clazz, final Serializer serializer) {
addOrOverrideRegistration(clazz, id -> GryoTypeReg.of(clazz, id, serializer));
return this;
}
/**
* Register custom class to serialize with a custom serialization shim.
*/
public Builder addCustom(final Class clazz, final SerializerShim serializer) {
addOrOverrideRegistration(clazz, id -> GryoTypeReg.of(clazz, id, serializer));
return this;
}
/**
* Register a custom class to serialize with a custom serializer as returned from a {@link Function}. Note
* that calling this method for a class that is already registered will override that registration.
*/
public Builder addCustom(final Class clazz, final Function<Kryo, Serializer> functionOfKryo) {
addOrOverrideRegistration(clazz, id -> GryoTypeReg.of(clazz, id, functionOfKryo));
return this;
}
/**
* When set to {@code true}, all classes serialized by the {@code Kryo} instances created from this
* {@link GryoMapper} must have their classes known up front and registered appropriately through this
* builder. By default this value is {@code true}. This approach is more efficient than setting the
* value to {@code false}.
*
* @param registrationRequired set to {@code true} if the classes should be registered up front or
* {@code false} otherwise
*/
public Builder registrationRequired(final boolean registrationRequired) {
this.registrationRequired = registrationRequired;
return this;
}
/**
* By default, each appearance of an object in the graph after the first is stored as an integer ordinal.
* This allows multiple references to the same object and cyclic graphs to be serialized. This has a small
* amount of overhead and can be disabled to save space if it is not needed.
*
* @param referenceTracking set to {@code true} to enable and {@code false} otherwise
*/
public Builder referenceTracking(final boolean referenceTracking) {
this.referenceTracking = referenceTracking;
return this;
}
/**
* Creates a {@code GryoMapper}.
*/
public GryoMapper create() {
// consult the registry if provided and inject registry entries as custom classes.
registries.forEach(registry -> {
final List<Pair<Class, Object>> serializers = registry.find(GryoIo.class);
serializers.forEach(p -> {
if (null == p.getValue1())
addCustom(p.getValue0());
else if (p.getValue1() instanceof Serializer)
addCustom(p.getValue0(), (Serializer) p.getValue1());
else if (p.getValue1() instanceof Function)
addCustom(p.getValue0(), (Function<Kryo, Serializer>) p.getValue1());
else
throw new IllegalStateException(String.format(
"Unexpected value provided by %s for serializable class %s - expected a parameter in [null, %s implementation or Function<%s, %s>], but received %s",
registry.getClass().getSimpleName(), p.getValue0().getClass().getCanonicalName(),
Serializer.class.getName(), Kryo.class.getSimpleName(),
Serializer.class.getSimpleName(), p.getValue1()));
});
});
return new GryoMapper(this);
}
private <T> void addOrOverrideRegistration(final Class<?> clazz,
final Function<Integer, TypeRegistration<T>> newRegistrationBuilder) {
final Iterator<TypeRegistration<?>> iter = typeRegistrations.iterator();
Integer registrationId = null;
while (iter.hasNext()) {
final TypeRegistration<?> existingRegistration = iter.next();
if (existingRegistration.getTargetClass().equals(clazz)) {
// when overridding a registration, use the old id
registrationId = existingRegistration.getId();
// remove the old registration (we install its override below)
iter.remove();
break;
}
}
if (null == registrationId) {
// when not overridding a registration, get an id from the counter
registrationId = currentSerializationId.getAndIncrement();
}
typeRegistrations.add(newRegistrationBuilder.apply(registrationId));
}
}
private static class GryoTypeReg<T> implements TypeRegistration<T> {
private final Class<T> clazz;
private final Serializer<T> shadedSerializer;
private final SerializerShim<T> serializerShim;
private final Function<Kryo, Serializer> functionOfShadedKryo;
private final int id;
private GryoTypeReg(final Class<T> clazz,
final Serializer<T> shadedSerializer,
final SerializerShim<T> serializerShim,
final Function<Kryo, Serializer> functionOfShadedKryo,
final int id) {
this.clazz = clazz;
this.shadedSerializer = shadedSerializer;
this.serializerShim = serializerShim;
this.functionOfShadedKryo = functionOfShadedKryo;
this.id = id;
int serializerCount = 0;
if (null != this.shadedSerializer)
serializerCount++;
if (null != this.serializerShim)
serializerCount++;
if (null != this.functionOfShadedKryo)
serializerCount++;
if (1 < serializerCount) {
final String msg = String.format(
"GryoTypeReg accepts at most one kind of serializer, but multiple " +
"serializers were supplied for class %s (id %s). " +
"Shaded serializer: %s. Shim serializer: %s. Shaded serializer function: %s.",
this.clazz.getCanonicalName(), id,
this.shadedSerializer, this.serializerShim, this.functionOfShadedKryo);
throw new IllegalArgumentException(msg);
}
}
private static <T> GryoTypeReg<T> of(final Class<T> clazz, final int id) {
return new GryoTypeReg<>(clazz, null, null, null, id);
}
private static <T> GryoTypeReg<T> of(final Class<T> clazz, final int id, final Serializer<T> shadedSerializer) {
return new GryoTypeReg<>(clazz, shadedSerializer, null, null, id);
}
private static <T> GryoTypeReg<T> of(final Class<T> clazz, final int id, final SerializerShim<T> serializerShim) {
return new GryoTypeReg<>(clazz, null, serializerShim, null, id);
}
private static <T> GryoTypeReg<T> of(final Class clazz, final int id, final Function<Kryo, Serializer> fct) {
return new GryoTypeReg<>(clazz, null, null, fct, id);
}
@Override
public Serializer<T> getShadedSerializer() {
return shadedSerializer;
}
@Override
public SerializerShim<T> getSerializerShim() {
return serializerShim;
}
@Override
public Function<Kryo, Serializer> getFunctionOfShadedKryo() {
return functionOfShadedKryo;
}
@Override
public Class<T> getTargetClass() {
return clazz;
}
@Override
public int getId() {
return id;
}
@Override
public Kryo registerWith(final Kryo kryo) {
if (null != functionOfShadedKryo)
kryo.register(clazz, functionOfShadedKryo.apply(kryo), id);
else if (null != shadedSerializer)
kryo.register(clazz, shadedSerializer, id);
else if (null != serializerShim)
kryo.register(clazz, new ShadedSerializerAdapter<>(serializerShim), id);
else {
kryo.register(clazz, kryo.getDefaultSerializer(clazz), id);
// Suprisingly, the preceding call is not equivalent to
// kryo.register(clazz, id);
}
return kryo;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("targetClass", clazz)
.append("id", id)
.append("shadedSerializer", shadedSerializer)
.append("serializerShim", serializerShim)
.append("functionOfShadedKryo", functionOfShadedKryo)
.toString();
}
}
}