| #region License |
| |
| /* |
| * 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. |
| */ |
| |
| #endregion |
| |
| using System; |
| using System.Collections.Generic; |
| using System.Diagnostics.CodeAnalysis; |
| using System.IO; |
| using System.Linq; |
| using System.Numerics; |
| using Gremlin.Net.Process.Traversal; |
| using Gremlin.Net.Process.Traversal.Strategy; |
| using Gremlin.Net.Structure.IO.GraphBinary.Types; |
| |
| namespace Gremlin.Net.Structure.IO.GraphBinary |
| { |
| /// <summary> |
| /// Provides GraphBinary serializers for different types. |
| /// </summary> |
| public class TypeSerializerRegistry |
| { |
| private readonly Dictionary<Type, ITypeSerializer> _serializerByType = |
| new Dictionary<Type, ITypeSerializer> |
| { |
| {typeof(int), SingleTypeSerializers.IntSerializer}, |
| {typeof(long), SingleTypeSerializers.LongSerializer}, |
| {typeof(string), new StringSerializer()}, |
| {typeof(DateTimeOffset), DateTimeOffsetSerializer.DateSerializer}, |
| {typeof(GremlinType), new ClassSerializer()}, |
| {typeof(Type), new TypeSerializer()}, |
| {typeof(double), SingleTypeSerializers.DoubleSerializer}, |
| {typeof(float), SingleTypeSerializers.FloatSerializer}, |
| {typeof(Guid), new UuidSerializer()}, |
| {typeof(Edge), new EdgeSerializer()}, |
| {typeof(Path), new PathSerializer()}, |
| {typeof(Property), new PropertySerializer()}, |
| {typeof(Vertex), new VertexSerializer()}, |
| {typeof(VertexProperty), new VertexPropertySerializer()}, |
| {typeof(Barrier), EnumSerializers.BarrierSerializer}, |
| {typeof(Binding), new BindingSerializer()}, |
| {typeof(Bytecode), new BytecodeSerializer()}, |
| {typeof(ITraversal), new TraversalSerializer()}, |
| {typeof(Cardinality), EnumSerializers.CardinalitySerializer}, |
| {typeof(Column), EnumSerializers.ColumnSerializer}, |
| {typeof(Direction), EnumSerializers.DirectionSerializer}, |
| {typeof(Merge), EnumSerializers.MergeSerializer}, |
| {typeof(Operator), EnumSerializers.OperatorSerializer}, |
| {typeof(Order), EnumSerializers.OrderSerializer}, |
| {typeof(Pick), EnumSerializers.PickSerializer}, |
| {typeof(Pop), EnumSerializers.PopSerializer}, |
| {typeof(ILambda), new LambdaSerializer()}, |
| {typeof(P), new PSerializer(DataType.P)}, |
| {typeof(Scope), EnumSerializers.ScopeSerializer}, |
| {typeof(T), EnumSerializers.TSerializer}, |
| {typeof(Traverser), new TraverserSerializer()}, |
| {typeof(decimal), new BigDecimalSerializer()}, |
| {typeof(BigInteger), new BigIntegerSerializer()}, |
| {typeof(byte), SingleTypeSerializers.ByteSerializer}, |
| {typeof(byte[]), new ByteBufferSerializer()}, |
| {typeof(short), SingleTypeSerializers.ShortSerializer}, |
| {typeof(bool), SingleTypeSerializers.BooleanSerializer}, |
| {typeof(TextP), new PSerializer(DataType.TextP)}, |
| {typeof(AbstractTraversalStrategy), new TraversalStrategySerializer()}, |
| {typeof(char), new CharSerializer()}, |
| {typeof(TimeSpan), new DurationSerializer()}, |
| }; |
| |
| private readonly Dictionary<DataType, ITypeSerializer> _serializerByDataType = |
| new Dictionary<DataType, ITypeSerializer> |
| { |
| {DataType.Int, SingleTypeSerializers.IntSerializer}, |
| {DataType.Long, SingleTypeSerializers.LongSerializer}, |
| {DataType.String, new StringSerializer()}, |
| {DataType.Date, DateTimeOffsetSerializer.DateSerializer}, |
| {DataType.Timestamp, DateTimeOffsetSerializer.TimestampSerializer}, |
| {DataType.Class, new ClassSerializer()}, |
| {DataType.Double, SingleTypeSerializers.DoubleSerializer}, |
| {DataType.Float, SingleTypeSerializers.FloatSerializer}, |
| {DataType.List, new ListSerializer<object>()}, |
| {DataType.Map, new MapSerializer<object, object>()}, |
| {DataType.Set, new SetSerializer<HashSet<object?>, object>()}, |
| {DataType.Uuid, new UuidSerializer()}, |
| {DataType.Edge, new EdgeSerializer()}, |
| {DataType.Path, new PathSerializer()}, |
| {DataType.Property, new PropertySerializer()}, |
| {DataType.Vertex, new VertexSerializer()}, |
| {DataType.VertexProperty, new VertexPropertySerializer()}, |
| {DataType.Barrier, EnumSerializers.BarrierSerializer}, |
| {DataType.Binding, new BindingSerializer()}, |
| {DataType.Bytecode, new BytecodeSerializer()}, |
| {DataType.Cardinality, EnumSerializers.CardinalitySerializer}, |
| {DataType.Column, EnumSerializers.ColumnSerializer}, |
| {DataType.Direction, EnumSerializers.DirectionSerializer}, |
| {DataType.Merge, EnumSerializers.MergeSerializer}, |
| {DataType.Operator, EnumSerializers.OperatorSerializer}, |
| {DataType.Order, EnumSerializers.OrderSerializer}, |
| {DataType.Pick, EnumSerializers.PickSerializer}, |
| {DataType.Pop, EnumSerializers.PopSerializer}, |
| {DataType.Lambda, new LambdaSerializer()}, |
| {DataType.P, new PSerializer(DataType.P)}, |
| {DataType.Scope, EnumSerializers.ScopeSerializer}, |
| {DataType.T, EnumSerializers.TSerializer}, |
| {DataType.Traverser, new TraverserSerializer()}, |
| {DataType.BigDecimal, new BigDecimalSerializer()}, |
| {DataType.BigInteger, new BigIntegerSerializer()}, |
| {DataType.Byte, SingleTypeSerializers.ByteSerializer}, |
| {DataType.ByteBuffer, new ByteBufferSerializer()}, |
| {DataType.Short, SingleTypeSerializers.ShortSerializer}, |
| {DataType.Boolean, SingleTypeSerializers.BooleanSerializer}, |
| {DataType.TextP, new PSerializer(DataType.TextP)}, |
| {DataType.TraversalStrategy, new TraversalStrategySerializer()}, |
| {DataType.BulkSet, new BulkSetSerializer<List<object>>()}, |
| {DataType.Char, new CharSerializer()}, |
| {DataType.Duration, new DurationSerializer()}, |
| }; |
| |
| private readonly Dictionary<string, CustomTypeSerializer> _serializerByCustomTypeName = |
| new Dictionary<string, CustomTypeSerializer>(); |
| |
| private TypeSerializerRegistry(List<CustomTypeRegistryEntry> customTypeEntries) |
| { |
| foreach (var entry in customTypeEntries) |
| { |
| _serializerByType[entry.Type] = entry.TypeSerializer; |
| _serializerByCustomTypeName[entry.CustomTypeName] = entry.TypeSerializer; |
| } |
| } |
| |
| /// <summary> |
| /// Provides a default <see cref="TypeSerializerRegistry"/> instance. |
| /// </summary> |
| public static readonly TypeSerializerRegistry Instance = Build().Create(); |
| |
| /// <summary> |
| /// Builds a <see cref="TypeSerializerRegistry"/>. |
| /// </summary> |
| public static Builder Build() => new Builder(); |
| |
| /// <summary> |
| /// Gets a serializer for the given type of the value to be serialized. |
| /// </summary> |
| /// <param name="valueType">Type of the value to be serialized.</param> |
| /// <returns>A serializer for the provided type.</returns> |
| /// <exception cref="InvalidOperationException">Thrown when no serializer can be found for the type.</exception> |
| public ITypeSerializer GetSerializerFor(Type valueType) |
| { |
| if (_serializerByType.ContainsKey(valueType)) |
| { |
| return _serializerByType[valueType]; |
| } |
| |
| if (IsDictionaryType(valueType, out var dictKeyType, out var dictValueType)) |
| { |
| var serializerType = typeof(MapSerializer<,>).MakeGenericType(dictKeyType, dictValueType); |
| var serializer = (ITypeSerializer?) Activator.CreateInstance(serializerType); |
| _serializerByType[valueType] = serializer ?? |
| throw new IOException( |
| $"Cannot create a serializer for the dictionary type {valueType}."); |
| return serializer; |
| } |
| |
| if (IsSetType(valueType)) |
| { |
| var memberType = valueType.GetGenericArguments()[0]; |
| var serializerType = typeof(SetSerializer<,>).MakeGenericType(valueType, memberType); |
| var serializer = (ITypeSerializer?) Activator.CreateInstance(serializerType); |
| _serializerByType[valueType] = serializer ?? |
| throw new IOException( |
| $"Cannot create a serializer for the set type {valueType}."); |
| return serializer; |
| } |
| |
| if (valueType.IsArray) |
| { |
| var memberType = valueType.GetElementType(); |
| var serializerType = typeof(ArraySerializer<>).MakeGenericType(memberType!); |
| var serializer = (ITypeSerializer?) Activator.CreateInstance(serializerType); |
| _serializerByType[valueType] = serializer ?? |
| throw new IOException( |
| $"Cannot create a serializer for the array type {valueType}."); |
| return serializer; |
| } |
| |
| if (IsListType(valueType)) |
| { |
| var memberType = valueType.GetGenericArguments()[0]; |
| var serializerType = typeof(ListSerializer<>).MakeGenericType(memberType); |
| var serializer = (ITypeSerializer?) Activator.CreateInstance(serializerType); |
| _serializerByType[valueType] = serializer ?? |
| throw new IOException( |
| $"Cannot create a serializer for the list type {valueType}."); |
| return serializer; |
| } |
| |
| foreach (var supportedType in _serializerByType.Keys) |
| { |
| if (supportedType.IsAssignableFrom(valueType)) |
| { |
| var serializer = _serializerByType[supportedType]; |
| _serializerByType[valueType] = serializer; |
| return serializer; |
| } |
| } |
| |
| throw new InvalidOperationException($"No serializer found for type '{valueType}'."); |
| } |
| |
| private static bool IsDictionaryType(Type type, [NotNullWhen(returnValue: true)] out Type? keyType, |
| [NotNullWhen(returnValue: true)] out Type? valueType) |
| { |
| var maybeInterfaceType = type |
| .GetInterfaces() |
| .FirstOrDefault(implementedInterfaceType => implementedInterfaceType.IsConstructedGenericType && implementedInterfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)); |
| |
| if (maybeInterfaceType is { } interfaceType) |
| { |
| keyType = interfaceType.GetGenericArguments()[0]; |
| valueType = interfaceType.GetGenericArguments()[1]; |
| |
| return true; |
| } |
| |
| keyType = null; |
| valueType = null; |
| |
| return false; |
| } |
| |
| private static bool IsSetType(Type type) |
| { |
| return type.GetInterfaces().Any(implementedInterface => implementedInterface.IsConstructedGenericType && |
| implementedInterface.GetGenericTypeDefinition() == |
| typeof(ISet<>)); |
| } |
| |
| private static bool IsListType(Type type) |
| { |
| return type.GetInterfaces().Any(implementedInterface => implementedInterface.IsConstructedGenericType && |
| implementedInterface.GetGenericTypeDefinition() == |
| typeof(IList<>)); |
| } |
| |
| /// <summary> |
| /// Gets a serializer for the given GraphBinary type. |
| /// </summary> |
| /// <param name="type">The GraphBinary type that should be (de)serialized.</param> |
| /// <returns>A serializer for the provided GraphBinary type.</returns> |
| public ITypeSerializer GetSerializerFor(DataType type) |
| { |
| if (type == DataType.Custom) |
| throw new ArgumentException("Custom type serializers can not be retrieved using this method"); |
| |
| return _serializerByDataType[type]; |
| } |
| |
| /// <summary> |
| /// Gets a serializer for the given custom type name. |
| /// </summary> |
| /// <param name="typeName">The custom type name.</param> |
| /// <returns>A serializer for the provided custom type name.</returns> |
| public CustomTypeSerializer GetSerializerForCustomType(string typeName) |
| { |
| return _serializerByCustomTypeName.TryGetValue(typeName, out var serializer) |
| ? serializer |
| : throw new InvalidOperationException($"No serializer found for type '{typeName}'."); |
| } |
| |
| /// <summary> |
| /// The builder of a <see cref="TypeSerializerRegistry"/>. |
| /// </summary> |
| public class Builder |
| { |
| private readonly List<CustomTypeRegistryEntry> _list = new List<CustomTypeRegistryEntry>(); |
| |
| /// <summary> |
| /// Creates the <see cref="TypeSerializerRegistry"/>. |
| /// </summary> |
| public TypeSerializerRegistry Create() => new TypeSerializerRegistry(_list); |
| |
| /// <summary> |
| /// Adds a serializer for a custom type. |
| /// </summary> |
| /// <param name="type">The custom type supported by the serializer.</param> |
| /// <param name="serializer">The serializer for the custom type.</param> |
| public Builder AddCustomType(Type type, CustomTypeSerializer serializer) |
| { |
| if (serializer == null) throw new ArgumentNullException(nameof(serializer)); |
| if (serializer.TypeName == null) |
| throw new ArgumentException("serializer custom type name can not be null"); |
| |
| _list.Add(new CustomTypeRegistryEntry(type, serializer)); |
| return this; |
| } |
| } |
| |
| private class CustomTypeRegistryEntry |
| { |
| public Type Type { get; } |
| public CustomTypeSerializer TypeSerializer { get; } |
| |
| public CustomTypeRegistryEntry(Type type, CustomTypeSerializer typeSerializer) |
| { |
| Type = type; |
| TypeSerializer = typeSerializer; |
| } |
| public string CustomTypeName => TypeSerializer.TypeName; |
| } |
| } |
| } |