blob: 39aeaaf4924f91d38a213ce813f2f9665a5b70bf [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.binary;
import org.apache.tinkerpop.gremlin.structure.io.binary.types.*;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics;
import org.apache.tinkerpop.gremlin.structure.Column;
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.util.function.Lambda;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.sql.Timestamp;
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.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
public class TypeSerializerRegistry {
public static Builder build() {
return new Builder();
}
private static final RegistryEntry[] defaultEntries = new RegistryEntry[] {
new RegistryEntry<>(Integer.class, SingleTypeSerializer.IntSerializer),
new RegistryEntry<>(Long.class, SingleTypeSerializer.LongSerializer),
new RegistryEntry<>(String.class, new StringSerializer()),
new RegistryEntry<>(Date.class, DateSerializer.DateSerializer),
new RegistryEntry<>(Timestamp.class, DateSerializer.TimestampSerializer),
new RegistryEntry<>(Class.class, new ClassSerializer()),
new RegistryEntry<>(Double.class, SingleTypeSerializer.DoubleSerializer),
new RegistryEntry<>(Float.class, SingleTypeSerializer.FloatSerializer),
new RegistryEntry<>(List.class, new ListSerializer()),
new RegistryEntry<>(Map.class, new MapSerializer()),
new RegistryEntry<>(Set.class, new SetSerializer()),
new RegistryEntry<>(UUID.class, new UUIDSerializer()),
new RegistryEntry<>(Edge.class, new EdgeSerializer()),
new RegistryEntry<>(Path.class, new PathSerializer()),
new RegistryEntry<>(VertexProperty.class, new VertexPropertySerializer()), // needs to register before the less specific Property
new RegistryEntry<>(Property.class, new PropertySerializer()),
new RegistryEntry<>(Graph.class, new GraphSerializer()),
new RegistryEntry<>(Vertex.class, new VertexSerializer()),
new RegistryEntry<>(SackFunctions.Barrier.class, EnumSerializer.BarrierSerializer),
new RegistryEntry<>(Bytecode.Binding.class, new BindingSerializer()),
new RegistryEntry<>(Bytecode.class, new ByteCodeSerializer()),
new RegistryEntry<>(VertexProperty.Cardinality.class, EnumSerializer.CardinalitySerializer),
new RegistryEntry<>(Column.class, EnumSerializer.ColumnSerializer),
new RegistryEntry<>(Direction.class, EnumSerializer.DirectionSerializer),
new RegistryEntry<>(Operator.class, EnumSerializer.OperatorSerializer),
new RegistryEntry<>(Order.class, EnumSerializer.OrderSerializer),
new RegistryEntry<>(TraversalOptionParent.Pick.class, EnumSerializer.PickSerializer),
new RegistryEntry<>(Pop.class, EnumSerializer.PopSerializer),
new RegistryEntry<>(Lambda.class, new LambdaSerializer()),
new RegistryEntry<>(P.class, new PSerializer<>(DataType.P, P.class)),
new RegistryEntry<>(AndP.class, new PSerializer<>(DataType.P, AndP.class)),
new RegistryEntry<>(OrP.class, new PSerializer<>(DataType.P, OrP.class)),
new RegistryEntry<>(TextP.class, new PSerializer<>(DataType.TEXTP, TextP.class)),
new RegistryEntry<>(Scope.class, EnumSerializer.ScopeSerializer),
new RegistryEntry<>(T.class, EnumSerializer.TSerializer),
new RegistryEntry<>(Traverser.class, new TraverserSerializer()),
new RegistryEntry<>(BigDecimal.class, new BigDecimalSerializer()),
new RegistryEntry<>(BigInteger.class, new BigIntegerSerializer()),
new RegistryEntry<>(Byte.class, SingleTypeSerializer.ByteSerializer),
new RegistryEntry<>(ByteBuffer.class, new ByteBufferSerializer()),
new RegistryEntry<>(Short.class, SingleTypeSerializer.ShortSerializer),
new RegistryEntry<>(Boolean.class, SingleTypeSerializer.BooleanSerializer),
new RegistryEntry<>(TraversalStrategy.class, new TraversalStrategySerializer()),
new RegistryEntry<>(BulkSet.class, new BulkSetSerializer()),
new RegistryEntry<>(Tree.class, new TreeSerializer()),
new RegistryEntry<>(Metrics.class, new MetricsSerializer()),
new RegistryEntry<>(TraversalMetrics.class, new TraversalMetricsSerializer()),
// TransformSerializer implementations
new RegistryEntry<>(Map.Entry.class, new MapEntrySerializer()),
new RegistryEntry<>(TraversalExplanation.class, new TraversalExplanationSerializer()),
new RegistryEntry<>(Character.class, new CharSerializer()),
new RegistryEntry<>(Duration.class, new DurationSerializer()),
new RegistryEntry<>(InetAddress.class, new InetAddressSerializer()),
new RegistryEntry<>(Inet4Address.class, new InetAddressSerializer<>()),
new RegistryEntry<>(Inet6Address.class, new InetAddressSerializer<>()),
new RegistryEntry<>(Instant.class, new InstantSerializer()),
new RegistryEntry<>(LocalDate.class, new LocalDateSerializer()),
new RegistryEntry<>(LocalTime.class, new LocalTimeSerializer()),
new RegistryEntry<>(LocalDateTime.class, new LocalDateTimeSerializer()),
new RegistryEntry<>(MonthDay.class, new MonthDaySerializer()),
new RegistryEntry<>(OffsetDateTime.class, new OffsetDateTimeSerializer()),
new RegistryEntry<>(OffsetTime.class, new OffsetTimeSerializer()),
new RegistryEntry<>(Period.class, new PeriodSerializer()),
new RegistryEntry<>(Year.class, SingleTypeSerializer.YearSerializer),
new RegistryEntry<>(YearMonth.class, new YearMonthSerializer()),
new RegistryEntry<>(ZonedDateTime.class, new ZonedDateTimeSerializer()),
new RegistryEntry<>(ZoneOffset.class, new ZoneOffsetSerializer())
};
public static final TypeSerializerRegistry INSTANCE = build().create();
public static class Builder {
private final List<RegistryEntry> list = new LinkedList<>();
private Function<Class<?>, TypeSerializer<?>> fallbackResolver;
/**
* Adds a serializer for a built-in type.
* <p>
* Note that when providing a serializer for an Interface ({@link VertexProperty}, {@link Property},
* {@link Vertex}, ...), the serializer resolution from the registry will be defined by the order that
* it was provided. In this case, you should provide the type serializer starting from most specific to
* less specific or to put it in other words, start from derived types and then parent types, e.g.,
* {@link VertexProperty} before {@link Property}.
* </p>
*/
public <DT> Builder add(final Class<DT> type, final TypeSerializer<DT> serializer) {
if (serializer.getDataType() == DataType.CUSTOM) {
throw new IllegalArgumentException("DataType can not be CUSTOM, use addCustomType() method instead");
}
if (serializer.getDataType() == DataType.UNSPECIFIED_NULL) {
throw new IllegalArgumentException("Adding a serializer for a UNSPECIFIED_NULL is not permitted");
}
if (serializer instanceof CustomTypeSerializer) {
throw new IllegalArgumentException(
"CustomTypeSerializer implementations are reserved for customtypes");
}
list.add(new RegistryEntry<>(type, serializer));
return this;
}
/**
* Adds a serializer for a custom type.
*/
public <DT> Builder addCustomType(final Class<DT> type, final CustomTypeSerializer<DT> serializer) {
if (serializer == null) {
throw new NullPointerException("serializer can not be null");
}
if (serializer.getDataType() != DataType.CUSTOM) {
throw new IllegalArgumentException("Custom serializer must use CUSTOM data type");
}
if (serializer.getTypeName() == null) {
throw new NullPointerException("serializer custom type name can not be null");
}
list.add(new RegistryEntry<>(type, serializer));
return this;
}
/**
* Provides a way to resolve the type serializer to use when there isn't any direct match.
*/
public Builder withFallbackResolver(Function<Class<?>, TypeSerializer<?>> fallbackResolver) {
this.fallbackResolver = fallbackResolver;
return this;
}
/**
* Creates a new {@link TypeSerializerRegistry} instance based on the serializers added.
*/
public TypeSerializerRegistry create() {
return new TypeSerializerRegistry(list, fallbackResolver);
}
}
private static class RegistryEntry<DT> {
private final Class<DT> type;
private final TypeSerializer<DT> typeSerializer;
private RegistryEntry(Class<DT> type, TypeSerializer<DT> typeSerializer) {
this.type = type;
this.typeSerializer = typeSerializer;
}
public Class<DT> getType() {
return type;
}
public DataType getDataType() {
return typeSerializer.getDataType();
}
public String getCustomTypeName() {
if (getDataType() != DataType.CUSTOM) {
return null;
}
final CustomTypeSerializer customTypeSerializer = (CustomTypeSerializer) typeSerializer;
return customTypeSerializer.getTypeName();
}
public TypeSerializer<DT> getTypeSerializer() {
return typeSerializer;
}
}
private final Map<Class<?>, TypeSerializer<?>> serializers = new HashMap<>();
private final Map<Class<?>, TypeSerializer<?>> serializersByInterface = new LinkedHashMap<>();
private final Map<DataType, TypeSerializer<?>> serializersByDataType = new HashMap<>();
private final Map<String, CustomTypeSerializer> serializersByCustomTypeName = new HashMap<>();
private Function<Class<?>, TypeSerializer<?>> fallbackResolver;
/**
* Stores serializers by class, where the class resolution involved a lookup or special conditions.
* This is used for interface implementations and enums.
*/
private final ConcurrentHashMap<Class<?>, TypeSerializer<?>> serializersByImplementation = new ConcurrentHashMap<>();
private TypeSerializerRegistry(final Collection<RegistryEntry> entries,
final Function<Class<?>, TypeSerializer<?>> fallbackResolver) {
final Set<Class> providedTypes = new HashSet<>(entries.size());
// Include user-provided entries first
for (RegistryEntry entry : entries) {
put(entry);
providedTypes.add(entry.type);
}
// Followed by the defaults
Arrays.stream(defaultEntries).filter(e -> !providedTypes.contains(e.type)).forEach(this::put);
this.fallbackResolver = fallbackResolver;
}
private void put(final RegistryEntry entry) {
final Class type = entry.getType();
final TypeSerializer serializer = entry.getTypeSerializer();
if (type == null) {
throw new NullPointerException("Type can not be null");
}
if (serializer == null) {
throw new NullPointerException("Serializer instance can not be null");
}
if (serializer.getDataType() == null && !(serializer instanceof TransformSerializer)) {
throw new NullPointerException("Serializer data type can not be null");
}
if (!type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
// Direct class match
serializers.put(type, serializer);
} else {
// Interface or abstract class can be assigned by provided type
serializersByInterface.put(type, serializer);
}
if (serializer.getDataType() == DataType.CUSTOM) {
serializersByCustomTypeName.put(entry.getCustomTypeName(), (CustomTypeSerializer) serializer);
} else if (serializer.getDataType() != null) {
serializersByDataType.put(serializer.getDataType(), serializer);
}
}
public <DT> TypeSerializer<DT> getSerializer(final Class<DT> type) throws IOException {
TypeSerializer<?> serializer = serializers.get(type);
if (null == serializer) {
// Try to obtain the serializer by the type interface implementation or superclass,
// when previously accessed.
serializer = serializersByImplementation.get(type);
}
if (serializer != null) {
return (TypeSerializer) serializer;
}
// Use different lookup techniques and cache the lookup result when successful
if (Enum.class.isAssignableFrom(type)) {
// maybe it's a enum - enums with bodies are weird in java, they are subclasses of themselves, so
// Columns.values will be of type Column$2.
serializer = serializers.get(type.getSuperclass());
}
if (null == serializer) {
// Lookup by interface
for (Map.Entry<Class<?>, TypeSerializer<?>> entry : serializersByInterface.entrySet()) {
if (entry.getKey().isAssignableFrom(type)) {
serializer = entry.getValue();
break;
}
}
}
if (null == serializer && fallbackResolver != null) {
serializer = fallbackResolver.apply(type);
}
validateInstance(serializer, type.getTypeName());
// Store the lookup match to avoid looking it up in the future
serializersByImplementation.put(type, serializer);
return (TypeSerializer<DT>) serializer;
}
public <DT> TypeSerializer<DT> getSerializer(final DataType dataType) throws IOException {
if (dataType == DataType.CUSTOM) {
throw new IllegalArgumentException("Custom type serializers can not be retrieved using this method");
}
return validateInstance(serializersByDataType.get(dataType), dataType.toString());
}
/**
* Gets the serializer for a given custom type name.
*/
public <DT> CustomTypeSerializer<DT> getSerializerForCustomType(final String name) throws IOException {
final CustomTypeSerializer serializer = serializersByCustomTypeName.get(name);
if (serializer == null) {
throw new IOException(String.format("Serializer for custom type '%s' not found", name));
}
return serializer;
}
private static TypeSerializer validateInstance(final TypeSerializer serializer, final String typeName) throws IOException {
if (serializer == null) {
throw new IOException(String.format("Serializer for type %s not found", typeName));
}
return serializer;
}
}