| // Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details. |
| |
| // All rights reserved. Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| import 'package:built_collection/built_collection.dart'; |
| import 'package:built_value/serializer.dart'; |
| |
| /// Default implementation of [Serializers]. |
| class BuiltJsonSerializers implements Serializers { |
| final BuiltMap<Type, Serializer> _typeToSerializer; |
| |
| // Implementation note: wire name is what gets sent in the JSON, type name is |
| // the runtime type name. Type name is complicated for two reasons: |
| // |
| // 1. Built Value classes have two types, the abstract class and the |
| // generated implementation. |
| // |
| // 2. When compiled to javascript the runtime type names are obfuscated. |
| final BuiltMap<String, Serializer> _wireNameToSerializer; |
| final BuiltMap<String, Serializer> _typeNameToSerializer; |
| |
| final BuiltMap<FullType, Function> builderFactories; |
| |
| final BuiltList<SerializerPlugin> _plugins; |
| |
| BuiltJsonSerializers._(this._typeToSerializer, this._wireNameToSerializer, |
| this._typeNameToSerializer, this.builderFactories, this._plugins); |
| |
| @override |
| Iterable<Serializer> get serializers => _wireNameToSerializer.values; |
| |
| @override |
| T deserializeWith<T>(Serializer<T> serializer, Object serialized) { |
| return deserialize(serialized, |
| specifiedType: FullType(serializer.types.first)) as T; |
| } |
| |
| @override |
| Object serializeWith<T>(Serializer<T> serializer, T object) { |
| return serialize(object, specifiedType: FullType(serializer.types.first)); |
| } |
| |
| @override |
| Object serialize(Object object, |
| {FullType specifiedType = FullType.unspecified}) { |
| var transformedObject = object; |
| for (var plugin in _plugins) { |
| transformedObject = |
| plugin.beforeSerialize(transformedObject, specifiedType); |
| } |
| var result = _serialize(transformedObject, specifiedType); |
| for (var plugin in _plugins) { |
| result = plugin.afterSerialize(result, specifiedType); |
| } |
| return result; |
| } |
| |
| Object _serialize(Object object, FullType specifiedType) { |
| if (specifiedType.isUnspecified) { |
| final serializer = serializerForType(object.runtimeType); |
| if (serializer == null) { |
| throw StateError("No serializer for '${object.runtimeType}'."); |
| } |
| if (serializer is StructuredSerializer) { |
| final result = <Object>[serializer.wireName]; |
| return result..addAll(serializer.serialize(this, object)); |
| } else if (serializer is PrimitiveSerializer) { |
| return <Object>[ |
| serializer.wireName, |
| serializer.serialize(this, object) |
| ]; |
| } else { |
| throw StateError( |
| 'serializer must be StructuredSerializer or PrimitiveSerializer'); |
| } |
| } else { |
| final serializer = serializerForType(specifiedType.root); |
| if (serializer == null) { |
| // Might be an interface; try resolving using the runtime type. |
| return serialize(object); |
| } |
| if (serializer is StructuredSerializer) { |
| return serializer |
| .serialize(this, object, specifiedType: specifiedType) |
| .toList(); |
| } else if (serializer is PrimitiveSerializer) { |
| return serializer.serialize(this, object, specifiedType: specifiedType); |
| } else { |
| throw StateError( |
| 'serializer must be StructuredSerializer or PrimitiveSerializer'); |
| } |
| } |
| } |
| |
| @override |
| Object deserialize(Object object, |
| {FullType specifiedType = FullType.unspecified}) { |
| var transformedObject = object; |
| for (var plugin in _plugins) { |
| transformedObject = |
| plugin.beforeDeserialize(transformedObject, specifiedType); |
| } |
| var result = _deserialize(object, transformedObject, specifiedType); |
| for (var plugin in _plugins) { |
| result = plugin.afterDeserialize(result, specifiedType); |
| } |
| return result; |
| } |
| |
| Object _deserialize( |
| Object objectBeforePlugins, Object object, FullType specifiedType) { |
| if (specifiedType.isUnspecified) { |
| final String wireName = (object as List).first as String; |
| |
| final serializer = serializerForWireName(wireName); |
| if (serializer == null) { |
| throw StateError("No serializer for '$wireName'."); |
| } |
| |
| if (serializer is StructuredSerializer) { |
| try { |
| return serializer.deserialize(this, (object as List).sublist(1)); |
| } on Error catch (error) { |
| throw DeserializationError(object, specifiedType, error); |
| } |
| } else if (serializer is PrimitiveSerializer) { |
| try { |
| return serializer.deserialize(this, (object as List)[1]); |
| } on Error catch (error) { |
| throw DeserializationError(object, specifiedType, error); |
| } |
| } else { |
| throw StateError( |
| 'serializer must be StructuredSerializer or PrimitiveSerializer'); |
| } |
| } else { |
| final serializer = serializerForType(specifiedType.root); |
| if (serializer == null) { |
| if (object is List && object.first is String) { |
| // Might be an interface; try resolving using the type on the wire. |
| return deserialize(objectBeforePlugins); |
| } else { |
| throw StateError("No serializer for '${specifiedType.root}'."); |
| } |
| } |
| |
| if (serializer is StructuredSerializer) { |
| try { |
| return serializer.deserialize(this, object as Iterable, |
| specifiedType: specifiedType); |
| } on Error catch (error) { |
| throw DeserializationError(object, specifiedType, error); |
| } |
| } else if (serializer is PrimitiveSerializer) { |
| try { |
| return serializer.deserialize(this, object, |
| specifiedType: specifiedType); |
| } on Error catch (error) { |
| throw DeserializationError(object, specifiedType, error); |
| } |
| } else { |
| throw StateError( |
| 'serializer must be StructuredSerializer or PrimitiveSerializer'); |
| } |
| } |
| } |
| |
| @override |
| Serializer serializerForType(Type type) => |
| _typeToSerializer[type] ?? _typeNameToSerializer[_getRawName(type)]; |
| |
| @override |
| Serializer serializerForWireName(String wireName) => |
| _wireNameToSerializer[wireName]; |
| |
| @override |
| Object newBuilder(FullType fullType) { |
| var builderFactory = builderFactories[fullType]; |
| if (builderFactory == null) _throwMissingBuilderFactory(fullType); |
| return builderFactory(); |
| } |
| |
| @override |
| void expectBuilder(FullType fullType) { |
| if (!hasBuilder(fullType)) _throwMissingBuilderFactory(fullType); |
| } |
| |
| void _throwMissingBuilderFactory(FullType fullType) { |
| throw StateError('No builder factory for $fullType. ' |
| 'Fix by adding one, see SerializersBuilder.addBuilderFactory.'); |
| } |
| |
| @override |
| bool hasBuilder(FullType fullType) { |
| return builderFactories.containsKey(fullType); |
| } |
| |
| @override |
| SerializersBuilder toBuilder() { |
| return BuiltJsonSerializersBuilder._( |
| _typeToSerializer.toBuilder(), |
| _wireNameToSerializer.toBuilder(), |
| _typeNameToSerializer.toBuilder(), |
| builderFactories.toBuilder(), |
| _plugins.toBuilder()); |
| } |
| } |
| |
| /// Default implementation of [SerializersBuilder]. |
| class BuiltJsonSerializersBuilder implements SerializersBuilder { |
| final MapBuilder<Type, Serializer> _typeToSerializer; |
| final MapBuilder<String, Serializer> _wireNameToSerializer; |
| final MapBuilder<String, Serializer> _typeNameToSerializer; |
| |
| final MapBuilder<FullType, Function> _builderFactories; |
| |
| final ListBuilder<SerializerPlugin> _plugins; |
| |
| factory BuiltJsonSerializersBuilder() => BuiltJsonSerializersBuilder._( |
| MapBuilder<Type, Serializer>(), |
| MapBuilder<String, Serializer>(), |
| MapBuilder<String, Serializer>(), |
| MapBuilder<FullType, Function>(), |
| ListBuilder<SerializerPlugin>()); |
| |
| BuiltJsonSerializersBuilder._( |
| this._typeToSerializer, |
| this._wireNameToSerializer, |
| this._typeNameToSerializer, |
| this._builderFactories, |
| this._plugins); |
| |
| @override |
| void add(Serializer serializer) { |
| if (serializer is! StructuredSerializer && |
| serializer is! PrimitiveSerializer) { |
| throw ArgumentError( |
| 'serializer must be StructuredSerializer or PrimitiveSerializer'); |
| } |
| |
| _wireNameToSerializer[serializer.wireName] = serializer; |
| for (var type in serializer.types) { |
| _typeToSerializer[type] = serializer; |
| _typeNameToSerializer[_getRawName(type)] = serializer; |
| } |
| } |
| |
| @override |
| void addAll(Iterable<Serializer> serializers) { |
| serializers.forEach(add); |
| } |
| |
| @override |
| void addBuilderFactory(FullType types, Function function) { |
| _builderFactories[types] = function; |
| } |
| |
| @override |
| void merge(Serializers serializers) { |
| addAll(serializers.serializers); |
| _builderFactories.addAll(serializers.builderFactories.asMap()); |
| } |
| |
| @override |
| void mergeAll(Iterable<Serializers> serializersIterable) { |
| for (var serializers in serializersIterable) { |
| merge(serializers); |
| } |
| } |
| |
| @override |
| void addPlugin(SerializerPlugin plugin) { |
| _plugins.add(plugin); |
| } |
| |
| @override |
| Serializers build() { |
| return BuiltJsonSerializers._( |
| _typeToSerializer.build(), |
| _wireNameToSerializer.build(), |
| _typeNameToSerializer.build(), |
| _builderFactories.build(), |
| _plugins.build()); |
| } |
| } |
| |
| String _getRawName(Type type) { |
| var name = type.toString(); |
| var genericsStart = name.indexOf('<'); |
| return genericsStart == -1 ? name : name.substring(0, genericsStart); |
| } |