diff --git a/.gitignore b/.gitignore index f2e22cc..fd88911 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ packages/ # Include when developing application packages. pubspec.lock +*.packages diff --git a/.packages b/.packages new file mode 100644 index 0000000..1bc3a07 --- /dev/null +++ b/.packages @@ -0,0 +1,43 @@ +# Generated by pub on 2015-12-03 09:20:10.458. +analyzer:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/analyzer-0.26.3/lib/ +args:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/args-0.13.2/lib/ +async:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/async-1.4.0/lib/ +barback:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/barback-0.15.2+7/lib/ +charcode:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/charcode-1.1.0/lib/ +cli_util:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/cli_util-0.0.1+2/lib/ +code_transformers:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/code_transformers-0.2.9+4/lib/ +collection:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/collection-1.2.0/lib/ +convert:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/convert-1.0.1/lib/ +crypto:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/crypto-0.9.1/lib/ +csslib:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/csslib-0.12.2/lib/ +dart_style:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/dart_style-0.2.0/lib/ +glob:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/glob-1.0.5/lib/ +html:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/html-0.12.2/lib/ +http_multi_server:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/http_multi_server-1.3.2/lib/ +http_parser:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/http_parser-1.1.0/lib/ +logging:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/logging-0.11.2/lib/ +matcher:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/matcher-0.12.0+1/lib/ +mime:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/mime-0.9.3/lib/ +package_config:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/package_config-0.1.3/lib/ +path:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/path-1.3.9/lib/ +plugin:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/plugin-0.1.0/lib/ +pool:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/pool-1.2.1/lib/ +pub_semver:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/pub_semver-1.2.3/lib/ +reflective:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/reflective-0.0.23/lib/ +shelf:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/shelf-0.6.4+2/lib/ +shelf_static:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/shelf_static-0.2.3+1/lib/ +shelf_web_socket:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/shelf_web_socket-0.0.1+4/lib/ +source_map_stack_trace:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/source_map_stack_trace-1.0.4/lib/ +source_maps:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/source_maps-0.10.1/lib/ +source_span:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/source_span-1.2.1/lib/ +stack_trace:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.5.0/lib/ +string_scanner:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/string_scanner-0.1.4/lib/ +test:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/test-0.12.4+9/lib/ +typed_data:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/typed_data-1.1.1/lib/ +unittest:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/unittest-0.12.4/lib/ +utf:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/utf-0.9.0+2/lib/ +watcher:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/watcher-0.9.7/lib/ +when:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/when-0.2.0/lib/ +which:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/which-0.1.3/lib/ +yaml:file:///C:/Users/Jake/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/yaml-2.1.7/lib/ +nomirrorsmap:lib/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d865b99 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: dart +dart: + - stable +sudo: false +before_script: + - chmod +x travis.sh + - chmod +x ensure_dartfmt.sh +script: ./travis.sh diff --git a/README.md b/README.md index 85c79b1..eccec9b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,115 @@ -# nomirrorsmap.dart -Maps from any source to any destination without using mirrors +## NoMirrorsMap [![Build Status](https://travis-ci.org/jrote1/nomirrorsmap.dart.svg)](https://travis-ci.org/jrote1/nomirrorsmap.dart) [![Coverage Status](https://coveralls.io/repos/jrote1/nomirrorsmap.dart/badge.svg?branch=master&service=github)](https://coveralls.io/github/jrote1/nomirrorsmap.dart?branch=master) +### Information +NoMirrorsMap allows you to map objects in one format to objects in another format. For example, you can use it to serialise and deserialise native dart objects to and from Json, or to map an object of one type to an object of another type with the same property names. + +NoMirrorsMap does this without using Mirrors (hence the name...). It uses a transformer to create a map file for all the objects that you may need to map, and uses that map file at runtime to avoid the use of Mirrors and to significantly improve the mapping performance. + +### Usage + +Before you can use NoMirrorsMap, you need to add a transformer entry to your pubspec.yaml file and you need to tell NoMirrorsMap which types you are going to be mapping so the transformer knows which types it needs to generate mapping data for. You can do this in two ways: + +1. You can decorate any types that need to be mapped with the `@Mappable()` attribute. +2. You can add an entry for a library under the transformer declaration in your pubspec.yaml file, which will cause NoMirrorsMap to generate mapping data for ALL the types in that library. + +NoMirrorsMap works via a two step process; first the object to be mapped from one format to another is mapped into an intermediate format, and then that intermediate format is mapped to the final target format. While the object is in its intermediate format, you have the opportunity to manipulate the object. In the current codebase, NoMirrorsMap contains manipulators which can change the casing of properties to PascalCase, or to camelCase, or to change the target type. + +When you call the `convert` method, you supply the object to be mapped, the converter to convert the object to the intermediate format, the converter to convert the intermediate format to the final format, and an optional list of manipulators to modify the intermediate format before it is run through the target format converter. + +So for example, to convert an object to Json, you would do this: + +``` +var json = new NoMirrorsMap().convert(objectToMap, new ClassConverter(), new JsonConverter()); +``` + +To convert an object to Json, changing the casing of the properties to PascalCase in the process, you would do this: + +``` +var json = new NoMirrorsMap().convert(objectToMap, new ClassConverter(), new JsonConverter(), [ new PascalCaseManipulator() ]); +``` + +Other manipulators include: +- PascalCaseManipulator +- CamelCaseManipulator +- TypeToTypeManipulator + +We recommend creating static methods for doing common mappings. For example you might create the following static methods for encoding and decoding json: + +``` +class Json { + static String encode( dynamic obj ) { + return new NoMirrorsMap().convert( obj, new ClassConverter(), new JsonConverter() ); + } + + static String decode( string json, Type type ) { + return new NoMirrorsMap().convert( json, new JsonConverter(), new ClassConverter( startType: type ) ); + } +} +``` + +NoMirrorsMap has a few converters within the codebase by default, but you can easily write your own if you need to map to or from any other formats you may need (XML, CSV etc). Feel free to submit these in a pull request! + +#### Basic Example Using The `@Mappable()` Attribute +pubspec.yaml +``` +dependencies: + nomirrorsmap: any +transformers: + - nomirrorsmap +``` +main.dart +```dart +import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){ + var obj = new EncodableObject()..id = 1; + var json = new NoMirrorsMap().convert(obj, new ClassConverter(), new JsonConverter()); +} + +@Mappable() +class EncodableObject { + int id; +} +``` +#### Basic Example Using Library Declaration in pubspec.yaml + + +pubspec.yaml +``` +dependencies: + nomirrorsmap: any +transformers: + - nomirrorsmap: + library_names: + - "my_library" +``` +main.dart +```dart +import 'package:nomirrorsmap/nomirrorsmap.dart'; +import 'my_library.dart'; + +main(){ + var obj1 = new FirstEncodableObject()..id = 1; + var obj2 = new SecondEncodableObject()..id = 2; + + var mapper = new NoMirrorsMap(); + var json1 = mapper.convert(obj1, new ClassConverter(), new JsonConverter()); + var json2 = mapper.convert(obj2, new ClassConverter(), new JsonConverter()); + +} +``` +my_library.dart +``` +library my_library; + +class FirstEncodableObject { + int id; +} + +class SecondEncodableObject { + int id; +} +``` + + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/jrote1/nomirrorsmap.dart/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + diff --git a/ensure_dartfmt.sh b/ensure_dartfmt.sh new file mode 100644 index 0000000..ed4e5ca --- /dev/null +++ b/ensure_dartfmt.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +dart_files=$(git ls-tree --name-only --full-tree -r HEAD | grep '.dart$') +[ -z "$dart_files" ] && exit 0 + +unformatted=$(dartfmt -n $dart_files) +[ -z "$unformatted" ] && exit 0 + +# Some files are not dartfmt'd. Print message and fail. +echo >&2 "dart files must be formatted with dartfmt. Please run:" +for fn in $unformatted; do + echo >&2 " dartfmt -w $PWD/$fn" +done + +exit 1 diff --git a/lib/main_transformer.dart b/lib/main_transformer.dart deleted file mode 100644 index 2345fe3..0000000 --- a/lib/main_transformer.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:barback/barback.dart'; -import 'package:code_transformers/resolver.dart'; -import 'package:nomirrorsmap/src/transformer/transformer.dart'; - -class MainTransformer implements TransformerGroup -{ - final Iterable phases; - - MainTransformer( ) - : phases = _createPhases( ); - - MainTransformer.asPlugin( ): phases = _createPhases( ); - - static _createPhases( ) - { - var resolvers = new Resolvers( dartSdkDirectory ); - return [[new MapGeneratorTransformer( resolvers )]]; - } -} \ No newline at end of file diff --git a/lib/nomirrorsmap.dart b/lib/nomirrorsmap.dart index 5965343..31e48e7 100644 --- a/lib/nomirrorsmap.dart +++ b/lib/nomirrorsmap.dart @@ -7,23 +7,19 @@ export 'src/shared/shared.dart'; import 'src/converters/converters.dart'; import 'src/manipulators/manipulators.dart'; -class NoMirrorsMap -{ - dynamic convert( dynamic value, Converter sourceConverter, Converter destinationConverter, [List manipulators] ) - { - var convertedSource = sourceConverter.toBaseObjectData( value ); - if(manipulators != null) - manipulators.forEach((m) => m.manipulate(convertedSource)); - return destinationConverter.fromBaseObjectData( convertedSource ); - } - -} - -class MapType{ - const MapType(); +class NoMirrorsMap { + NoMirrorsMap() { + if (TypeInformationRetrieverLocator.instance == + null) TypeInformationRetrieverLocator + .setInstance(new NoMirrorsMapStore()); + } + + dynamic convert( + dynamic value, Converter sourceConverter, Converter destinationConverter, + [List manipulators]) { + var convertedSource = sourceConverter.toBaseIntermediateObject(value); + if (manipulators != null) manipulators + .forEach((m) => m.manipulate(convertedSource)); + return destinationConverter.fromBaseIntermediateObject(convertedSource); + } } - - - - - diff --git a/lib/nomirrorsmap_mirrors.dart b/lib/nomirrorsmap_mirrors.dart new file mode 100644 index 0000000..05104a9 --- /dev/null +++ b/lib/nomirrorsmap_mirrors.dart @@ -0,0 +1,62 @@ +library nomirrorsmap.mirrors; + +import 'src/converters/converters.dart'; +import 'package:reflective/reflective.dart' as reflective; + +void useMirrors() { + TypeInformationRetrieverLocator + .setInstance(new MirrorsTypeInformationRetriever()); +} + +class MirrorsTypeInformationRetriever implements TypeInformationRetriever { + @override + bool containsEnumGeneratedMap(Type type) { + return new reflective.TypeReflection(type).isEnum; + } + + @override + ClassMapping getClassGeneratedMap(Type type) { + var result = new ClassMapping()..fields = []; + reflective.TypeReflection reflectiveType = reflective.type(type); + for (var fieldName in reflectiveType.fields.keys) { + var field = reflectiveType.field(fieldName); + var fieldMapping = new FieldMapping() + ..name = field.name + ..setter = field.set + ..getter = field.value; + + result.fields.add(new ClassField() + ..type = field.type.rawType + ..fieldMapping = fieldMapping); + } + result.type = type; + result.fullName = reflectiveType.fullName; + result.instantiate = () => reflectiveType.construct(); + + return result; + } + + @override + ClassMapping getClassGeneratedMapByListType(Type type) { + var classType = + new reflective.TypeReflection(type).typeArguments.first.rawType; + return getClassGeneratedMap(classType); + } + + @override + ClassMapping getClassGeneratedMapByQualifiedName(String qualifiedName) { + var classType = + new reflective.TypeReflection.fromFullName(qualifiedName).rawType; + return getClassGeneratedMap(classType); + } + + @override + ClassMapping getClassGeneratedMapWithNoCheck(Type type) { + return getClassGeneratedMap(type); + } + + @override + List getEnumGeneratedMap(Type type) { + return new reflective.TypeReflection(type).enumValues; + } +} diff --git a/lib/src/conversion_objects/base_intermediate_object.dart b/lib/src/conversion_objects/base_intermediate_object.dart new file mode 100644 index 0000000..cf8d15c --- /dev/null +++ b/lib/src/conversion_objects/base_intermediate_object.dart @@ -0,0 +1,5 @@ +part of nomirrorsmap.conversion_objects; + +class BaseIntermediateObject { + Type objectType; +} diff --git a/lib/src/conversion_objects/base_object_data.dart b/lib/src/conversion_objects/base_object_data.dart deleted file mode 100644 index fdd8528..0000000 --- a/lib/src/conversion_objects/base_object_data.dart +++ /dev/null @@ -1,9 +0,0 @@ -part of nomirrorsmap.conversion_objects; - -class BaseObjectData -{ - Type objectType; - - bool get isNativeType - => false; -} \ No newline at end of file diff --git a/lib/src/conversion_objects/class_intermediate_object.dart b/lib/src/conversion_objects/class_intermediate_object.dart new file mode 100644 index 0000000..f61c0b7 --- /dev/null +++ b/lib/src/conversion_objects/class_intermediate_object.dart @@ -0,0 +1,7 @@ +part of nomirrorsmap.conversion_objects; + +class ClassIntermediateObject extends BaseIntermediateObject { + String previousHashCode; + + Map properties; +} diff --git a/lib/src/conversion_objects/class_object_data.dart b/lib/src/conversion_objects/class_object_data.dart deleted file mode 100644 index 3932988..0000000 --- a/lib/src/conversion_objects/class_object_data.dart +++ /dev/null @@ -1,11 +0,0 @@ -part of nomirrorsmap.conversion_objects; - -class ClassObjectData extends BaseObjectData -{ - bool get isNativeType - => false; - - String previousHashCode; - - Map properties; -} \ No newline at end of file diff --git a/lib/src/conversion_objects/conversion_objects.dart b/lib/src/conversion_objects/conversion_objects.dart index 10aae4f..c180fe0 100644 --- a/lib/src/conversion_objects/conversion_objects.dart +++ b/lib/src/conversion_objects/conversion_objects.dart @@ -1,6 +1,6 @@ library nomirrorsmap.conversion_objects; -part 'class_object_data.dart'; -part 'native_object_data.dart'; -part 'list_object_data.dart'; -part 'base_object_data.dart'; \ No newline at end of file +part 'class_intermediate_object.dart'; +part 'native_intermediate_object.dart'; +part 'list_intermediate_object.dart'; +part 'base_intermediate_object.dart'; diff --git a/lib/src/conversion_objects/list_intermediate_object.dart b/lib/src/conversion_objects/list_intermediate_object.dart new file mode 100644 index 0000000..59b8966 --- /dev/null +++ b/lib/src/conversion_objects/list_intermediate_object.dart @@ -0,0 +1,5 @@ +part of nomirrorsmap.conversion_objects; + +class ListIntermediateObject extends BaseIntermediateObject { + List values; +} diff --git a/lib/src/conversion_objects/list_object_data.dart b/lib/src/conversion_objects/list_object_data.dart deleted file mode 100644 index 18e18b9..0000000 --- a/lib/src/conversion_objects/list_object_data.dart +++ /dev/null @@ -1,10 +0,0 @@ -part of nomirrorsmap.conversion_objects; - -class ListObjectData extends BaseObjectData -{ - - bool get isNativeType - => true; - - List values; -} \ No newline at end of file diff --git a/lib/src/conversion_objects/native_intermediate_object.dart b/lib/src/conversion_objects/native_intermediate_object.dart new file mode 100644 index 0000000..983a0e3 --- /dev/null +++ b/lib/src/conversion_objects/native_intermediate_object.dart @@ -0,0 +1,5 @@ +part of nomirrorsmap.conversion_objects; + +class NativeIntermediateObject extends BaseIntermediateObject { + dynamic value; +} diff --git a/lib/src/conversion_objects/native_object_data.dart b/lib/src/conversion_objects/native_object_data.dart deleted file mode 100644 index 1a7958d..0000000 --- a/lib/src/conversion_objects/native_object_data.dart +++ /dev/null @@ -1,10 +0,0 @@ -part of nomirrorsmap.conversion_objects; - -class NativeObjectData extends BaseObjectData -{ - - bool get isNativeType - => true; - - dynamic value; -} \ No newline at end of file diff --git a/lib/src/converters/class_converter.dart b/lib/src/converters/class_converter.dart index 3dcdb14..243bbd8 100644 --- a/lib/src/converters/class_converter.dart +++ b/lib/src/converters/class_converter.dart @@ -1,152 +1,165 @@ part of nomirrorsmap.converters; - - -class ClassConverter implements Converter -{ - Type startType; - - ClassConverter( {this.startType} ); - - static Map converters = { - }; - - List seenHashCodes = []; - - BaseObjectData toBaseObjectData( dynamic value ) - { - var valueType = value.runtimeType; - if ( converters.containsKey( valueType ) ) - value = converters[valueType].from( value ); - - if ( _isPrimitive( value ) ) - return new NativeObjectData( ) - ..objectType = valueType - ..value = value; - if(_isEnum( value )){ - return new NativeObjectData( ) - ..objectType = int - ..value = value.index; - } - - if ( value is List ) - { - return new ListObjectData( ) - ..values = value.map( ( v ) - => toBaseObjectData( v ) ).toList( ); - } - - if ( seenHashCodes.contains( value.hashCode.toString( ) ) ) - return new ClassObjectData( ) - ..objectType = valueType - ..previousHashCode = value.hashCode.toString( ) - ..properties = { - }; - seenHashCodes.add( value.hashCode.toString( ) ); - - var generatedMap = GeneratedMapProvider.getClassGeneratedMap( (value as Object).runtimeType ); - - var properties = { - }; - - generatedMap.properties.forEach((name, propertyMap){ - properties[name] = toBaseObjectData( propertyMap.getValue( value ) ); - }); - - return new ClassObjectData( ) - ..objectType = valueType - ..previousHashCode = value.hashCode.toString( ) - ..properties = properties; - } - - bool _isEnum( dynamic value ) - { - //Not safe - return _isTypeEnum(value.runtimeType); - } - - bool _isPrimitive( v ) => v is num || v is bool || v is String || v == null || v is DateTime; - - Map instances = { - }; - - dynamic fromBaseObjectData( BaseObjectData baseObjectData ) - { - return _fromBaseObjectData( baseObjectData, baseObjectData.objectType == null ? startType : baseObjectData.objectType ); - } - - dynamic _fromBaseObjectData( BaseObjectData baseObjectData, Type type ) - { - if ( baseObjectData is ClassObjectData ) - { - var generatedMap = GeneratedMapProvider.getClassGeneratedMap(type); - var instance = generatedMap.initialize(); - - - ClassConverterInstance classConverterInstance; - if ( baseObjectData.previousHashCode != null && instances.containsKey( baseObjectData.previousHashCode ) ) - classConverterInstance = instances[baseObjectData.previousHashCode]; - else - { - classConverterInstance = new ClassConverterInstance( ) - ..filled = false - ..instance = instance; - - if(baseObjectData.previousHashCode != null) - instances[baseObjectData.previousHashCode] = classConverterInstance; - } - if ( !classConverterInstance.filled && baseObjectData.properties.length > 0 ) - { - generatedMap.properties.forEach((name, propertyMap){ - if ( baseObjectData.properties.containsKey( name ) ) - { - var propertyObjectData = baseObjectData.properties[name]; - var propertyType = propertyObjectData.objectType == null ? propertyMap.type : propertyObjectData.objectType; - var value = _fromBaseObjectData( propertyObjectData, propertyType ); - if ( converters.containsKey( propertyType ) ) - value = converters[propertyType].to( value ); - if ( value is List ) - { - var list = GeneratedMapProvider.getListGeneratedMap( propertyType ).initialize(); - list.addAll( value ); - value = list; - } - propertyMap.setValue( classConverterInstance.instance, value ); - } - }); - classConverterInstance.filled = true; - } - return classConverterInstance.instance; - } - if ( baseObjectData is ListObjectData ) - { - var listType = GeneratedMapProvider.getListGeneratedMap(type).innerType; - - return baseObjectData.values.map( ( v ) - => _fromBaseObjectData( v, v.objectType == null ? listType : v.objectType ) ).toList( ); - } - var nativeObjectValue = (baseObjectData as NativeObjectData).value; - - if( type == DateTime){ - if(nativeObjectValue is DateTime) - return nativeObjectValue; - return DateTime.parse( nativeObjectValue ); - } - if(_isTypeEnum(type)){ - return GeneratedMapProvider.getEnumGeneratedMap(type).values[nativeObjectValue]; - } - - if (type == double && nativeObjectValue != null) - { - return double.parse( nativeObjectValue.toString()); - } - - - return nativeObjectValue; - } - - bool _isTypeEnum( Type type ) - { - return GeneratedMapProvider.containsEnumGeneratedMap(type); - } -} \ No newline at end of file +class ClassConverter implements Converter { + Type startType; + + ClassConverter({this.startType}); + + static Map converters = {}; + + Set seenHashCodes = new Set(); + + TypeInformationRetriever get _typeInformationRetriever => + TypeInformationRetrieverLocator.instance; + + BaseIntermediateObject toBaseIntermediateObject(Object value) { + var valueType = value.runtimeType; + if (converters.containsKey(valueType)) return converters[valueType] + .from(value); + + if (value is List) { + return new ListIntermediateObject() + ..values = value.map((v) => toBaseIntermediateObject(v)).toList(); + } + + if (_isPrimitive(value)) return new NativeIntermediateObject() + ..objectType = valueType + ..value = value; + + if (_isEnum(value)) { + return new NativeIntermediateObject() + ..objectType = int + ..value = (value as dynamic).index; + } + + var hashCode = value.hashCode; + if (seenHashCodes.contains(hashCode)) return new ClassIntermediateObject() + ..objectType = valueType + ..previousHashCode = hashCode.toString() + ..properties = {}; + seenHashCodes.add(hashCode); + + var generatedMap = + _typeInformationRetriever.getClassGeneratedMap(valueType); + + var properties = {}; + + //Faster than foreach loop + for (var i = 0; i < generatedMap.fields.length; i++) { + var property = generatedMap.fields[i]; + var getter = property.fieldMapping.getter; + properties[property.fieldMapping.name] = + toBaseIntermediateObject(getter(value)); + } + + return new ClassIntermediateObject() + ..objectType = valueType + ..previousHashCode = value.hashCode.toString() + ..properties = properties; + } + + bool _isEnum(dynamic value) { + //Not safe + return _isTypeEnum(value.runtimeType); + } + + bool _isPrimitive(v) => + v is num || v is bool || v is String || v == null || v is DateTime; + + Map instances = + new Map(); + + dynamic fromBaseIntermediateObject(BaseIntermediateObject baseObjectData) { + return _fromBaseObjectData( + baseObjectData, + baseObjectData.objectType == null + ? startType + : baseObjectData.objectType); + } + + dynamic _fromBaseObjectData( + BaseIntermediateObject baseObjectData, Type type) { + if (baseObjectData is ClassIntermediateObject) { + var generatedMap = _typeInformationRetriever.getClassGeneratedMap(type); + if (generatedMap.instantiate == + null) throw "Type '$type' does not have a default constructor, make sure it has a default constructor wuth no paramters"; + var instance = generatedMap.instantiate(); + + ClassConverterInstance classConverterInstance; + if (baseObjectData.previousHashCode != null && + instances.containsKey( + baseObjectData.previousHashCode)) classConverterInstance = + instances[baseObjectData.previousHashCode]; + else { + classConverterInstance = new ClassConverterInstance() + ..filled = false + ..instance = instance; + + if (baseObjectData.previousHashCode != null) instances[ + baseObjectData.previousHashCode] = classConverterInstance; + } + if (!classConverterInstance.filled && + baseObjectData.properties.length > 0) { + for (var property in generatedMap.fields) { + if (baseObjectData.properties + .containsKey(property.fieldMapping.name)) { + var setter = property.fieldMapping.setter; + + var propertyObjectData = + baseObjectData.properties[property.fieldMapping.name]; + var propertyType = propertyObjectData.objectType == null + ? property.type + : propertyObjectData.objectType; + + Object value; + if (converters.containsKey(propertyType)) value = + converters[propertyType].to(propertyObjectData); + else value = _fromBaseObjectData(propertyObjectData, propertyType); + + if (value is List) { + var list = []; + list.addAll(value); + value = list; + } + setter(classConverterInstance.instance, value); + } + } + classConverterInstance.filled = true; + } + return classConverterInstance.instance; + } + if (baseObjectData is ListIntermediateObject) { + var classMap = + _typeInformationRetriever.getClassGeneratedMapByListType(type); + + var listType = classMap == null ? Object : classMap.type; + + return baseObjectData.values + .map((v) => _fromBaseObjectData( + v, v.objectType == null ? listType : v.objectType)) + .toList(); + } + var nativeObjectValue = (baseObjectData as NativeIntermediateObject).value; + + if (nativeObjectValue == null) return null; + + if (type == DateTime) { + if (nativeObjectValue is DateTime) return nativeObjectValue; + return DateTime.parse(nativeObjectValue); + } + if (_isTypeEnum(type)) { + return _typeInformationRetriever.getEnumGeneratedMap(type)[ + nativeObjectValue]; + } + + if (type == double && nativeObjectValue != null) { + return double.parse(nativeObjectValue.toString()); + } + + return nativeObjectValue; + } + + bool _isTypeEnum(Type type) { + return _typeInformationRetriever.containsEnumGeneratedMap(type); + } +} diff --git a/lib/src/converters/converter.dart b/lib/src/converters/converter.dart index a9ea526..1286d6f 100644 --- a/lib/src/converters/converter.dart +++ b/lib/src/converters/converter.dart @@ -1,8 +1,7 @@ part of nomirrorsmap.converters; -abstract class Converter -{ - BaseObjectData toBaseObjectData( dynamic value ); +abstract class Converter { + BaseIntermediateObject toBaseIntermediateObject(dynamic value); - dynamic fromBaseObjectData( BaseObjectData baseObjectData ); -} \ No newline at end of file + dynamic fromBaseIntermediateObject(BaseIntermediateObject baseObjectData); +} diff --git a/lib/src/converters/converters.dart b/lib/src/converters/converters.dart index 46094c7..bc86366 100644 --- a/lib/src/converters/converters.dart +++ b/lib/src/converters/converters.dart @@ -9,4 +9,10 @@ part 'converter.dart'; part 'class_converter.dart'; part 'json_converter.dart'; part 'newton_soft_json_converter.dart'; -part 'helpers/class_converter_instance.dart'; \ No newline at end of file +part 'helpers/class_converter_instance.dart'; + +part 'mapping_store/no_mirrors_map_store.dart'; +part 'mapping_store/class_mapping.dart'; +part 'mapping_store/class_property.dart'; +part 'mapping_store/property_mapping.dart'; +part 'mapping_store/type_information_retriever.dart'; diff --git a/lib/src/converters/helpers/class_converter_instance.dart b/lib/src/converters/helpers/class_converter_instance.dart index 8a9a2b6..c860aee 100644 --- a/lib/src/converters/helpers/class_converter_instance.dart +++ b/lib/src/converters/helpers/class_converter_instance.dart @@ -1,7 +1,6 @@ part of nomirrorsmap.converters; -class ClassConverterInstance -{ - dynamic instance; - bool filled; -} \ No newline at end of file +class ClassConverterInstance { + dynamic instance; + bool filled; +} diff --git a/lib/src/converters/json_converter.dart b/lib/src/converters/json_converter.dart index a984a72..8f9f221 100644 --- a/lib/src/converters/json_converter.dart +++ b/lib/src/converters/json_converter.dart @@ -1,81 +1,111 @@ part of nomirrorsmap.converters; -class JsonConverter implements Converter -{ - String _hashcodeName; - - JsonConverter( [String hashcodeName = "\$hashcode"] ) - { - _hashcodeName = hashcodeName; - } - - BaseObjectData toBaseObjectData( dynamic value ) - { - if ( !(value is String) ) - throw new Exception( "value is not a String" ); - var json = JSON.decode( value ); - return _jsonToBaseObjectData( json ); - } - - String getPreviousHashcode( Map json ) - => json[_hashcodeName]; - - Type findObjectType( dynamic json ) - { - return json.containsKey( "\$type" ) ? GeneratedMapProvider.getClassGeneratedMapByQualifiedName( json["\$type"] ).type : null; - } - - void afterCreatingClassObjectData( ClassObjectData classObjectData ) - { - } - - BaseObjectData _jsonToBaseObjectData( dynamic json ) - { - if ( json is Map ) - { - var classObjectData = new ClassObjectData( ); - classObjectData.previousHashCode = getPreviousHashcode( json ); - classObjectData.previousHashCode = getPreviousHashcode( json ); - classObjectData.objectType = findObjectType( json ); - - afterCreatingClassObjectData( classObjectData ); - Map properties = { - }; - (json as Map).forEach( ( key, value ) - { - properties[key] = _jsonToBaseObjectData( value ); - } ); - - - classObjectData.properties = properties; - - return classObjectData; - } else if ( json is List ) - return new ListObjectData( ) - ..values = json.map( ( o ) - => _jsonToBaseObjectData( o ) ).toList( ); - return new NativeObjectData( ) - ..value = json; - } - - dynamic fromBaseObjectData( BaseObjectData baseObjectData ) - { - return JSON.encode( _fromBaseObjectData( baseObjectData ) ); - } - - void setMetaData( Map result, String hashcode, ClassObjectData classObjectData ) - { - result[_hashcodeName] = hashcode; - setTypeFromObjectType( result, classObjectData ); - } - - void setTypeFromObjectType( Map json, ClassObjectData classObjectData ) - { - json["\$type"] = GeneratedMapProvider.getClassGeneratedMap(classObjectData.objectType).qualifiedName; - } - - dynamic _fromBaseObjectData( BaseObjectData baseObjectData ) - { +class JsonConverter implements Converter { + String _hashcodeName; + + TypeInformationRetriever get _typeInformationRetriever => + TypeInformationRetrieverLocator.instance; + + JsonConverter([String hashcodeName = "\$hashcode"]) { + _hashcodeName = hashcodeName; + } + + BaseIntermediateObject toBaseIntermediateObject(dynamic value) { + if (!(value is String)) throw new Exception("value is not a String"); + var json = JSON.decode(value); + return _jsonToBaseObjectData(json); + } + + String getPreviousHashcode(Map json) => json[_hashcodeName]; + + Type findObjectType(dynamic json) { + return json.containsKey("\$type") + ? _typeInformationRetriever + .getClassGeneratedMapByQualifiedName(json["\$type"]) + .type + : null; + } + + void afterCreatingClassObjectData(ClassIntermediateObject classObjectData) {} + + BaseIntermediateObject _jsonToBaseObjectData(dynamic json) { + if (json is Map) { + var classObjectData = new ClassIntermediateObject(); + classObjectData.previousHashCode = getPreviousHashcode(json); + classObjectData.objectType = findObjectType(json); + + afterCreatingClassObjectData(classObjectData); + Map properties = {}; + (json as Map).forEach((key, value) { + properties[key] = _jsonToBaseObjectData(value); + }); + + classObjectData.properties = properties; + + return classObjectData; + } else if (json is List) return new ListIntermediateObject() + ..values = json.map((o) => _jsonToBaseObjectData(o)).toList(); + return new NativeIntermediateObject()..value = json; + } + + dynamic fromBaseIntermediateObject(BaseIntermediateObject baseObjectData) { + var stringBuffer = new StringBuffer(); + _fromBaseObjectData(baseObjectData, stringBuffer); + return stringBuffer.toString(); + } + + void setMetaData( + StringBuffer stringBuffer, ClassIntermediateObject classObjectData) { + stringBuffer + .write("\"$_hashcodeName\":\"${classObjectData.previousHashCode}\","); + + setTypeFromObjectType(stringBuffer, classObjectData); + } + + void setTypeFromObjectType( + StringBuffer stringBuffer, ClassIntermediateObject classObjectData) { + var map = _typeInformationRetriever + .getClassGeneratedMapWithNoCheck(classObjectData.objectType); + if (map != null) { + stringBuffer.write("\"\$type\":\"${map.fullName}\""); + stringBuffer.write(classObjectData.properties.length > 0 ? "," : ""); + } + } + + void _fromBaseObjectData( + BaseIntermediateObject baseObjectData, StringBuffer stringBuffer) { + if (baseObjectData is ClassIntermediateObject) { + stringBuffer.write("{"); + + setMetaData(stringBuffer, baseObjectData); + + for (var key in baseObjectData.properties.keys) { + stringBuffer.write("\"$key\":"); + _fromBaseObjectData(baseObjectData.properties[key], stringBuffer); + if (baseObjectData.properties.keys.last != key) stringBuffer.write(","); + } + + stringBuffer.write("}"); + } + if (baseObjectData is ListIntermediateObject) { + stringBuffer.write("["); + for (var i = 0; i < baseObjectData.values.length; i++) { + var value = baseObjectData.values[i]; + _fromBaseObjectData(value, stringBuffer); + if (i != (baseObjectData.values.length - 1)) stringBuffer.write(","); + } + stringBuffer.write("]"); + } + + if (baseObjectData is NativeIntermediateObject) { + if (baseObjectData.value is String) stringBuffer.write("\"" + + baseObjectData.value.replaceAll(r"\", r'\\').replaceAll("\"", '\\"') + + "\""); + else if (baseObjectData.value is DateTime) stringBuffer + .write('"${baseObjectData.value.toString()}"'); + else stringBuffer.write(baseObjectData.value); + } + /* if ( baseObjectData is ClassObjectData ) { var result = { @@ -93,6 +123,6 @@ class JsonConverter implements Converter => _fromBaseObjectData( v ) ).toList( ); } return (baseObjectData as NativeObjectData).value; - } + */ + } } - diff --git a/lib/src/converters/mapping_store/class_mapping.dart b/lib/src/converters/mapping_store/class_mapping.dart new file mode 100644 index 0000000..de83ce7 --- /dev/null +++ b/lib/src/converters/mapping_store/class_mapping.dart @@ -0,0 +1,9 @@ +part of nomirrorsmap.converters; + +class ClassMapping { + String fullName; + Type type; + Type listType; + Function instantiate; + List fields; +} diff --git a/lib/src/converters/mapping_store/class_property.dart b/lib/src/converters/mapping_store/class_property.dart new file mode 100644 index 0000000..0beb8c8 --- /dev/null +++ b/lib/src/converters/mapping_store/class_property.dart @@ -0,0 +1,6 @@ +part of nomirrorsmap.converters; + +class ClassField { + Type type; + FieldMapping fieldMapping; +} diff --git a/lib/src/converters/mapping_store/no_mirrors_map_store.dart b/lib/src/converters/mapping_store/no_mirrors_map_store.dart new file mode 100644 index 0000000..8daf4bd --- /dev/null +++ b/lib/src/converters/mapping_store/no_mirrors_map_store.dart @@ -0,0 +1,88 @@ +part of nomirrorsmap.converters; + +class NoMirrorsMapStore implements TypeInformationRetriever { + static List _fieldMappings = []; + static List _classMappings = []; + static Map _enumMappings = {}; + + static void registerField( + String fieldName, + void setter(dynamic object, dynamic value), + dynamic getter(dynamic object)) { + _fieldMappings.add(new FieldMapping() + ..getter = getter + ..setter = setter + ..name = fieldName); + } + + static Map _classMappingsByType = {}; + + ClassMapping getClassGeneratedMap(Type type) { + var classMapping = _classMappingsByType[type]; + if (classMapping == null) { + if (_classMappings + .any((m) => m.type == type)) return _classMappingsByType[type] = + _classMappings.firstWhere((m) => m.type == type); + else { + throw "Can't find map for type '${type.toString( )}' is it missing the @Mappable() annotation "; + } + } + return classMapping; + } + + ClassMapping getClassGeneratedMapWithNoCheck(Type type) { + var classMapping = _classMappingsByType[type]; + if (classMapping == null) { + if (_classMappings + .any((m) => m.type == type)) return _classMappingsByType[type] = + _classMappings.firstWhere((m) => m.type == type); + return null; + } + return classMapping; + } + + static Map _classMappingsByListType = {}; + + ClassMapping getClassGeneratedMapByListType(Type type) { + var classMapping = _classMappingsByListType[type]; + if (classMapping == null) { + if (!type.toString().contains("<")) return null; + if (_classMappings + .any((m) => m.listType == type)) return _classMappingsByListType[ + type] = _classMappings.firstWhere((m) => m.listType == type); + throw "Can't find map for type '${type.toString( )}' is it missing the @Mappable() annotation "; + } + return classMapping; + } + + ClassMapping getClassGeneratedMapByQualifiedName(String qualifiedName) { + if (_classMappings + .any((m) => m.fullName == qualifiedName)) return _classMappings + .firstWhere((m) => m.fullName == qualifiedName); + throw "Can't find map for type '$qualifiedName' is it missing the @Mappable() annotation "; + } + + static void registerClass(String fullName, Type type, Type listType, + dynamic instantiate(), Map fields) { + var classFields = []; + fields.forEach((k, v) { + classFields.add(new ClassField() + ..type = v + ..fieldMapping = _fieldMappings.firstWhere((p) => p.name == k)); + }); + + _classMappings.add(new ClassMapping() + ..type = type + ..listType = listType + ..fullName = fullName + ..instantiate = instantiate + ..fields = classFields); + } + + static void registerEnum(Type type, List values) { + _enumMappings[type] = values; + } + + bool containsEnumGeneratedMap(Type type) => _enumMappings.containsKey(type); + List getEnumGeneratedMap(Type type) => _enumMappings[type]; +} diff --git a/lib/src/converters/mapping_store/property_mapping.dart b/lib/src/converters/mapping_store/property_mapping.dart new file mode 100644 index 0000000..b6b2dcf --- /dev/null +++ b/lib/src/converters/mapping_store/property_mapping.dart @@ -0,0 +1,7 @@ +part of nomirrorsmap.converters; + +class FieldMapping { + String name; + Function setter; + Function getter; +} diff --git a/lib/src/converters/mapping_store/type_information_retriever.dart b/lib/src/converters/mapping_store/type_information_retriever.dart new file mode 100644 index 0000000..168c9ad --- /dev/null +++ b/lib/src/converters/mapping_store/type_information_retriever.dart @@ -0,0 +1,20 @@ +part of nomirrorsmap.converters; + +abstract class TypeInformationRetriever { + ClassMapping getClassGeneratedMap(Type type); + ClassMapping getClassGeneratedMapWithNoCheck(Type type); + ClassMapping getClassGeneratedMapByListType(Type type); + ClassMapping getClassGeneratedMapByQualifiedName(String qualifiedName); + bool containsEnumGeneratedMap(Type type); + List getEnumGeneratedMap(Type type); +} + +class TypeInformationRetrieverLocator { + static TypeInformationRetriever _instance; + + static setInstance(TypeInformationRetriever instance) { + _instance = instance; + } + + static TypeInformationRetriever get instance => _instance; +} diff --git a/lib/src/converters/newton_soft_json_converter.dart b/lib/src/converters/newton_soft_json_converter.dart index 7a16256..ada2b45 100644 --- a/lib/src/converters/newton_soft_json_converter.dart +++ b/lib/src/converters/newton_soft_json_converter.dart @@ -1,53 +1,53 @@ part of nomirrorsmap.converters; -class NewtonSoftJsonConverter extends JsonConverter -{ - Map fromJsonHashCodesAndTypes = new Map(); - List toJsonSeenHashcodes = new List(); - - @override - void setMetaData(Map result, String hashcode, ClassObjectData classObjectData){ - if (toJsonSeenHashcodes.contains(hashcode)) - result["\$ref"] = hashcode; - else - { - toJsonSeenHashcodes.add(hashcode); - result["\$id"] = hashcode; - setTypeFromObjectType(result, classObjectData); - } - } - - @override - String getPreviousHashcode(Map json) - { - if (json.containsKey("\$ref")) - return json["\$ref"]; - - return json["\$id"]; - } - - @override - Type findObjectType(dynamic json) - { - Type objectType = null; - - if (json.containsKey("\$type")) - objectType = GeneratedMapProvider.getClassGeneratedMapByQualifiedName( json["\$type"] ).type; - else - { - if - ( !json.containsKey( "\$type" ) && fromJsonHashCodesAndTypes.containsKey( json["\$ref"] ) ) - objectType =fromJsonHashCodesAndTypes[json["\$ref"]]; - } - - return objectType; - } - - @override - void afterCreatingClassObjectData(ClassObjectData classObjectData) - { - if (!fromJsonHashCodesAndTypes.containsKey(classObjectData.previousHashCode) && classObjectData.objectType != null) - fromJsonHashCodesAndTypes[classObjectData.previousHashCode] = classObjectData.objectType; - } - -} \ No newline at end of file +class NewtonSoftJsonConverter extends JsonConverter { + Map fromJsonHashCodesAndTypes = new Map(); + List toJsonSeenHashcodes = new List(); + + TypeInformationRetriever get _typeInformationRetriever => + TypeInformationRetrieverLocator.instance; + + @override + void setMetaData( + StringBuffer stringBuffer, ClassIntermediateObject classObjectData) { + if (toJsonSeenHashcodes.contains(classObjectData.previousHashCode)) { + stringBuffer.write("\"\$ref\":\"${classObjectData.previousHashCode}\""); + stringBuffer.write(classObjectData.properties.length > 0 ? "," : ""); + } else { + toJsonSeenHashcodes.add(classObjectData.previousHashCode); + stringBuffer.write("\"\$id\":\"${classObjectData.previousHashCode}\","); + setTypeFromObjectType(stringBuffer, classObjectData); + } + } + + @override + String getPreviousHashcode(Map json) { + if (json.containsKey("\$ref")) return json["\$ref"]; + + return json["\$id"]; + } + + @override + Type findObjectType(dynamic json) { + Type objectType = null; + + if (json.containsKey("\$type")) objectType = _typeInformationRetriever + .getClassGeneratedMapByQualifiedName(json["\$type"]) + .type; + else { + if (!json.containsKey("\$type") && + fromJsonHashCodesAndTypes.containsKey(json["\$ref"])) objectType = + fromJsonHashCodesAndTypes[json["\$ref"]]; + } + + return objectType; + } + + @override + void afterCreatingClassObjectData(ClassIntermediateObject classObjectData) { + if (!fromJsonHashCodesAndTypes + .containsKey(classObjectData.previousHashCode) && + classObjectData.objectType != null) fromJsonHashCodesAndTypes[ + classObjectData.previousHashCode] = classObjectData.objectType; + } +} diff --git a/lib/src/manipulators/base_class_iterator_data_manipulator.dart b/lib/src/manipulators/base_class_iterator_data_manipulator.dart index 7b78665..36121a5 100644 --- a/lib/src/manipulators/base_class_iterator_data_manipulator.dart +++ b/lib/src/manipulators/base_class_iterator_data_manipulator.dart @@ -1,31 +1,26 @@ part of nomirrorsmap.manipulators; -abstract class BaseClassIteratorDataManipulator implements BaseObjectDataManipulator -{ +abstract class BaseClassIteratorDataManipulator + implements BaseObjectDataManipulator { + void manipulate(BaseIntermediateObject baseObjectData) { + if (baseObjectData is ClassIntermediateObject) { + ClassIntermediateObject classObjectData = baseObjectData; - void manipulate( BaseObjectData baseObjectData ) - { - if ( baseObjectData is ClassObjectData ) - { - ClassObjectData classObjectData = baseObjectData; + var newProperties = {}; - var newProperties = { - }; + classObjectData.properties.forEach((k, v) { + newProperties[manipulatePropertyName(k)] = v; + manipulate(v); + }); - classObjectData.properties.forEach( ( k, v ) - { - newProperties[manipulatePropertyName( k )] = v; - manipulate( v ); - } ); + classObjectData.properties = newProperties; + } + if (baseObjectData is ListIntermediateObject) { + for (var value in baseObjectData.values) { + manipulate(value); + } + } + } - classObjectData.properties = newProperties; - } - if ( baseObjectData is ListObjectData ){ - for(var value in baseObjectData.values){ - manipulate(value); - } - } - } - - String manipulatePropertyName( String propertyName ) => propertyName; -} \ No newline at end of file + String manipulatePropertyName(String propertyName) => propertyName; +} diff --git a/lib/src/manipulators/base_object_data_manipulator.dart b/lib/src/manipulators/base_object_data_manipulator.dart index cbde9cc..bf8c0a5 100644 --- a/lib/src/manipulators/base_object_data_manipulator.dart +++ b/lib/src/manipulators/base_object_data_manipulator.dart @@ -1,5 +1,5 @@ part of nomirrorsmap.manipulators; -abstract class BaseObjectDataManipulator{ - void manipulate(BaseObjectData baseObjectData); -} \ No newline at end of file +abstract class BaseObjectDataManipulator { + void manipulate(BaseIntermediateObject baseObjectData); +} diff --git a/lib/src/manipulators/camel_case_manipulator.dart b/lib/src/manipulators/camel_case_manipulator.dart index 0377eb6..1058321 100644 --- a/lib/src/manipulators/camel_case_manipulator.dart +++ b/lib/src/manipulators/camel_case_manipulator.dart @@ -1,5 +1,6 @@ part of nomirrorsmap.manipulators; -class CamelCaseManipulator extends BaseClassIteratorDataManipulator{ - String manipulatePropertyName( String propertyName ) => propertyName[0].toLowerCase() + propertyName.substring(1); -} \ No newline at end of file +class CamelCaseManipulator extends BaseClassIteratorDataManipulator { + String manipulatePropertyName(String propertyName) => + propertyName[0].toLowerCase() + propertyName.substring(1); +} diff --git a/lib/src/manipulators/manipulators.dart b/lib/src/manipulators/manipulators.dart index c074d26..4ce38c0 100644 --- a/lib/src/manipulators/manipulators.dart +++ b/lib/src/manipulators/manipulators.dart @@ -1,10 +1,10 @@ library nomirrorsmap.manipulators; import 'package:nomirrorsmap/src/conversion_objects/conversion_objects.dart'; -import 'package:nomirrorsmap/src/shared/shared.dart'; +import 'package:nomirrorsmap/src/converters/converters.dart'; part 'base_class_iterator_data_manipulator.dart'; part 'base_object_data_manipulator.dart'; part 'camel_case_manipulator.dart'; part 'pascal_case_manipulator.dart'; -part 'type_to_type_manipulator.dart'; \ No newline at end of file +part 'type_to_type_manipulator.dart'; diff --git a/lib/src/manipulators/pascal_case_manipulator.dart b/lib/src/manipulators/pascal_case_manipulator.dart index 3a3ebb0..0446bfc 100644 --- a/lib/src/manipulators/pascal_case_manipulator.dart +++ b/lib/src/manipulators/pascal_case_manipulator.dart @@ -1,7 +1,6 @@ part of nomirrorsmap.manipulators; -class PascalCaseManipulator extends BaseClassIteratorDataManipulator -{ - String manipulatePropertyName( String propertyName ) - => propertyName[0].toUpperCase( ) + propertyName.substring( 1 ); -} \ No newline at end of file +class PascalCaseManipulator extends BaseClassIteratorDataManipulator { + String manipulatePropertyName(String propertyName) => + propertyName[0].toUpperCase() + propertyName.substring(1); +} diff --git a/lib/src/manipulators/type_to_type_manipulator.dart b/lib/src/manipulators/type_to_type_manipulator.dart index 9aa8698..4f31b19 100644 --- a/lib/src/manipulators/type_to_type_manipulator.dart +++ b/lib/src/manipulators/type_to_type_manipulator.dart @@ -1,52 +1,54 @@ part of nomirrorsmap.manipulators; -class TypeToTypeManipulator extends BaseObjectDataManipulator -{ - Type startType; - Map typeMaps; - - TypeToTypeManipulator( this.startType, [Map typeMaps = null] ){ - this.typeMaps = typeMaps == null ? {} : typeMaps; - } - - void manipulate( BaseObjectData baseObjectData ) - { - _manipulate( startType, baseObjectData ); - } - - void _manipulate( Type toType, BaseObjectData baseObjectData ) - { - toType = _getMappedType(baseObjectData, toType); - if ( baseObjectData is ClassObjectData ) - { - var classGeneratedMap = GeneratedMapProvider.getClassGeneratedMap( toType ); - if(classGeneratedMap.isAbstract){ - throw 'Are you missing a type map from "class ${baseObjectData.objectType}" to "abstract class $toType"'; - } - - - ClassObjectData classObjectData = baseObjectData; - - classObjectData.objectType = toType; - - classObjectData.properties.forEach( ( k, v ) - { - _manipulate( classGeneratedMap.properties[k].type, v ); - } ); - } - if ( baseObjectData is ListObjectData ) - { - var listGeneratedMap = GeneratedMapProvider.getListGeneratedMap( toType ); - for ( var value in baseObjectData.values ) - { - _manipulate( listGeneratedMap.innerType, value ); - } - } - } - - Type _getMappedType(BaseObjectData baseObjectData, Type type){ - if(typeMaps.containsKey(baseObjectData.objectType)) - return typeMaps[baseObjectData.objectType]; - return type; - } -} \ No newline at end of file +class TypeToTypeManipulator extends BaseObjectDataManipulator { + Type startType; + Map typeMaps; + + TypeInformationRetriever get _typeInformationRetriever => + TypeInformationRetrieverLocator.instance; + + TypeToTypeManipulator(this.startType, [Map typeMaps = null]) { + this.typeMaps = typeMaps == null ? {} : typeMaps; + } + + void manipulate(BaseIntermediateObject baseObjectData) { + _manipulate(startType, baseObjectData); + } + + void _manipulate(Type toType, BaseIntermediateObject baseObjectData) { + toType = _getMappedType(baseObjectData, toType); + if (baseObjectData is ClassIntermediateObject) { + var classGeneratedMap = + _typeInformationRetriever.getClassGeneratedMapWithNoCheck(toType); + if (classGeneratedMap == null || classGeneratedMap.instantiate == null) { + throw 'Are you missing a type map from "class ${baseObjectData.objectType}" to "abstract class $toType" or a @Mappable() attribute on "class ${baseObjectData.objectType}"'; + } + + ClassIntermediateObject classObjectData = baseObjectData; + + classObjectData.objectType = toType; + + classObjectData.properties.forEach((k, v) { + if (classGeneratedMap.fields + .any((p) => p.fieldMapping.name == k)) _manipulate( + classGeneratedMap.fields + .firstWhere((p) => p.fieldMapping.name == k) + .type, + v); + }); + } + if (baseObjectData is ListIntermediateObject) { + var listGeneratedMap = + _typeInformationRetriever.getClassGeneratedMapByListType(toType); + for (var value in baseObjectData.values) { + _manipulate(listGeneratedMap.type, value); + } + } + } + + Type _getMappedType(BaseIntermediateObject baseObjectData, Type type) { + if (typeMaps.containsKey(baseObjectData.objectType)) return typeMaps[ + baseObjectData.objectType]; + return type; + } +} diff --git a/lib/src/shared/shared.dart b/lib/src/shared/shared.dart index 90ab13e..232dee0 100644 --- a/lib/src/shared/shared.dart +++ b/lib/src/shared/shared.dart @@ -1,126 +1,31 @@ library nomirrorsmap.shared; -class TypeOf{ - Type get type => T; - - const TypeOf(); -} +import 'package:nomirrorsmap/src/conversion_objects/conversion_objects.dart'; -class EnumGeneratedMap extends GeneratedMap -{ - EnumGeneratedMap(Type type, this.values){ - this.type = type; - } +class CustomClassConverter { + Function _fromFunc; - List values; -} + set from(BaseIntermediateObject func(TActualType val)) { + _fromFunc = func; + } -class ClassGeneratedMap extends InstanceGeneratedMap -{ - ClassGeneratedMap(Type type, this.qualifiedName, dynamic initialize(), this.properties, [bool isAbstract = false]){ - this.type = type; - this.initialize = initialize; - this.isAbstract = isAbstract; - } + Function get from => _fromFunc; - String qualifiedName; - Map properties; -} + Function _toFunc; -class ListGeneratedMap extends InstanceGeneratedMap -{ - ListGeneratedMap(Type type, this.innerType, dynamic initialize(), [bool isAbstract = false]){ - this.type = type; - this.initialize = initialize; - this.isAbstract = isAbstract; - } + set to(TActualType func(BaseIntermediateObject val)) { + _toFunc = func; + } - Type innerType; + Function get to => _toFunc; } -abstract class InstanceGeneratedMap extends GeneratedMap{ - bool isAbstract; - - //initialize() - Function initialize; -} - -abstract class GeneratedMap{ - Type type; -} - -class GeneratedPropertyMap{ - GeneratedPropertyMap( this.type, dynamic getValue( dynamic obj ), void setValue( dynamic obj, dynamic value ) ){ - this.getValue = getValue; - this.setValue = setValue; - } - - Type type; - - //getValue( Object obj ); - Function getValue; - - //setValue( Object obj, Object value ); - Function setValue; +class Mappable { + const Mappable(); } -class CustomClassConverter -{ - Function _fromFunc; - - set from( TConvertedType func( TActualType val ) ) - { - _fromFunc = func; - } - - Function get from - => _fromFunc; - - Function _toFunc; - - set to( TActualType func( TConvertedType val ) ) - { - _toFunc = func; - } +class TypeOf { + const TypeOf(); - Function get to - => _toFunc; + Type get type => T; } - -class GeneratedMapProvider{ - static List _maps = []; - - static ClassGeneratedMap getClassGeneratedMap(Type type){ - if(_maps.where((m) => m is ClassGeneratedMap).any((m) => m.type == type)) - return _maps.where((m) => m is ClassGeneratedMap).firstWhere((m) => m.type == type ); - throw "Can't find map for type '${type.toString()}' is it missing the @Map() annotation "; - } - - static ClassGeneratedMap getClassGeneratedMapByQualifiedName( String qualifiedName ) - { - if(_maps.where((m) => m is ClassGeneratedMap).any((m) => m.qualifiedName == qualifiedName )) - return _maps.where((m) => m is ClassGeneratedMap).firstWhere((m) => m.qualifiedName == qualifiedName); - throw "Can't find map for type '$qualifiedName' is it missing the @Map() annotation "; - } - - static ListGeneratedMap getListGeneratedMap(Type type){ - if(_maps.where((m) => m is ListGeneratedMap).any((m) => m.type == type)) - return _maps.where((m) => m is ListGeneratedMap).firstWhere((m) => m.type == type); - throw "Can't find map for type '${type.toString()}' is it missing the @Map() annotation "; - } - - static EnumGeneratedMap getEnumGeneratedMap(Type type){ - if(_maps.where((m) => m is EnumGeneratedMap).any((m) => m.type == type)) - return _maps.where((m) => m is EnumGeneratedMap).firstWhere((m) => m.type == type); - throw "Can't find map for type '${type.toString()}' is it missing the @Map() annotation "; - } - - static bool containsEnumGeneratedMap(Type type){ - return _maps.where((m) => m is EnumGeneratedMap).any((m) => m.type == type); - } - - static void addMaps( List maps ) - { - _maps.addAll(maps); - } -} \ No newline at end of file diff --git a/lib/src/transformer/generators/class_bottom_generator.dart b/lib/src/transformer/generators/class_bottom_generator.dart new file mode 100644 index 0000000..4f826b7 --- /dev/null +++ b/lib/src/transformer/generators/class_bottom_generator.dart @@ -0,0 +1,8 @@ +part of nomirrorsmap.transformer; + +class _ClassBottomGenerator implements _Generator { + @override + String generate(_GeneratorParameters parameters) { + return '''}'''; + } +} diff --git a/lib/src/transformer/generators/class_generator.dart b/lib/src/transformer/generators/class_generator.dart index ad0e911..8513b50 100644 --- a/lib/src/transformer/generators/class_generator.dart +++ b/lib/src/transformer/generators/class_generator.dart @@ -1,32 +1,55 @@ -part of nomirrorsmap.generators; +part of nomirrorsmap.transformer; -class ClassGenerator implements Generator{ - final TypeHelper _typeHelper; +class _ClassGenerator extends _Generator with _TypeInformationRetriever { + @override + String generate(_GeneratorParameters parameters) { + var stringBuilder = new StringBuffer(); + stringBuilder.write('''static void _registerClasses() + {'''); - ClassGenerator(this._typeHelper); + for (var type in parameters.typesToMap.where((type) => !type.isEnum)) { + var fullTypeName = type.library.displayName; + if (fullTypeName.length > 0) fullTypeName += "."; + fullTypeName += type.displayName; - @override - bool isApplicable(element) => !(element is FieldElementImpl) && element is ClassElement && !element.isAbstract; + var importedTypeName = + _getImportTypeName(parameters.libraryImportAliases, type); + var hasDefaultConstructor = _typeHasConstructor(type); + var constructor = + hasDefaultConstructor ? "() => new $importedTypeName()" : "null"; - //Should return noticed types - @override - List process(element, StringBuffer fileContent) { - var seenTypes = []; - - fileContent.write( "new nomirrorsmap.ClassGeneratedMap( ${_typeHelper.getTypeString(element)}, \"${_typeHelper.getFullTypeName(element)}\", ${_typeHelper.getInstantiationFunc(element)}, {\n" ); - - var currentElement = element; - do { - for (var field in currentElement.fields) { - seenTypes.add(field); - fileContent.write("'${field.displayName}': new nomirrorsmap.GeneratedPropertyMap( ${_typeHelper.getTypeString(field)}, (obj) => obj.${field.displayName}, (obj, value) => obj.${field.displayName} = value ),\n"); - } - currentElement = currentElement.supertype.element; + stringBuilder.writeln( + "NoMirrorsMapStore.registerClass( \"$fullTypeName\", $importedTypeName, const TypeOf>().type, $constructor, {"); + + _outputFields(type, parameters, stringBuilder); + + stringBuilder.writeln("} );"); } - while(currentElement != null && !currentElement.library.name.startsWith("dart.core")); - fileContent.write( "}),\n" ); - return seenTypes; + stringBuilder.write("}"); + return stringBuilder.toString(); + } + + String _getImportTypeName( + UnmodifiableMapView libraryImportAliases, + ClassElement type) { + if (libraryImportAliases + .containsKey(type.library)) return libraryImportAliases[type.library] + + "." + + type.displayName; + return type.displayName; + } + + void _outputFields(Element type, _GeneratorParameters parameters, + StringBuffer stringBuilder) { + var fields = _getAllTypeFields(type, parameters).toList(); + for (var field in fields) { + var typeText = field.typeText; + if (typeText.contains("<")) typeText = "const TypeOf<$typeText>().type"; + + stringBuilder.write("'${field.name}': $typeText"); + if (fields.last != field) stringBuilder.writeln(","); + } } -} \ No newline at end of file +} diff --git a/lib/src/transformer/generators/class_top_generator.dart b/lib/src/transformer/generators/class_top_generator.dart new file mode 100644 index 0000000..e432393 --- /dev/null +++ b/lib/src/transformer/generators/class_top_generator.dart @@ -0,0 +1,31 @@ +part of nomirrorsmap.transformer; + +class _ClassTopGenerator implements _Generator { + final Resolver _resolver; + + _ClassTopGenerator(this._resolver); + + @override + String generate(_GeneratorParameters parameters) { + var stringBuilder = new StringBuffer(); + stringBuilder.writeln("library ${parameters.mappingsClassName};"); + stringBuilder.writeln("import 'package:nomirrorsmap/nomirrorsmap.dart';"); + + for (var library in parameters.libraryImportAliases.keys) { + var importPath = + _resolver.getImportUri(library, from: parameters.assetId); + stringBuilder.writeln( + "import '$importPath' as ${parameters.libraryImportAliases[library]};"); + } + + return '''${stringBuilder.toString( )} +class ${parameters.mappingsClassName} +{ + static void register( ) + { + _registerFields( ); + _registerClasses( ); + _registerEnums( ); + }'''; + } +} diff --git a/lib/src/transformer/generators/enum_generator.dart b/lib/src/transformer/generators/enum_generator.dart deleted file mode 100644 index 0895a0e..0000000 --- a/lib/src/transformer/generators/enum_generator.dart +++ /dev/null @@ -1,18 +0,0 @@ -part of nomirrorsmap.generators; - -class EnumGenerator implements Generator{ - final TypeHelper _typeHelper; - - EnumGenerator(this._typeHelper); - - @override - bool isApplicable(element) => element.type.element.isEnum; - - @override - List process(element, StringBuffer fileContent) { - fileContent.write( "new nomirrorsmap.EnumGeneratedMap( "); - fileContent.write( "${_typeHelper.getTypeString( element )}, "); - fileContent.write( "${_typeHelper.getTypeString( element )}.values ),\n" ); - return []; - } -} \ No newline at end of file diff --git a/lib/src/transformer/generators/enums_generator.dart b/lib/src/transformer/generators/enums_generator.dart new file mode 100644 index 0000000..37ced1e --- /dev/null +++ b/lib/src/transformer/generators/enums_generator.dart @@ -0,0 +1,21 @@ +part of nomirrorsmap.transformer; + +class _EnumsGenerator implements _Generator { + @override + String generate(_GeneratorParameters parameters) { + var stringBuilder = new StringBuffer(); + stringBuilder.write('''static void _registerEnums() + {'''); + + for (var type in parameters.typesToMap.where((type) => type.isEnum)) { + var importedTypeName = parameters.libraryImportAliases[type.library] + + "." + + type.displayName; + stringBuilder.writeln( + "NoMirrorsMapStore.registerEnum( $importedTypeName, $importedTypeName.values );"); + } + + stringBuilder.write("}"); + return stringBuilder.toString(); + } +} diff --git a/lib/src/transformer/generators/field.dart b/lib/src/transformer/generators/field.dart new file mode 100644 index 0000000..f30c4d9 --- /dev/null +++ b/lib/src/transformer/generators/field.dart @@ -0,0 +1,6 @@ +part of nomirrorsmap.transformer; + +class _Field { + String name; + String typeText; +} diff --git a/lib/src/transformer/generators/generator.dart b/lib/src/transformer/generators/generator.dart index 28c8ee4..bdc9cf7 100644 --- a/lib/src/transformer/generators/generator.dart +++ b/lib/src/transformer/generators/generator.dart @@ -1,6 +1,5 @@ -part of nomirrorsmap.generators; +part of nomirrorsmap.transformer; -abstract class Generator{ - bool isApplicable(dynamic element); - List process(dynamic element, StringBuffer fileContent); -} \ No newline at end of file +abstract class _Generator { + String generate(_GeneratorParameters parameters); +} diff --git a/lib/src/transformer/generators/generator_parameters.dart b/lib/src/transformer/generators/generator_parameters.dart new file mode 100644 index 0000000..26b6864 --- /dev/null +++ b/lib/src/transformer/generators/generator_parameters.dart @@ -0,0 +1,17 @@ +part of nomirrorsmap.transformer; + +class _GeneratorParameters { + final String mappingsClassName; + final AssetId assetId; + final UnmodifiableListView typesToMap; + final UnmodifiableMapView libraryImportAliases; + + _GeneratorParameters( + this.mappingsClassName, + this.assetId, + List typesToMap, + Map libraryImportAliases) + : this.typesToMap = new UnmodifiableListView(typesToMap), + this.libraryImportAliases = new UnmodifiableMapView(libraryImportAliases); +} diff --git a/lib/src/transformer/generators/generators.dart b/lib/src/transformer/generators/generators.dart deleted file mode 100644 index e16c886..0000000 --- a/lib/src/transformer/generators/generators.dart +++ /dev/null @@ -1,10 +0,0 @@ -library nomirrorsmap.generators; - -import 'package:analyzer/src/generated/element.dart'; -import 'package:code_transformers/resolver.dart'; - -part 'generator.dart'; -part 'list_generator.dart'; -part 'enum_generator.dart'; -part 'class_generator.dart'; -part 'type_helper.dart'; \ No newline at end of file diff --git a/lib/src/transformer/generators/list_generator.dart b/lib/src/transformer/generators/list_generator.dart deleted file mode 100644 index 320891d..0000000 --- a/lib/src/transformer/generators/list_generator.dart +++ /dev/null @@ -1,20 +0,0 @@ -part of nomirrorsmap.generators; - -class ListGenerator implements Generator{ - final DartType _listType; - final TypeHelper _typeHelper; - - ListGenerator(Resolver resolver, this._typeHelper) : _listType = resolver.getType( "dart.core.List" ).type; - - @override - bool isApplicable(dynamic element) => _listType == element.type.element.type || element.type.isSubtypeOf( _listType ); - - @override - List process(dynamic element, StringBuffer fileContent) { - fileContent.write( "new nomirrorsmap.ListGeneratedMap(" ); - fileContent.write( " ${_typeHelper.getTypeString( element )},"); - fileContent.write( " ${_typeHelper.getTypeString( element.type.typeArguments.first.element )},"); - fileContent.write( " ${_typeHelper.getInstantiationFunc( element )} ),\n" ); - return []; - } -} \ No newline at end of file diff --git a/lib/src/transformer/generators/properties_generator.dart b/lib/src/transformer/generators/properties_generator.dart new file mode 100644 index 0000000..7ea97f6 --- /dev/null +++ b/lib/src/transformer/generators/properties_generator.dart @@ -0,0 +1,24 @@ +part of nomirrorsmap.transformer; + +class _FieldsGenerator extends _Generator with _TypeInformationRetriever { + @override + String generate(_GeneratorParameters parameters) { + var stringBuilder = new StringBuffer(); + stringBuilder.write('''static void _registerFields() + {'''); + + var fieldNames = parameters.typesToMap + .where((type) => !type.isEnum) + .expand((type) => _getAllTypeFields(type, parameters)) + .map((field) => field.name) + .toList(); + + for (var field in TransformerHelpers.uniquifyList(fieldNames)) { + stringBuilder.writeln( + '''NoMirrorsMapStore.registerField( "$field", ( object, value ) => object.$field = value, (object) => object.$field );'''); + } + + stringBuilder.write("\t}"); + return stringBuilder.toString(); + } +} diff --git a/lib/src/transformer/generators/type_helper.dart b/lib/src/transformer/generators/type_helper.dart deleted file mode 100644 index 789c908..0000000 --- a/lib/src/transformer/generators/type_helper.dart +++ /dev/null @@ -1,50 +0,0 @@ -part of nomirrorsmap.generators; - -class TypeHelper{ - final Map _libraryImportNames; - - TypeHelper(this._libraryImportNames); - - String getInstantiationFunc(Element element){ - return "() => new ${_getTypeString(element)}()"; - } - - String getTypeString(Element element){ - var result = _getTypeString( element ); - if ( result.contains( "<" ) ) - { - return "const nomirrorsmap.TypeOf<$result>().type"; - } - return result; - } - - String _getTypeString( dynamic element ) - { - var result = ""; - if ( _libraryImportNames.containsKey( element.type.element.library ) ) - result = "${_libraryImportNames[element.type.element.library]}.${element.type}"; - else - result = element.type.name; - - if ( element.type is TypeParameterTypeImpl || element.type is DynamicTypeImpl ) - { - print( "Type parameter found: ${element.type}" ); - return result; - } - if ( element.type.typeArguments.length > 0 ) - { - result += "<"; - result += element.type.typeArguments.map( ( a ) => _getTypeString( a.element ) ).join( "," ); - result += ">"; - } - - return result; - - } - - String getFullTypeName(dynamic element){ - if ( element.type.element.library.displayName == "" ) - return element.displayName; - return "${element.type.element.library.displayName}.${element.displayName}"; - } -} \ No newline at end of file diff --git a/lib/src/transformer/generators/type_information_retriever.dart b/lib/src/transformer/generators/type_information_retriever.dart new file mode 100644 index 0000000..4cb81ac --- /dev/null +++ b/lib/src/transformer/generators/type_information_retriever.dart @@ -0,0 +1,94 @@ +part of nomirrorsmap.transformer; + +class _TypeInformationRetriever { + Iterable<_Field> _getAllTypeFields( + ClassElement type, _GeneratorParameters parameters) sync* { + bool isObject(InterfaceType type) => + type == null || type.isObject || type.displayName == "Object"; + + yield* _getOnlyAccessibleAndUsageFields(type.fields).map((field) { + return new _Field() + ..name = field.name + ..typeText = field.type is TypeParameterTypeImpl + ? "dynamic" + : _getPropertyType(type.name, field.name, field.type, parameters); + }); + + for (var mixin + in type.mixins) yield* _getAllTypeFields(mixin.element, parameters); + + if (!isObject(type.supertype)) { + for (var currentType = type.supertype; + !isObject(currentType); + currentType = currentType.element.supertype) { + var genericParameters = {}; + if (currentType.typeArguments.length > 0) { + for (var generic in currentType.typeArguments) { + genericParameters[currentType.element.typeParameters[ + currentType.typeArguments.indexOf(generic)]] = generic; + } + } + + for (var field + in _getOnlyAccessibleAndUsageFields(currentType.element.fields)) { + var type = field.type; + if (type is TypeParameterType) type = genericParameters[type.element]; + if (type is InterfaceTypeImpl) type = type; + + yield new _Field() + ..name = field.name + ..typeText = _getPropertyType( + currentType.element.name, field.name, type, parameters); + } + } + } + } + + List _getOnlyAccessibleAndUsageFields( + List fields) { + return fields + .where((field) => + field.setter != null && field.getter != null && field.isPublic) + .toList(); + } + + String _getPropertyType(String containTypeName, String propertyName, + InterfaceType type, _GeneratorParameters parameters) { + try { + return _getActualTypeText(type, parameters); + } catch (ex) { + throw "In the type '$containTypeName' for the property '$propertyName' of type '${type.name}', could not generate the type text"; + } + } + + String _getActualTypeText( + InterfaceType type, _GeneratorParameters parameters) { + var typeName = type.name; + if (parameters.libraryImportAliases + .containsKey(type.element.library)) typeName = + parameters.libraryImportAliases[type.element.library] + "." + typeName; + + if (type.typeArguments.length > 0) { + var genericPart = "<${type.typeArguments.map( ( typeArgument ) + { + if ( typeArgument is DynamicTypeImpl ) + return "dynamic"; + return _getActualTypeText( typeArgument, parameters ); + } ).join( "," )}>"; + if (genericPart != "") typeName += genericPart; + } + + return typeName; + } + + bool _typeHasConstructor(ClassElement type) { + if (type.unnamedConstructor != null && + type.unnamedConstructor.parameters.length > 0 && + type.unnamedConstructor.parameters + .every((p) => p.parameterKind.isOptional)) return true; + return type.constructors + .any((constructor) => constructor.parameters.length == 0) && + !type.isAbstract && + type.library.name != "dart.core"; + } +} diff --git a/lib/src/transformer/map_generator.dart b/lib/src/transformer/map_generator.dart deleted file mode 100644 index 122d662..0000000 --- a/lib/src/transformer/map_generator.dart +++ /dev/null @@ -1,130 +0,0 @@ -part of nomirrorsmap.transformer; - -class MapGenerator -{ - Resolver _resolver; - DartType _mapType; - - MapGenerator( Resolver resolver ) - { - _resolver = resolver; - _mapType = resolver.getType( "nomirrorsmap.MapType" ).type; - } - - List _typesToGenerate = []; - - void addTypes( List types ) - { - _typesToGenerate.addAll( types.where( _shouldBeMapped ).toList( ) ); - } - - String buildMapFile( AssetId assetId ) - { - var mapFileContent = new StringBuffer( ); - - _appendHeader( mapFileContent, assetId ); - - var libraryImportNames = _getLibraryImportNames( assetId ); - _appendLibraryImports( libraryImportNames, mapFileContent, assetId ); - - mapFileContent.write( "\n" ); - - mapFileContent.write( '''class NoMirrorsMapGeneratedMaps{ - static List load(){ - return [\n''' ); - List seenTypes = []; - List typesToRun = _typesToGenerate.toList( ); - - var typeHelper = new TypeHelper(libraryImportNames); - - var resolvers = [new ListGenerator(_resolver, typeHelper), - new EnumGenerator(typeHelper), - new ClassGenerator(typeHelper)]; - - do - { - for ( dynamic element in typesToRun.toList( ) ) - { - if ( _isNotDartCoreType(element) && !seenTypes.contains( element ) && !(element.type.element is TypeParameterElementImpl) ) - { - if(resolvers.any((r) => r.isApplicable(element))){ - typesToRun.addAll(resolvers.firstWhere((r) => r.isApplicable(element)).process(element, mapFileContent)); - } - } - seenTypes.add( element ); - } - } - while ( typesToRun.where( ( t ) => !seenTypes.contains( t ) ).length > 0 ); - - mapFileContent.write( ''']; - } -}''' ); - - return mapFileContent.toString( ); - } - - bool _isNotDartCoreType(dynamic element){ - return !(element.type.element.library.name.startsWith( 'dart.core' ) && _isPrimitiveTypeName( element.type.name )); - } - - bool _isPrimitiveTypeName( String name ) - { - return name == "String" || name == "int" || name == "double" || name == "num" || name == "bool" || name == "int" ; - } - - void _appendLibraryImports( Map libraryImportNames, StringBuffer mapFileContent, AssetId assetId ) - { - libraryImportNames.forEach( ( library, importName ) - { - var importPath = _resolver.getImportUri( library, from: assetId ); - mapFileContent.write( 'import "$importPath" as $importName;\n' ); - } ); - } - - Map _getLibraryImportNames( AssetId assetId ) - { - List uniqueLibraries = []; - _typesToGenerate.map( ( e ) - => e.library ).forEach( ( e ) - { - if ( !uniqueLibraries.contains( e ) ) - uniqueLibraries.add( e ); - } ); - - Map libraryImportNames = {}; - uniqueLibraries.forEach( ( l ) - { - var importAs = _resolver.getImportUri( l, from: assetId ).toString( ).replaceAll( ".", "_" ).replaceAll( "/", "_" ).replaceAll( "package:", "" ); - if ( importAs == "" ) - importAs = l.displayName; - libraryImportNames[l] = importAs; - } ); - return libraryImportNames; - } - - bool _shouldBeMapped( ClassElement element ) - { - if ( element.isEnum ) - return true; - for ( var meta in element.metadata ) - { - if ( meta.element is ConstructorElement ) - { - DartType metaType = meta.element.enclosingElement.type; - if ( metaType.isAssignableTo( _mapType ) ) - { - if ( element.unnamedConstructor == null ) - throw "The type '${element.displayName}' has a @Map() annotation but no DefaultConstructor"; - return true; - } - } - } - return false; - } - - void _appendHeader( StringBuffer stringBuffer, AssetId assetId ) - { - stringBuffer.write( "library ${path.url.basenameWithoutExtension( assetId.path )}_nomirrorsmap_generated_maps;\n\n" ); - stringBuffer.write( "import 'package:nomirrorsmap/src/shared/shared.dart' as nomirrorsmap;\n" ); - } -} \ No newline at end of file diff --git a/lib/src/transformer/map_generator_transformer.dart b/lib/src/transformer/map_generator_transformer.dart index 9fa5be0..d534963 100644 --- a/lib/src/transformer/map_generator_transformer.dart +++ b/lib/src/transformer/map_generator_transformer.dart @@ -1,75 +1,115 @@ part of nomirrorsmap.transformer; -class MapGeneratorTransformer extends Transformer with ResolverTransformer -{ - MapGeneratorTransformer( Resolvers resolvers ) - { - this.resolvers = resolvers; - } - - Future shouldApplyResolver( Asset asset ) - => new Future.value( true ); - - void applyResolver( Transform transform, Resolver resolver ) - { - if ( resolver.getType( "nomirrorsmap.MapType" ) != null ) - { - var id = transform.primaryInput.id; - var outputPath = path.url.join( path.url.dirname( id.path ), "${path.url.basenameWithoutExtension( id.path )}_nomirrorsmap_generated_maps.dart" ); - var generatedAssetId = new AssetId( id.package, outputPath ); - - var mapFile = (new MapGenerator( resolver ) - ..addTypes( resolver.libraries - .expand( ( lib ) - => lib.units ) - .expand( ( compilationUnit ) - => compilationUnit.types ).toList( ) )) - .buildMapFile( generatedAssetId ); - - - transform.addOutput( - new Asset.fromString( generatedAssetId, mapFile ) ); - - _editMain( transform, resolver ); - } - } - - void _editMain( Transform transform, Resolver resolver ) - { - AssetId id = transform.primaryInput.id; - var lib = resolver.getLibrary( id ); - var unit = lib.definingCompilationUnit.node; - var transaction = resolver.createTextEditTransaction( lib ); - - var imports = unit.directives.where( ( d ) - => d is ImportDirective ); - transaction.edit( imports.last.end, imports.last.end, '\nimport ' - "'${path.url.basenameWithoutExtension( id.path )}" - "_nomirrorsmap_generated_maps.dart' show NoMirrorsMapGeneratedMaps;\n" - "import 'package:nomirrorsmap/src/shared/shared.dart' as nomirrorsmap;\n" ); - - FunctionExpression main = unit.declarations.where( ( d ) - => - d is FunctionDeclaration && d.name.toString( ) == 'main' ) - .first.functionExpression; - var body = main.body; - if ( body is BlockFunctionBody ) - { - var location = body.beginToken.end; - transaction.edit( location, location, '\n nomirrorsmap.GeneratedMapProvider.addMaps(NoMirrorsMapGeneratedMaps.load());' ); - } else if ( body is ExpressionFunctionBody ) - { - transaction.edit( body.beginToken.offset, body.endToken.end, - "{\n nomirrorsmap.GeneratedMapProvider.addMaps(NoMirrorsMapGeneratedMaps.load());\n" - " return ${body.expression};\n}" ); - } - // EmptyFunctionBody can only appear as abstract methods and constructors. - - var printer = transaction.commit( ); - var url = id.path.startsWith( 'lib/' ) ? - 'package:${id.package}/${id.path.substring( 4 )}' : id.path; - printer.build( url ); - transform.addOutput( new Asset.fromString( id, printer.text ) ); - } +class MapGeneratorTransformer extends Transformer with ResolverTransformer { + final TransformerOptions _options; + + MapGeneratorTransformer(Resolvers resolvers, this._options) { + this.resolvers = resolvers; + } + + void applyResolver(Transform transform, Resolver resolver) { + var id = transform.primaryInput.id; + + var filePrefix = TransformerHelpers.sanitizePathToUsableImport(id.path); + var mappingsClassName = + TransformerHelpers.sanitizePathToUsableClassName(id.path) + "Mappings"; + + var mappingsFileName = "${filePrefix}_mappings.dart"; + var outputPath = path.url.join(path.url.dirname(id.path), mappingsFileName); + var generatedAssetId = new AssetId(id.package, outputPath); + + _transformEntryFile( + transform, resolver, mappingsFileName, mappingsClassName); + + var mappingsFile = new MappingsGenerator(resolver, id) + .generate(mappingsClassName, _options.libraryNames); + + transform.addOutput(new Asset.fromString(generatedAssetId, mappingsFile)); + } + + void _transformEntryFile(Transform transform, Resolver resolver, + String mappingsFileName, String mappingsClassName) { + AssetId id = transform.primaryInput.id; + var lib = resolver.getLibrary(id); + var unit = lib.definingCompilationUnit.computeNode(); + var transaction = resolver.createTextEditTransaction(lib); + + var importParameters = _getImportParameters(unit); + + for (var directive in unit.directives) { + if (directive is ImportDirective && + directive.uri.stringValue == + 'package:nomirrorsmap/nomirrorsmap_mirrors.dart') { + transaction.edit(directive.beginToken.offset, directive.end, ''); + } + } + + transaction.edit( + importParameters.startPoint, + importParameters.startPoint, + '${importParameters.importStart}import "$mappingsFileName" as $mappingsClassName;' + + (importParameters.startPoint == 0 ? "\n" : "")); + + FunctionExpression main = unit.declarations + .where((d) => d is FunctionDeclaration && d.name.toString() == 'main') + .first + .functionExpression; + var body = main.body; + + if (body is BlockFunctionBody) { + Iterable methodInvocations = body.block.statements + .where((statement) => statement is ExpressionStatement && + statement.expression is MethodInvocation) + .map((statement) => statement.expression); + if (methodInvocations + .any((method) => method.methodName.toString() == "useMirrors")) { + var methodInvocation = methodInvocations.firstWhere( + (method) => method.methodName.toString() == "useMirrors"); + transaction.edit( + methodInvocation.beginToken.offset, methodInvocation.end + 1, ''); + } + } + if (body is BlockFunctionBody) { + var location = body.beginToken.end; + transaction.edit(location, location, + '\n\t$mappingsClassName.$mappingsClassName.register();\n'); + } else if (body is ExpressionFunctionBody) { + transaction.edit( + body.beginToken.offset, + body.endToken.end, + "{\n\t$mappingsClassName.$mappingsClassName.register();\n" + "\treturn ${body.expression};\n}"); + } + + var printer = transaction.commit(); + printer.build(id.path); + transform.addOutput(new Asset.fromString(id, printer.text)); + } + + _EntryPointImportParameters _getImportParameters(dynamic unit) { + List imports = + unit.directives.where((d) => d is ImportDirective).toList(); + + var result = new _EntryPointImportParameters() + ..startPoint = 0 + ..importStart = ""; + + if (imports.length > 0) { + result.importStart = "\n"; + result.startPoint = imports.last.end; + } else { + List libraries = + unit.directives.where((d) => d is LibraryDirective).toList(); + if (libraries.length > 0) { + result.importStart = "\n\n"; + result.startPoint = libraries.last.end; + } + } + return result; + } } +class _EntryPointImportParameters { + int startPoint; + String importStart; +} diff --git a/lib/src/transformer/mappings_generator.dart b/lib/src/transformer/mappings_generator.dart new file mode 100644 index 0000000..8fb6a72 --- /dev/null +++ b/lib/src/transformer/mappings_generator.dart @@ -0,0 +1,138 @@ +part of nomirrorsmap.transformer; + +class MappingsGenerator { + final Resolver _resolver; + final AssetId _assetId; + + List _typesToMap = []; + Map _libraryImportAliases = {}; + + MappingsGenerator(this._resolver, this._assetId); + + void _addTypes(List libraryNamesToInclude) { + var libraries = []; + for (var libraryName in libraryNamesToInclude) { + var library = _resolver.getLibraryByName(libraryName); + if (library == null) print( + "nomirrorsmap: '$libraryName' was not found so will be ignored"); + else libraries.add(library); + } + + var allTypes = _expandCompilationUnitsWhereShouldBeMapped( + (compilationUnit) => compilationUnit.enums); + + allTypes.addAll(_expandCompilationUnitsWhereShouldBeMapped( + (compilationUnit) => compilationUnit.types) + .where((type) => !type.isAbstract)); + + _typesToMap.addAll(_getTypesThatShouldBeMapped(allTypes, libraries)); + _typesToMap.addAll(_getListTypesFromPropertiesThatAreNotAlreadyMapped()); + + _generateLibraryAliases(); + } + + List _expandCompilationUnitsWhereShouldBeMapped( + Iterable expand(CompilationUnitElement unit)) { + return _resolver.libraries + .expand((lib) => lib.units) + .expand((compilationUnit) => expand(compilationUnit)) + .toList(); + } + + void _generateLibraryAliases() { + for (var type in _typesToMap) { + if (!_libraryImportAliases.containsKey(type.library)) { + var source = type.library.definingCompilationUnit.source; + if (source is! DartSourceProxy) { + var libraryFullPath = source.assetId.path as String; + var libraryImportAlias = + TransformerHelpers.sanitizePathToUsableImport(libraryFullPath); + _libraryImportAliases[type.library] = libraryImportAlias; + } + } + } + } + + Iterable _getTypesThatShouldBeMapped( + List types, List libraries) sync* { + var mappableMetadataType = + _resolver.getType("nomirrorsmap.shared.Mappable"); + for (var type in types) { + if (libraries.contains(type.library)) yield type; + if (mappableMetadataType != null) { + var metadata = type.metadata + .map((meta) => meta.element) + .where((element) => element is ConstructorElement) + .toList(); + if (type.isEnum) metadata = _getEnumMetaData(type); + + for (ConstructorElement meta in metadata) { + DartType metaType = meta.enclosingElement.type; + if (metaType.isAssignableTo(mappableMetadataType.type)) { + if (!type.isEnum && + (type.unnamedConstructor == null || + (type.unnamedConstructor.parameters.length > 0 && + type.unnamedConstructor.parameters.any((p) => !p + .parameterKind + .isOptional)))) throw "The type '${type.displayName}' has a @Mappable() annotation but no DefaultConstructor"; + yield type; + } + } + } + } + } + + //This is a hack to fix a bug in analyzer don't judge it + List _getEnumMetaData(ClassElement type) { + var annotations = type.library.units + .expand((u) => u + .computeNode() + .declarations + .where((d) => d is EnumDeclaration && d.name.name == type.name)) + .first + .metadata; + return annotations + .map((a) => a.element) + .where((element) => element is ConstructorElement) + .toList(); + } + + String generate(String className, List libraryNamesToInclude) { + if (libraryNamesToInclude == null) libraryNamesToInclude = []; + + _addTypes(libraryNamesToInclude); + + var generators = <_Generator>[ + new _ClassTopGenerator(_resolver), + new _FieldsGenerator(), + new _ClassGenerator(), + new _EnumsGenerator(), + new _ClassBottomGenerator() + ]; + + var parameters = new _GeneratorParameters( + className, _assetId, _typesToMap, _libraryImportAliases); + + var formatter = new DartFormatter(); + return formatter.format( + generators.map((generator) => generator.generate(parameters)).join()); + } + + bool _typeHasConstructor(ClassElement type) { + return type.constructors.any((ctor) => ctor.parameters.length == 0) && + !type.isAbstract; + } + + List _getListTypesFromPropertiesThatAreNotAlreadyMapped() { + return TransformerHelpers.uniquifyList(_typesToMap + .where((type) => !type.isEnum) + .expand((type) => type.fields) + .where((FieldElement field) => field.type.name == "List") + .map((field) => field.type) + .where((InterfaceType type) => + type is InterfaceType && type.typeArguments.length > 0) + .map((type) => type.typeArguments.first.element) + .where((ClassElement type) => !_typesToMap.contains(type)) + .toList()); + } +} diff --git a/lib/src/transformer/transformer.dart b/lib/src/transformer/transformer.dart index 0f27e4a..df36842 100644 --- a/lib/src/transformer/transformer.dart +++ b/lib/src/transformer/transformer.dart @@ -4,13 +4,22 @@ import 'package:barback/barback.dart'; import 'package:analyzer/src/generated/ast.dart'; import 'package:analyzer/src/generated/element.dart'; import 'package:code_transformers/resolver.dart'; -import 'package:nomirrorsmap/nomirrorsmap.dart'; import 'package:path/path.dart' as path; -import 'package:nomirrorsmap/src/transformer/generators/generators.dart'; -import 'dart:io'; - -import 'dart:async'; -import 'dart:math'; +import 'dart:collection'; +import 'package:dart_style/dart_style.dart'; part 'map_generator_transformer.dart'; -part 'map_generator.dart'; \ No newline at end of file +part 'transformer_options.dart'; + +part 'mappings_generator.dart'; +part 'transformer_helpers.dart'; + +part 'generators/class_bottom_generator.dart'; +part 'generators/class_generator.dart'; +part 'generators/class_top_generator.dart'; +part 'generators/enums_generator.dart'; +part 'generators/field.dart'; +part 'generators/generator.dart'; +part 'generators/generator_parameters.dart'; +part 'generators/properties_generator.dart'; +part 'generators/type_information_retriever.dart'; diff --git a/lib/src/transformer/transformer_helpers.dart b/lib/src/transformer/transformer_helpers.dart new file mode 100644 index 0000000..f458af3 --- /dev/null +++ b/lib/src/transformer/transformer_helpers.dart @@ -0,0 +1,24 @@ +part of nomirrorsmap.transformer; + +class TransformerHelpers { + static String sanitizePathToUsableImport(String path) { + return path.replaceAll("/", "_").replaceAll(".", "_").replaceAll(":", "_"); + } + + static String sanitizePathToUsableClassName(String path) { + var importName = sanitizePathToUsableImport(path); + return importName + .split("_") + .map((str) => str[0].toUpperCase() + str.substring(1)) + .join(); + } + + static List uniquifyList(List list) { + var result = []; + + for (var element + in list) if (!result.contains(element)) result.add(element); + + return result; + } +} diff --git a/lib/src/transformer/transformer_options.dart b/lib/src/transformer/transformer_options.dart new file mode 100644 index 0000000..e99ec3c --- /dev/null +++ b/lib/src/transformer/transformer_options.dart @@ -0,0 +1,34 @@ +part of nomirrorsmap.transformer; + +class TransformerOptions { + static const LIBRARY_NAMES_PARAM = "library_names"; + + final List libraryNames; + + TransformerOptions.initialize(this.libraryNames); + + factory TransformerOptions(BarbackSettings settings) { + return new TransformerOptions.initialize( + _readLibraryList(settings.configuration, LIBRARY_NAMES_PARAM)); + } + + static List _readLibraryList(Map config, String paramName) { + var value = config[paramName]; + if (value == null) return null; + var files = []; + bool error = false; + if (value is List) { + files = value; + error = value.any((e) => e is! String); + } else if (value is String) { + files = [value]; + error = false; + } else { + error = true; + } + if (error) { + print('Invalid value for "$paramName" in the nomirrorsmap transformer.'); + } + return files; + } +} diff --git a/lib/transformer.dart b/lib/transformer.dart new file mode 100644 index 0000000..478c38c --- /dev/null +++ b/lib/transformer.dart @@ -0,0 +1,23 @@ +import 'package:barback/barback.dart'; +import 'package:nomirrorsmap/src/transformer/transformer.dart'; +import 'package:code_transformers/resolver.dart'; + +class MainTransformer extends TransformerGroup { + MainTransformer._(phases) : super(phases) {} + + factory MainTransformer(TransformerOptions options) { + var resolvers = new Resolvers(dartSdkDirectory); + + var phases = [ + [new MapGeneratorTransformer(resolvers, options)] + ]; + + return new MainTransformer._(phases); + } + + factory MainTransformer.asPlugin(BarbackSettings settings) { + return new MainTransformer(new TransformerOptions(settings)); + } +} + +class LibraryTransformer {} diff --git a/pubspec.yaml b/pubspec.yaml index 0dd7cb0..c61ed63 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,14 @@ name: nomirrorsmap -version: 1.0.0-beta1 +version: 1.0.22 author: jrote1 -description: A multipurpose mapping library +description: A library for mapping without mirrors this includes JSON and Object to Object homepage: https://github.com/jrote1/nomirrorsmap.dart environment: sdk: '>=1.9.0 <2.0.0' dependencies: - barback: '>=0.12.0 <0.17.0' - code_transformers: '>=0.2.3 <0.3.0' + code_transformers: any + dart_style: any path: '>=1.0.0 <2.0.0' + reflective: '^0.0.23' dev_dependencies: unittest: any -transformers: -- nomirrorsmap diff --git a/test/all_test.dart b/test/all_test.dart new file mode 100644 index 0000000..d13889a --- /dev/null +++ b/test/all_test.dart @@ -0,0 +1,603 @@ +library nomirrorsmap.tests; + +import 'package:test/test.dart'; +import 'package:nomirrorsmap/nomirrorsmap.dart'; +import 'package:nomirrorsmap/nomirrorsmap_mirrors.dart'; +import 'package:nomirrorsmap/src/conversion_objects/conversion_objects.dart'; +import 'package:nomirrorsmap/src/shared/shared.dart'; + +import 'test_mappings.dart' as test_mappings; + +import 'type_to_type_objects.dart' as objects; +import 'test_objects.dart'; +import 'new_transformer_tests.dart'; + +part 'type_to_type_tests.dart'; + +main() async { + test_mappings.TestProjectMappings.register(); + + group("Type to Type", () => TypeToTypeTests.run()); + group("Transformer Main Modification", + () => MainModificationTransformerTests.run(getPhases())); + group("Transformer", () => TransformerTests.run()); + + var noMirrorsMapInstances = { + 'Mirrors Based': () { + useMirrors(); + }, + 'Mappings Based': () {} + }; + + for (var noMirrorsMapFuncKey in noMirrorsMapInstances.keys) { + var noMirrorsMap = new NoMirrorsMap(); + var doThingsFunction = noMirrorsMapInstances[noMirrorsMapFuncKey]; + group("${noMirrorsMapFuncKey} tests", () { + setUp(() { + doThingsFunction(); + }); + + group("Serialization Tests", () { + test("Can serialize an object that is null", () { + var result = noMirrorsMap.convert( + null, new ClassConverter(), new JsonConverter()); + expect(result, "null"); + }); + + test("Can serialize to Pascal case", () { + var result = noMirrorsMap.convert(new Person() + ..id = 1 + ..children = [] + ..parents = [], new ClassConverter(), new JsonConverter(), + [new PascalCaseManipulator()]); + expect(result, endsWith('''"Id":1,"Parents":[],"Children":[]}''')); + }); + + test("Performance test", () { + //218 + var list = []; + for (int i = 0; i < 1000; i++) list.add(new Person() + ..id = i + ..children = [ + new Person() + ..id = i + ..children = [] + ..parents = [] + ] + ..parents = [ + new Person() + ..id = i + ..children = [] + ..parents = [] + ]); + + var stopwatch = new Stopwatch()..start(); + noMirrorsMap.convert( + list, new ClassConverter(), new NewtonSoftJsonConverter()); + stopwatch.stop(); + print("Took: ${stopwatch.elapsedMilliseconds}"); + }); + + test("Generic test", () { + var person = new PersonGeneric()..val = new Person(); + var json = noMirrorsMap.convert( + person, new ClassConverter(), new NewtonSoftJsonConverter()); + noMirrorsMap.convert(json, new NewtonSoftJsonConverter(), + new ClassConverter(startType: PersonGeneric)); + }); + + test("Can deserialize to object", () { + ClassConverter.converters[Duration] = + new CustomClassConverter() + ..to = ((BaseIntermediateObject input) { + var classObjectData = input as ClassIntermediateObject; + return new Duration( + minutes: classObjectData.properties["minutes"].value, + seconds: classObjectData.properties["seconds"].value); + }) + ..from = ((Duration duration) { + return { + "minutes": duration.inMinutes, + "seconds": duration.inSeconds % 60 + }; + }); + + var json = r'''{ "duration": {"minutes":15, "seconds":10} }'''; + var result = noMirrorsMap.convert(json, new JsonConverter(), + new ClassConverter(startType: TypeWithDuration)) + as TypeWithDuration; + + expect(result.duration.inMinutes, 15); + expect(result.duration.inSeconds, 15 * 60 + 10); + }); + }); + + group("Deserialization Tests", () { + test("Can deserialize null DateTime", () { + var json = "null"; + + var result = noMirrorsMap.convert(json, new JsonConverter(), + new ClassConverter(startType: DateTime)) as Person; + + expect(result, isNull); + }); + + test("Can deserialize simple object structure", () { + var json = r'''{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "547833245", + "id": 1, + "parents": [], + "children": [] +}'''; + + var result = noMirrorsMap.convert( + json, new JsonConverter(), new ClassConverter()) as Person; + + expect(result.id, 1); + expect(result.children, isNotNull); + expect(result.parents, isNotNull); + }); + + test("Can deserialize objects with circular references", () { + var json = r'''{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "547833245", + "id": 3, + "parents": [{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "48854486", + "id": 1, + "parents": [], + "children": [{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "48854487", + "id": 2, + "parents": [{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "48854486" + }], + "children": [] + }] + }], + "children": [] +}'''; + + var result = noMirrorsMap.convert( + json, new JsonConverter(), new ClassConverter()) as Person; + + var parent = result.parents[0]; + + expect(parent.children[0].parents[0], parent); + }); + + test( + "Can deserialize objects with circular references, even if properties are seen after reference", + () { + var json = r'''{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "547833245", + "id": 3, + "parents": [{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "48854486", + "parents": [], + "children": [{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "48854487", + "id": 2, + "parents": [{ + "$type": "nomirrorsmap.test_objects.Person", + "$hashcode": "48854486", + "id": 1 + }], + "children": [] + }] + }], + "children": [] +}'''; + + var result = noMirrorsMap.convert( + json, new JsonConverter(), new ClassConverter()) as Person; + + var parent = result.parents[0]; + + expect(parent, parent.children[0].parents[0]); + expect(parent.id, 1); + }); + + test("Can deserialize objects that do not have \$type", () { + var json = "{\"id\": 1, \"children\": [], \"parents\": []}"; + + Person result = noMirrorsMap.convert( + json, new JsonConverter(), new ClassConverter(startType: Person)); + + expect(result.id, 1); + expect(result.children, isNotNull); + expect(result.parents, isNotNull); + }); + + test("Can deserialize null", () { + var result = noMirrorsMap.convert( + "null", new JsonConverter(), new ClassConverter()); + expect(result, null); + }); + + test("Can deserialize using CamelCaseManipulator", () { + var json = + '''{"\$type":"nomirrorsmap.test_objects.Person","\$hashcode":"511757599","Id":1,"Parents":[],"Children":[]}'''; + + Person result = noMirrorsMap.convert(json, new JsonConverter(), + new ClassConverter(), [new CamelCaseManipulator()]); + expect(result.id, 1); + }); + + test("Can deserialize type that contains a list", () { + var json = + "{\"id\": 1, \"children\": [{\"id\": 2,\"children\": [], \"parents\": []}], \"parents\": []}"; + + Person result = noMirrorsMap.convert( + json, new JsonConverter(), new ClassConverter(startType: Person)); + + expect(result.id, 1); + expect(result.children.length, 1); + expect(result.children[0].id, 2); + }); + + test("Can deserialize type that contains a list", () { + var json = r'''{ + "odata.metadata": "http://localhost/odata/$metadata#Users/@Element", + "Id": 2, + "FirstName": "No", + "LastName": "Mirrors", + "EmailAddress": "fsgpoidhnfoglb@example.com", + "MobilePhone": "65406834354356", + "Umpire": false, + "TeamUsers": [ + { + "Id": 6665, + "Role": { + "Id": 1, + "Name": "Organiser" + } + }, + { + "Id": 6677, + "Role": { + "Id": 1, + "Name": "Organiser" + } + }, + { + "Id": 6680, + "Role": { + "Id": 1, + "Name": "Organiser" + } + } + ], + "SecurityRole": { + "Id": 1, + "Name": "Administrator", + "Description": "Top level security. Can perform all actions within the system", + "AssociationLevel": { + "Id": 1, + "Value": "Top" + } + } +}'''; + + User result = noMirrorsMap.convert( + json, + new JsonConverter(), + new ClassConverter(startType: User), + [new CamelCaseManipulator()]); + + expect(result.id, 2); + expect(result.teamUsers[0].role.id, 1); + }); + + test("Can deserialize type that contains a DateTime", () { + ClassWithDateTime result = noMirrorsMap.convert( + "{\"time\": \"2055-02-03T15:57:12\"}", + new JsonConverter(), + new ClassConverter(startType: ClassWithDateTime)); + + expect(result.time, new isInstanceOf()); + }); + }); + + group("ClassConverter test", () { + setUp(() { + ClassConverter.converters[CustomConverterTest] = + new CustomClassConverter() + ..to = ((NativeIntermediateObject val) { + var values = val.value.split("|"); + var result = new CustomConverterTest() + ..id = int.parse(values[0]) + ..value = values[1]; + return result; + }) + ..from = (CustomConverterTest val) => + (new NativeIntermediateObject() + ..value = "${val.id}|${val.value}" + ..objectType = String); + }); + test( + "When a custom converter is specified for a type, the converter is used when converting to baseObject", + () { + var object = new CustomConverterParentTest() + ..testProperty = (new CustomConverterTest() + ..id = 1 + ..value = "Matthew"); + + var classConverter = new ClassConverter(); + ClassIntermediateObject baseObject = + classConverter.toBaseIntermediateObject(object); + var result = baseObject.properties["testProperty"]; + + expect(result.value, "1|Matthew"); + }); + + test( + "When a custom converter is specified for a type, the convert is used when converting from baseObject", + () { + var classObjectData = new ClassIntermediateObject()..properties = {}; + classObjectData.properties["testProperty"] = + new NativeIntermediateObject()..value = "1|Matthew"; + classObjectData.previousHashCode = "1"; + classObjectData.objectType = CustomConverterParentTest; + + var classConverter = new ClassConverter(); + CustomConverterParentTest result = + classConverter.fromBaseIntermediateObject(classObjectData); + + expect(result.testProperty.value, "Matthew"); + expect(result.testProperty.id, 1); + }); + + test( + "With a json string with no type attributes and a sub property of different type, deserialises correctly", + () { + var json = r'''{ + "id": 1, + "firstName": "Matthew", + "testProperty": { + "id": 2, + "name": "OtherName" + } +}'''; + NoTypeTestClass result = noMirrorsMap.convert( + json, + new JsonConverter(), + new ClassConverter(startType: NoTypeTestClass)); + expect(result.testProperty.name, "OtherName"); + }); + + test("With json with int value and setting double does not explode", + () { + var objectData = new NativeIntermediateObject()..value = 1; + var objectd = new ClassIntermediateObject() + ..objectType = ClassWithDouble + ..properties = {"val": objectData}; + + var classConverter = new ClassConverter(startType: ClassWithDouble); + + var result = classConverter.fromBaseIntermediateObject(objectd); + + expect(result.val, 1.0); + }); + }); + + group("JsonConverter tests", () { + test("can convert to object using HashCode", () { + const String hashcode = "1234"; + const String jsonHashcodeName = "\$ref"; + + var data = new ClassIntermediateObject() + ..properties = {} + ..previousHashCode = hashcode + ..objectType = CustomConverterTest; + + var converter = new JsonConverter(jsonHashcodeName); + String jsonResult = converter.fromBaseIntermediateObject(data); + + var expected = "\"$jsonHashcodeName\":\"$hashcode\""; + expect(jsonResult, contains(expected)); + }); + + test("can convert from json to object using custom HashCode", () { + const String hashcode = "1234"; + const String jsonHashcodeName = "\$ref"; + + var converter = new JsonConverter(jsonHashcodeName); + var json = + '{ "\$type": "nomirrorsmap.test_objects.CustomConverterTest",\"$jsonHashcodeName\": \"$hashcode\"}'; + var baseObjectData = converter.toBaseIntermediateObject(json) + as ClassIntermediateObject; + + expect(baseObjectData.previousHashCode, hashcode); + expect(baseObjectData.properties.containsKey(jsonHashcodeName), true); + }); + + test("can convert datetime to json", () { + var nativeIntermediateObject = new NativeIntermediateObject() + ..objectType = DateTime + ..value = new DateTime(2015, 9, 29, 10, 11); + var converter = new JsonConverter(); + var json = + converter.fromBaseIntermediateObject(nativeIntermediateObject); + expect(json, '"${nativeIntermediateObject.value.toString( )}"'); + }); + + test("can convert int in json to enum value", () { + var result = noMirrorsMap.convert("0", new JsonConverter(), + new ClassConverter(startType: TestEnum)); + expect(result, TestEnum.One); + }); + }); + + group("NewtonSoft json test", () { + test( + "For fromBaseObjectData, When called with two objects with same reference, Then returned json should have \$id in first object and \$ref in second object", + () { + var list = new ListIntermediateObject(); + var klass1 = new ClassIntermediateObject(); + var klass2 = new ClassIntermediateObject(); + + klass1.objectType = klass2.objectType = NewtonSoftTest; + klass1.previousHashCode = klass2.previousHashCode = "1"; + klass1.properties = {}; + klass1.properties["age"] = new NativeIntermediateObject()..value = 14; + klass1.properties["gender"] = new NativeIntermediateObject() + ..value = "m"; + + klass2.properties = {}; + + list.values = [klass1, klass2]; + + var converter = new NewtonSoftJsonConverter(); + String json = converter.fromBaseIntermediateObject(list); + + expect( + json, + contains( + r'''[{"$id":"1","$type":"nomirrorsmap.test_objects.NewtonSoftTest","age":14,"gender":"m"},{"$ref":"1"}]''')); + }); + + test( + "For toBaseObjectData, When called with two objects with same reference, Then returned objects should restore references", + () { + var converter = new NewtonSoftJsonConverter(); + ListIntermediateObject json = converter.toBaseIntermediateObject( + r'''[{"$id":"1","$type":"nomirrorsmap.test_objects.NewtonSoftTest","age":14,"gender":"m"},{"$ref":"1"}]'''); + + expect((json.values[0] as ClassIntermediateObject).previousHashCode, + "1"); + expect((json.values[0] as ClassIntermediateObject).previousHashCode, + (json.values[1] as ClassIntermediateObject).previousHashCode); + }); + + test("can deserialize using dollar ref property only", () { + var converter = new NewtonSoftJsonConverter(); + var jsonText = r'''{ + "$id": "1", + "$type": "nomirrorsmap.test_objects.SimpleTypeUsingDollarRef", + "name": "Test User", + "people": [{ + "$id": "2", + "$type": "nomirrorsmap.test_objects.SimpleTypeUsingDollarRef", + "name": "Another Test User", + "people": [] + }, + { + "$ref": "1" + } + ] +}'''; + var mapper = noMirrorsMap; + var result = + mapper.convert(jsonText, converter, new ClassConverter()); + + expect(result != null, true); + result as SimpleTypeUsingDollarRef; + expect(result.name, result.people[1].name); + expect(result.people[1].name, "Test User"); + }); + + test("can serialize object with no properties", () { + var json = + '''[{"\$id":"994910500","\$type":"nomirrorsmap.test_objects.TypeWithNoProperties"},{"\$ref":"994910500"}]'''; + + noMirrorsMap.convert( + json, + new NewtonSoftJsonConverter(), + new ClassConverter( + startType: const TypeOf>().type)); + }); + + test("Can deserialize", () { + var converter = new NewtonSoftJsonConverter(); + var jsonText = r'''{ + "$id": "1", + "$type": "nomirrorsmap.test_objects.InheritedClass", + "data": [ + { + "$ref": "1" + } + ] +}'''; + BaseIntermediateObject result = + converter.toBaseIntermediateObject(jsonText); + + assertClassObjectDataTypeNotNull(result); + }); + + test("Can deserialize generic", () { + var json = '''{ "id": 1 }'''; + var result = noMirrorsMap.convert( + json, + new JsonConverter(), + new ClassConverter( + startType: const TypeOf().type)) + as GenericType; + expect(result.id, 1); + }); + }); + }); + } +} + +/* +void buildMappingsFile() { + var resolvers = new Resolvers(dartSdkDirectory); + + var phases = [ + [new MapGeneratorTransformer(resolvers)] + ]; + + test("Generate Mappings", () async { + var helper = new TestHelper( + phases, + { + 'nomirrorsmap|lib/nomirrorsmap.dart': ''' + library nomirrorsmap; + + class Mappable{ + const Mappable(); + } + ''', + 'testProject|web/all_test.dart': '''import '../test/test_objects.dart'; + + main() {}''', + 'testProject|test/test_objects.dart': await new File("test/test_objects.dart").readAsString() + }, + [], + formatter: StringFormatter.noTrailingWhitespace); + helper.run(); + + var text = await helper['testProject|web/test_project_mappings.dart']; + + await new File("test/test_mappings.dart").writeAsString(text); + + //test_mappings.TestProjectMappings.re( ); + }); +} +*/ + +void assertClassObjectDataTypeNotNull(BaseIntermediateObject objectData) { + if (objectData is ClassIntermediateObject) { + var classObjectData = objectData; + if (classObjectData.objectType == null) { + expect(classObjectData.objectType, isNotNull); + } + classObjectData.properties.forEach((k, v) { + assertClassObjectDataTypeNotNull(v); + }); + } else if (objectData is ListIntermediateObject) { + var listObjectData = objectData; + listObjectData.values.forEach((v) { + assertClassObjectDataTypeNotNull(v); + }); + } else {} +} diff --git a/test/new_transformer_tests.dart b/test/new_transformer_tests.dart new file mode 100644 index 0000000..9d8621e --- /dev/null +++ b/test/new_transformer_tests.dart @@ -0,0 +1,706 @@ +library transformer.tests; + +import 'package:test/test.dart'; +import 'package:nomirrorsmap/src/transformer/transformer.dart'; +import 'package:dart_style/dart_style.dart'; + +import 'package:barback/barback.dart'; +import 'package:code_transformers/resolver.dart'; +import 'package:code_transformers/tests.dart'; +import 'package:code_transformers/src/dart_sdk.dart'; + +String MAP_LIBRARY = ''' + library nomirrorsmap.shared; + + class Mappable{ + const Mappable(); + } + '''; + +List getPhases([List libraryNames = const []]) { + var resolvers = new Resolvers(dartSdkDirectory); + + return [ + [ + new MapGeneratorTransformer( + resolvers, new TransformerOptions.initialize(libraryNames)) + ] + ]; +} + +String mappingsClassGenerator(List imports, List propertyMaps, + List classMaps, List enumMaps) { + var source = '''library WebMainDartMappings; + +import 'package:nomirrorsmap/nomirrorsmap.dart'; +${imports.join("\n")} + +class WebMainDartMappings +{ + static void register( ) + { + _registerFields( ); + _registerClasses( ); + _registerEnums( ); + } + static void _registerFields() + { + ${propertyMaps.map((name)=>'''NoMirrorsMapStore.registerField( "$name", ( object, value ) => object.$name = value, (object) => object.$name );''').join("\n")} + } + static void _registerClasses() + { + ${classMaps.join("\n")} + } + static void _registerEnums() + { + ${enumMaps.join("\n")} + } +}'''; + + return new DartFormatter().format(source); +} + +class TransformerTests { + static void run() { + test("With empty type generates mappings", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class TestClass +{ + +}''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [], [ + '''NoMirrorsMapStore.registerClass( "TestClass", web_main_dart.TestClass, const TypeOf>().type, () => new web_main_dart.TestClass(), { + } );''' + ], []) + }); + }); + + test("With type with properties of native types generates mappings", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class TestClass +{ + String stringVal; + int intVal; +}''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "stringVal", + "intVal" + ], [ + '''NoMirrorsMapStore.registerClass( "TestClass", web_main_dart.TestClass, const TypeOf>().type, () => new web_main_dart.TestClass(), { + 'stringVal': String, + 'intVal': int + } );''' + ], []) + }); + }); + + test("With type with properties of seen types", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class TestClass +{ + TestClass testClass; +}''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "testClass" + ], [ + '''NoMirrorsMapStore.registerClass( "TestClass", web_main_dart.TestClass, const TypeOf>().type, () => new web_main_dart.TestClass(), { + 'testClass': web_main_dart.TestClass + } );''' + ], []) + }); + }); + + test("With type in different package", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:testProject1/testProject1.dart'; + + main() {}''', + 'testProject1|lib/testProject1.dart': '''library TestProject1; + + import 'package:nomirrorsmap/nomirrorsmap.dart'; + + part 'test_class.dart';''', + 'testProject1|lib/test_class.dart': '''part of TestProject1; + + @Mappable() + class TestClass + { + TestClass testClass; + } + + @Mappable() + enum TestEnum { + One + }''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'package:testProject1/testProject1.dart' as lib_testProject1_dart;" + ], [ + "testClass" + ], [ + '''NoMirrorsMapStore.registerClass( "TestProject1.TestClass", lib_testProject1_dart.TestClass, const TypeOf>().type, () => new lib_testProject1_dart.TestClass(), { + 'testClass': lib_testProject1_dart.TestClass + } );''' + ], [ + "NoMirrorsMapStore.registerEnum( lib_testProject1_dart.TestEnum, lib_testProject1_dart.TestEnum.values );" + ]) + }); + }); + + test("With type that is enum", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +enum MyEnum +{ + EnumValue1, + EnumValue2, + EnumValue3, + EnumValue4 +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [], [], [ + "NoMirrorsMapStore.registerEnum( web_main_dart.MyEnum, web_main_dart.MyEnum.values );" + ]) + }); + }); + + test("With type that has base types", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class Type1 extends Type2 +{ + int intVal; +} + +class Type2 extends Type3 +{ + String stringVal; +} + +class Type3 +{ + DateTime dateTimeVal; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "intVal", + "stringVal", + "dateTimeVal" + ], [ + '''NoMirrorsMapStore.registerClass( "Type1", web_main_dart.Type1, const TypeOf>().type, () => new web_main_dart.Type1(), { + 'intVal': int, + 'stringVal': String, + 'dateTimeVal': DateTime + } );''' + ], []) + }); + }); + + test("With type that has GenericBaseType", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class Type1 extends Type2 +{ + int intVal; +} + +class Type2 +{ + T tVal; +} + +class Type3 +{ + DateTime dateTimeVal; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "intVal", + "tVal" + ], [ + '''NoMirrorsMapStore.registerClass( "Type1", web_main_dart.Type1, const TypeOf>().type, () => new web_main_dart.Type1(), { + 'intVal': int, + 'tVal': web_main_dart.Type3 + } );''' + ], []) + }); + }); + + test("With type that has List", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class Type1 +{ + List values; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "values" + ], [ + '''NoMirrorsMapStore.registerClass( "Type1", web_main_dart.Type1, const TypeOf>().type, () => new web_main_dart.Type1(), { + 'values': const TypeOf>().type + } );''' + ], []) + }); + }); + + test("With library name specified reads all type form that library", () { + return applyTransformers(getPhases(["TestProject1"]), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; +import 'package:testProject1/testProject1.dart'; + +main(){} +''', + 'testProject1|lib/testProject1.dart': '''library TestProject1; + + class Class1 {} + class Class2 {}''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'package:testProject1/testProject1.dart' as lib_testProject1_dart;" + ], [], [ + '''NoMirrorsMapStore.registerClass( "TestProject1.Class1", lib_testProject1_dart.Class1, const TypeOf>().type, () => new lib_testProject1_dart.Class1(), { + } );''', + '''NoMirrorsMapStore.registerClass( "TestProject1.Class2", lib_testProject1_dart.Class2, const TypeOf>().type, () => new lib_testProject1_dart.Class2(), { + } );''' + ], []) + }); + }); + + test("With type is generic ignores generic part", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; +import 'package:testProject1/testProject1.dart'; + +main(){} + +@Mappable() +class Class1 +{ + T val; +} + +@Mappable() +class Class2 +{ + Class1 val; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "val" + ], [ + '''NoMirrorsMapStore.registerClass( "Class1", web_main_dart.Class1, const TypeOf>().type, () => new web_main_dart.Class1(), { + 'val': dynamic + } );''', + '''NoMirrorsMapStore.registerClass( "Class2", web_main_dart.Class2, const TypeOf>().type, () => new web_main_dart.Class2(), { + 'val': web_main_dart.Class1 + } );''' + ], []) + }); + }); + + test("Picks up lists from properties", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; +import 'package:testProject1/testProject1.dart'; + +main(){} + +@Mappable() +class Class1 +{ + List val; + List val2; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "val", + "val2" + ], [ + '''NoMirrorsMapStore.registerClass( "Class1", web_main_dart.Class1, const TypeOf>().type, () => new web_main_dart.Class1(), { + 'val': const TypeOf>().type, + 'val2': const TypeOf>().type + } );''', + '''NoMirrorsMapStore.registerClass( "dart.core.String", String, const TypeOf>().type, null, { + } );''' + ], []) + }); + }); + + test("Does not add constructor for abstract type", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class ClassWithListOfAbstract +{ + List val; +} + +abstract class Class1 +{ + +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "val" + ], [ + '''NoMirrorsMapStore.registerClass( "ClassWithListOfAbstract", web_main_dart.ClassWithListOfAbstract, const TypeOf>().type, () => new web_main_dart.ClassWithListOfAbstract(), { + 'val': const TypeOf>().type + } );''', + '''NoMirrorsMapStore.registerClass( "Class1", web_main_dart.Class1, const TypeOf>().type, null, { + } );''' + ], []) + }); + }); + + test("Does not property map where does not have set/get", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class ClassWithOnlyGetOrSet +{ + int get prop1 => 1; + + set prop2 (int val){ + + } +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [], [ + '''NoMirrorsMapStore.registerClass( "ClassWithOnlyGetOrSet", web_main_dart.ClassWithOnlyGetOrSet, const TypeOf>().type, () => new web_main_dart.ClassWithOnlyGetOrSet(), {} );''' + ], []) + }); + }); + + test("Does not property map where is not public", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class ClassWithOnlyGetOrSet +{ + int _prop; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [], [ + '''NoMirrorsMapStore.registerClass( "ClassWithOnlyGetOrSet", web_main_dart.ClassWithOnlyGetOrSet, const TypeOf>().type, () => new web_main_dart.ClassWithOnlyGetOrSet(), {} );''' + ], []) + }); + }); + + test("Generates constuctor for ClassWithPositionalArguments", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class ClassWithPositionalArguments +{ + ClassWithPositionalArguments([int val = 0]); +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [], [ + '''NoMirrorsMapStore.registerClass( "ClassWithPositionalArguments", web_main_dart.ClassWithPositionalArguments, const TypeOf>().type, () => new web_main_dart.ClassWithPositionalArguments(), {} );''' + ], []) + }); + }); + + test("Does not property map for DateTime", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class ClassWithDateTime +{ + List val; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "val" + ], [ + '''NoMirrorsMapStore.registerClass( "ClassWithDateTime", web_main_dart.ClassWithDateTime, const TypeOf>().type, () => new web_main_dart.ClassWithDateTime(), { + 'val': const TypeOf>().type + } );''', + '''NoMirrorsMapStore.registerClass( "dart.core.DateTime", DateTime, const TypeOf>().type, null, {} );''' + ], []) + }); + }); + + test("Type has with generates correct map", () { + return applyTransformers(getPhases(), inputs: { + 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, + 'testProject|web/main.dart': + '''import 'package:nomirrorsmap/nomirrorsmap.dart'; + +main(){} + +@Mappable() +class ClassWithWith with BaseClass +{ + +} + +class BaseClass +{ + int id; +} +''' + }, results: { + 'testProject|web/web_main_dart_mappings.dart': mappingsClassGenerator([ + "import 'main.dart' as web_main_dart;" + ], [ + "id" + ], [ + '''NoMirrorsMapStore.registerClass( "ClassWithWith", web_main_dart.ClassWithWith, const TypeOf>().type, () => new web_main_dart.ClassWithWith(), { + 'id': int + } );''' + ], []) + }); + }); + } +} + +class MainModificationTransformerTests { + static String defaultMappingsFile = mappingsClassGenerator([], [], [], []); + + static void run(List> phases) { + test( + "With no types and no imports generates mappings and modifys main method", + () { + return applyTransformers(phases, inputs: { + 'testProject|web/main.dart': '''main(){}''' + }, results: { + 'testProject|web/main.dart': + '''import "web_main_dart_mappings.dart" as WebMainDartMappings; +main(){ + WebMainDartMappings.WebMainDartMappings.register(); +}''', + 'testProject|web/web_main_dart_mappings.dart': defaultMappingsFile + }); + }); + + test( + "With no types and has imports generates mappings and modifys main method", + () { + return applyTransformers(phases, inputs: { + 'testProject|web/main.dart': '''import 'dart:io'; + +main(){}''' + }, results: { + 'testProject|web/main.dart': '''import 'dart:io'; +import "web_main_dart_mappings.dart" as WebMainDartMappings; + +main(){ + WebMainDartMappings.WebMainDartMappings.register(); +}''', + 'testProject|web/web_main_dart_mappings.dart': defaultMappingsFile + }); + }); + + test( + "With no types and no imports and library directive generates mappings and modifys main method", + () { + return applyTransformers(phases, inputs: { + 'testProject|web/main.dart': '''library TestProject; + +main(){}''' + }, results: { + 'testProject|web/main.dart': '''library TestProject; + +import "web_main_dart_mappings.dart" as WebMainDartMappings; + +main(){ + WebMainDartMappings.WebMainDartMappings.register(); +}''', + 'testProject|web/web_main_dart_mappings.dart': defaultMappingsFile + }); + }); + + test( + "With no types and no imports and main is expression directive generates mappings and modifys main method", + () { + return applyTransformers(phases, inputs: { + 'testProject|web/main.dart': '''library TestProject; + +main() => 1;''' + }, results: { + 'testProject|web/main.dart': '''library TestProject; + +import "web_main_dart_mappings.dart" as WebMainDartMappings; + +main() { + WebMainDartMappings.WebMainDartMappings.register(); + return 1; +}''', + 'testProject|web/web_main_dart_mappings.dart': defaultMappingsFile + }); + }); + + test("Whe using nomirrorsmap_mirrors import it gets removed", () { + return applyTransformers(phases, inputs: { + 'nomirrrorsmap|lib/nomirrorsmap_mirrors.dart': + 'library nomirrorsmap.mirrors;', + 'testProject|web/main.dart': '''library TestProject; + +import 'package:nomirrorsmap/nomirrorsmap_mirrors.dart'; + +main() => 1;''' + }, results: { + 'testProject|web/main.dart': '''library TestProject; + + +import "web_main_dart_mappings.dart" as WebMainDartMappings; + +main() { + WebMainDartMappings.WebMainDartMappings.register(); + return 1; +}''', + 'testProject|web/web_main_dart_mappings.dart': defaultMappingsFile + }); + }); + + test("Whe using nomirrorsmap_mirrors useMirrors gets removed", () { + return applyTransformers(phases, inputs: { + 'nomirrrorsmap|lib/nomirrorsmap_mirrors.dart': + 'library nomirrorsmap.mirrors;' + '' + 'void useMirrors(){}', + 'testProject|web/main.dart': '''library TestProject; + +import 'package:nomirrorsmap/nomirrorsmap_mirrors.dart' as nomirrorsmapmirrors; + +main(){ + nomirrorsmapmirrors.useMirrors(); +}''' + }, results: { + 'testProject|web/main.dart': '''library TestProject; + + +import "web_main_dart_mappings.dart" as WebMainDartMappings; + +main(){ + WebMainDartMappings.WebMainDartMappings.register(); + + +}''', + 'testProject|web/web_main_dart_mappings.dart': defaultMappingsFile + }); + }); + } +} diff --git a/test/nomirrorsmap_generated_maps.dart b/test/nomirrorsmap_generated_maps.dart deleted file mode 100644 index ba85ec7..0000000 --- a/test/nomirrorsmap_generated_maps.dart +++ /dev/null @@ -1,165 +0,0 @@ -library nomirrorsmap.generated_maps; - -import 'tests.dart'; -import 'type_to_type_objects.dart' as objects; -import 'package:nomirrorsmap/src/shared/shared.dart'; - - -//Transformer has to generate this -class NoMirrorsMapGeneratedMaps{ - static List load(){ - return [ - new ClassGeneratedMap(objects.BaseDto,"nomirrorsmap.type_to_type_objects.BaseDto", () => null, {}, true), - new ClassGeneratedMap(objects.ConcreteWithNoMapEntity,"nomirrorsmap.type_to_type_objects.ConcreteWithNoMapEntity", () => new objects.ConcreteWithNoMapEntity(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'name': new GeneratedPropertyMap( String, (obj) => obj.name, (obj, value) => obj.name = value ) - }), - new ClassGeneratedMap(objects.ConcreteEntity,"nomirrorsmap.type_to_type_objects.ConcreteEntity", () => new objects.ConcreteEntity(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'name': new GeneratedPropertyMap( String, (obj) => obj.name, (obj, value) => obj.name = value ) - }), - new ClassGeneratedMap(objects.ConcreteDto,"nomirrorsmap.type_to_type_objects.ConcreteDto", () => new objects.ConcreteDto(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'name': new GeneratedPropertyMap( String, (obj) => obj.name, (obj, value) => obj.name = value ) - }), - new ClassGeneratedMap(objects.InheritedDto,"nomirrorsmap.type_to_type_objects.InheritedDto", () => new objects.InheritedDto(), { - 'extraProperty': new GeneratedPropertyMap( int, (obj) => obj.extraProperty, (obj, value) => obj.extraProperty = value ), - 'stringProperty': new GeneratedPropertyMap( String, (obj) => obj.stringProperty, (obj, value) => obj.stringProperty = value ), - 'intProperty': new GeneratedPropertyMap( int, (obj) => obj.intProperty, (obj, value) => obj.intProperty = value ), - 'dateTimeProperty': new GeneratedPropertyMap( DateTime, (obj) => obj.dateTimeProperty, (obj, value) => obj.dateTimeProperty = value ), - 'doubleProperty': new GeneratedPropertyMap( double, (obj) => obj.doubleProperty, (obj, value) => obj.doubleProperty = value ), - 'boolProperty': new GeneratedPropertyMap( bool, (obj) => obj.boolProperty, (obj, value) => obj.boolProperty = value ), - 'numProperty': new GeneratedPropertyMap( num, (obj) => obj.numProperty, (obj, value) => obj.numProperty = value ) - }), - new ClassGeneratedMap(objects.InheritedEntity,"nomirrorsmap.type_to_type_objects.InheritedEntity", () => new objects.InheritedEntity(), { - 'extraProperty': new GeneratedPropertyMap( int, (obj) => obj.extraProperty, (obj, value) => obj.extraProperty = value ), - 'stringProperty': new GeneratedPropertyMap( String, (obj) => obj.stringProperty, (obj, value) => obj.stringProperty = value ), - 'intProperty': new GeneratedPropertyMap( int, (obj) => obj.intProperty, (obj, value) => obj.intProperty = value ), - 'dateTimeProperty': new GeneratedPropertyMap( DateTime, (obj) => obj.dateTimeProperty, (obj, value) => obj.dateTimeProperty = value ), - 'doubleProperty': new GeneratedPropertyMap( double, (obj) => obj.doubleProperty, (obj, value) => obj.doubleProperty = value ), - 'boolProperty': new GeneratedPropertyMap( bool, (obj) => obj.boolProperty, (obj, value) => obj.boolProperty = value ), - 'numProperty': new GeneratedPropertyMap( num, (obj) => obj.numProperty, (obj, value) => obj.numProperty = value ) - }), - new ListGeneratedMap(const TypeOf>().type, objects.TestDto, () => new objects.CustomList()), - new ClassGeneratedMap(objects.CustomListEntity,"nomirrorsmap.type_to_type_objects.CustomListEntity", () => new objects.CustomListEntity(), { - 'list': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.list, (obj, value) => obj.list = value ) - }), - new ClassGeneratedMap(objects.CustomListDto,"nomirrorsmap.type_to_type_objects.CustomListDto", () => new objects.CustomListDto(), { - 'list': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.list, (obj, value) => obj.list = value ) - }), - new ListGeneratedMap(const TypeOf>().type, objects.TestDto, () => new List()), - new ClassGeneratedMap(objects.NonPrimitiveListEntity,"nomirrorsmap.type_to_type_objects.NonPrimitiveListEntity", () => new objects.NonPrimitiveListEntity(), { - 'list': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.list, (obj, value) => obj.list = value ) - }), - new ClassGeneratedMap(objects.NonPrimitiveListDto,"nomirrorsmap.type_to_type_objects.NonPrimitiveListDto", () => new objects.NonPrimitiveListDto(), { - 'list': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.list, (obj, value) => obj.list = value ) - }), - new ClassGeneratedMap(objects.ListEntity,"nomirrorsmap.type_to_type_objects.ListEntity", () => new objects.ListEntity(), { - 'list': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.list, (obj, value) => obj.list = value ) - }), - new ClassGeneratedMap(objects.ListDto,"nomirrorsmap.type_to_type_objects.ListDto", () => new objects.ListDto(), { - 'list': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.list, (obj, value) => obj.list = value ) - }), - new ClassGeneratedMap(objects.TestEntity2,"nomirrorsmap.type_to_type_objects.TestEntity2", () => new objects.TestEntity2(), { - 'test': new GeneratedPropertyMap( objects.TestEntity, (obj) => obj.test, (obj, value) => obj.test = value ) - }), - new ClassGeneratedMap(objects.TestDto2,"nomirrorsmap.type_to_type_objects.TestDto2", () => new objects.TestDto2(), { - 'test': new GeneratedPropertyMap( objects.TestDto, (obj) => obj.test, (obj, value) => obj.test = value ) - }), - new ClassGeneratedMap(objects.TestDto,"nomirrorsmap.type_to_type_objects.TestDto", () => new objects.TestDto(), { - 'stringProperty': new GeneratedPropertyMap( String, (obj) => obj.stringProperty, (obj, value) => obj.stringProperty = value ), - 'intProperty': new GeneratedPropertyMap( int, (obj) => obj.intProperty, (obj, value) => obj.intProperty = value ), - 'dateTimeProperty': new GeneratedPropertyMap( DateTime, (obj) => obj.dateTimeProperty, (obj, value) => obj.dateTimeProperty = value ), - 'doubleProperty': new GeneratedPropertyMap( double, (obj) => obj.doubleProperty, (obj, value) => obj.doubleProperty = value ), - 'boolProperty': new GeneratedPropertyMap( bool, (obj) => obj.boolProperty, (obj, value) => obj.boolProperty = value ), - 'numProperty': new GeneratedPropertyMap( num, (obj) => obj.numProperty, (obj, value) => obj.numProperty = value ) - }), - new ClassGeneratedMap(objects.TestEntity,"nomirrorsmap.type_to_type_objects.TestEntity", () => new objects.TestEntity(), { - 'stringProperty': new GeneratedPropertyMap( String, (obj) => obj.stringProperty, (obj, value) => obj.stringProperty = value ), - 'intProperty': new GeneratedPropertyMap( int, (obj) => obj.intProperty, (obj, value) => obj.intProperty = value ), - 'dateTimeProperty': new GeneratedPropertyMap( DateTime, (obj) => obj.dateTimeProperty, (obj, value) => obj.dateTimeProperty = value ), - 'doubleProperty': new GeneratedPropertyMap( double, (obj) => obj.doubleProperty, (obj, value) => obj.doubleProperty = value ), - 'boolProperty': new GeneratedPropertyMap( bool, (obj) => obj.boolProperty, (obj, value) => obj.boolProperty = value ), - 'numProperty': new GeneratedPropertyMap( num, (obj) => obj.numProperty, (obj, value) => obj.numProperty = value ) - }), - new ClassGeneratedMap(InheritedClass,"nomirrorsmap.tests.InheritedClass", () => new InheritedClass(), { - 'data': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.data, (obj, value) => obj.data = value ) - }), - new ClassGeneratedMap(NewtonSoftTest,"nomirrorsmap.tests.NewtonSoftTest", () => new NewtonSoftTest(), { - 'age': new GeneratedPropertyMap( int, (obj) => obj.age, (obj, value) => obj.age = value ), - 'gender': new GeneratedPropertyMap( String, (obj) => obj.gender, (obj, value) => obj.gender = value ) - }), - new ClassGeneratedMap(CustomConverterTest,"nomirrorsmap.tests.CustomConverterTest", () => new CustomConverterTest(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'value': new GeneratedPropertyMap( String, (obj) => obj.value, (obj, value) => obj.value = value ) - }), - new EnumGeneratedMap( TestEnum, TestEnum.values ), - new ClassGeneratedMap(User,"nomirrorsmap.tests.User", () => new User(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'firstName': new GeneratedPropertyMap( String, (obj) => obj.firstName, (obj, value) => obj.firstName = value ), - 'lastName': new GeneratedPropertyMap( String, (obj) => obj.lastName, (obj, value) => obj.lastName = value ), - 'emailAddress': new GeneratedPropertyMap( String, (obj) => obj.emailAddress, (obj, value) => obj.emailAddress = value ), - 'mobilePhone': new GeneratedPropertyMap( String, (obj) => obj.mobilePhone, (obj, value) => obj.mobilePhone = value ), - 'umpire': new GeneratedPropertyMap( bool, (obj) => obj.umpire, (obj, value) => obj.umpire = value ), - 'teamUsers': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.teamUsers, (obj, value) => obj.teamUsers = value ), - 'securityRole': new GeneratedPropertyMap( SecurityRole, (obj) => obj.securityRole, (obj, value) => obj.securityRole = value ), - }), - new ClassGeneratedMap(TeamMember,"nomirrorsmap.tests.TeamMember", () => new TeamMember(), { - 'role': new GeneratedPropertyMap( Role, (obj) => obj.role, (obj, value) => obj.role = value ), - 'user': new GeneratedPropertyMap( User, (obj) => obj.user, (obj, value) => obj.user = value ) - }), - new ClassGeneratedMap(Role,"nomirrorsmap.tests.Role", () => new Role(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'name': new GeneratedPropertyMap( String, (obj) => obj.name, (obj, value) => obj.name = value ) - }), - new ClassGeneratedMap(AssociationLevel,"nomirrorsmap.tests.AssociationLevel", () => new AssociationLevel(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'value': new GeneratedPropertyMap( String, (obj) => obj.value, (obj, value) => obj.value = value ) - }), - new ListGeneratedMap(const TypeOf>().type, TeamMember, () => new List()), - new ListGeneratedMap(const TypeOf>().type, String, () => new List()), - new ListGeneratedMap(const TypeOf>().type, Person, () => new List()), - new ClassGeneratedMap(TypeWithNoProperties,"nomirrorsmap.tests.TypeWithNoProperties", () => new TypeWithNoProperties(), {}), - new ListGeneratedMap(const TypeOf>().type, TypeWithNoProperties, () => new List()), - new ListGeneratedMap(const TypeOf>().type, SimpleTypeUsingDollarRef, () => new List()), - new ClassGeneratedMap(SimpleTypeUsingDollarRef,"nomirrorsmap.tests.SimpleTypeUsingDollarRef", () => new SimpleTypeUsingDollarRef(), { - 'name': new GeneratedPropertyMap( String, (obj) => obj.name, (obj, value) => obj.name = value ), - 'people': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.people, (obj, value) => obj.people = value ) - }), - new ClassGeneratedMap(ClassWithDouble,"nomirrorsmap.tests.ClassWithDouble", () => new ClassWithDouble(), { - 'val': new GeneratedPropertyMap( double, (obj) => obj.val, (obj, value) => obj.val = value ) - }), - new ClassGeneratedMap(NoTypeTestPropertyClass,"nomirrorsmap.tests.NoTypeTestPropertyClass", () => new NoTypeTestPropertyClass(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'name': new GeneratedPropertyMap(String, (obj) => obj.name, (obj, value) => obj.name = value ) - }), - new ClassGeneratedMap(NoTypeTestClass,"nomirrorsmap.tests.NoTypeTestClass", () => new NoTypeTestClass(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'firstName': new GeneratedPropertyMap(String, (obj) => obj.firstName, (obj, value) => obj.firstName = value ), - 'testProperty': new GeneratedPropertyMap( NoTypeTestPropertyClass, (obj) => obj.testProperty, (obj, value) => obj.testProperty = value ) - }), - new ListGeneratedMap(const TypeOf>().type, String, () => new CustomList()), - new ClassGeneratedMap(TestObjectWithCustomList,"nomirrorsmap.tests.TestObjectWithCustomList", () => new TestObjectWithCustomList(), { - 'customList': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.customList, (obj, value) => obj.customList = value ) - }), - new ClassGeneratedMap(CustomConverterParentTest,"nomirrorsmap.tests.CustomConverterParentTest", () => new CustomConverterParentTest(), { - 'testProperty': new GeneratedPropertyMap( CustomConverterTest, (obj) => obj.testProperty, (obj, value) => obj.testProperty = value ) - }), - new ClassGeneratedMap(Person,"nomirrorsmap.tests.Person", () => new Person(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'parents': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.parents, (obj, value) => obj.parents = value ), - 'children': new GeneratedPropertyMap( const TypeOf>().type, (obj) => obj.children, (obj, value) => obj.children = value ) - }), - new ClassGeneratedMap(SecurityRole,"nomirrorsmap.tests.SecurityRole", () => new SecurityRole(), { - 'id': new GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), - 'name': new GeneratedPropertyMap( String, (obj) => obj.name, (obj, value) => obj.name = value ), - 'description': new GeneratedPropertyMap( String, (obj) => obj.description, (obj, value) => obj.description = value ), - 'associationLevel': new GeneratedPropertyMap( AssociationLevel, (obj) => obj.associationLevel, (obj, value) => obj.associationLevel = value ) - }), - new ClassGeneratedMap(ClassWithDateTime,"nomirrorsmap.tests.User", () => new ClassWithDateTime(), { - 'time': new GeneratedPropertyMap( DateTime, (obj) => obj.time, (obj, value) => obj.time = value ) - }) - ]; - } -} - diff --git a/test/test_json/abstract_class_and_inheritence.json b/test/test_json/abstract_class_and_inheritence.json deleted file mode 100644 index 88bb9f5..0000000 --- a/test/test_json/abstract_class_and_inheritence.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$id": "1", - "$type": "nomirrorsmap.tests.InheritedClass", - "data": [ - { - "$ref": "1" - } - ] -} \ No newline at end of file diff --git a/test/test_json/convert_using_dollarRef.json b/test/test_json/convert_using_dollarRef.json deleted file mode 100644 index 8ecf0a0..0000000 --- a/test/test_json/convert_using_dollarRef.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$id": "1", - "$type": "nomirrorsmap.tests.SimpleTypeUsingDollarRef", - "name": "Test User", - "people": [{ - "$id": "2", - "$type": "nomirrorsmap.tests.SimpleTypeUsingDollarRef", - "name": "Another Test User", - "people": [] - }, - { - "$ref": "1" - } - ] -} \ No newline at end of file diff --git a/test/test_json/hashcode_test.json b/test/test_json/hashcode_test.json deleted file mode 100644 index 025fe73..0000000 --- a/test/test_json/hashcode_test.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "547833245", - "id": 3, - "parents": [{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "48854486", - "id": 1, - "parents": [], - "children": [{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "48854487", - "id": 2, - "parents": [{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "48854486" - }], - "children": [] - }] - }], - "children": [] -} \ No newline at end of file diff --git a/test/test_json/list.json b/test/test_json/list.json deleted file mode 100644 index 3f024e1..0000000 --- a/test/test_json/list.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "odata.metadata": "http://localhost/odata/$metadata#Users/@Element", - "Id": 2, - "FirstName": "No", - "LastName": "Mirrors", - "EmailAddress": "fsgpoidhnfoglb@example.com", - "MobilePhone": "65406834354356", - "Umpire": false, - "TeamUsers": [ - { - "Id": 6665, - "Role": { - "Id": 1, - "Name": "Organiser" - } - }, - { - "Id": 6677, - "Role": { - "Id": 1, - "Name": "Organiser" - } - }, - { - "Id": 6680, - "Role": { - "Id": 1, - "Name": "Organiser" - } - } - ], - "SecurityRole": { - "Id": 1, - "Name": "Administrator", - "Description": "Top level security. Can perform all actions within the system", - "AssociationLevel": { - "Id": 1, - "Value": "Top" - } - } -} \ No newline at end of file diff --git a/test/test_json/newtonsoft_test.json b/test/test_json/newtonsoft_test.json deleted file mode 100644 index 068b32e..0000000 --- a/test/test_json/newtonsoft_test.json +++ /dev/null @@ -1 +0,0 @@ -[{"$id":"1","$type":"nomirrorsmap.tests.NewtonSoftTest","age":14,"gender":"m"},{"$ref":"1"}] \ No newline at end of file diff --git a/test/test_json/no_type_string_objects.json b/test/test_json/no_type_string_objects.json deleted file mode 100644 index d015dcf..0000000 --- a/test/test_json/no_type_string_objects.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "id": 1, - "firstName": "Matthew", - "testProperty": { - "id": 2, - "name": "OtherName" - } -} \ No newline at end of file diff --git a/test/test_json/reversed_hashcode_json.json b/test/test_json/reversed_hashcode_json.json deleted file mode 100644 index a6af7b3..0000000 --- a/test/test_json/reversed_hashcode_json.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "547833245", - "id": 3, - "parents": [{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "48854486", - "parents": [], - "children": [{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "48854487", - "id": 2, - "parents": [{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "48854486", - "id": 1 - }], - "children": [] - }] - }], - "children": [] -} \ No newline at end of file diff --git a/test/test_json/simple_object.json b/test/test_json/simple_object.json deleted file mode 100644 index 2609d85..0000000 --- a/test/test_json/simple_object.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$type": "nomirrorsmap.tests.Person", - "$hashcode": "547833245", - "id": 1, - "parents": [], - "children": [] -} \ No newline at end of file diff --git a/test/test_mappings.dart b/test/test_mappings.dart new file mode 100644 index 0000000..bf266d7 --- /dev/null +++ b/test/test_mappings.dart @@ -0,0 +1,398 @@ +library TestProject.Mappings; + +import 'package:nomirrorsmap/nomirrorsmap.dart'; +import 'test_objects.dart' as test_test_objects_dart; +import 'type_to_type_objects.dart' as test_type_to_type_objects_dart; + +class TestProjectMappings { + static void register() { + _registerAccessors(); + _registerClasses(); + _registerEnums(); + } + + static void _registerAccessors() { + NoMirrorsMapStore.registerField("data", + (object, value) => object.data = value, (object) => object.data); + NoMirrorsMapStore.registerField("name", + (object, value) => object.name = value, (object) => object.name); + NoMirrorsMapStore.registerField("people", + (object, value) => object.people = value, (object) => object.people); + NoMirrorsMapStore.registerField( + "age", (object, value) => object.age = value, (object) => object.age); + NoMirrorsMapStore.registerField("gender", + (object, value) => object.gender = value, (object) => object.gender); + NoMirrorsMapStore.registerField( + "id", (object, value) => object.id = value, (object) => object.id); + NoMirrorsMapStore.registerField("parents", + (object, value) => object.parents = value, (object) => object.parents); + NoMirrorsMapStore.registerField( + "children", + (object, value) => object.children = value, + (object) => object.children); + NoMirrorsMapStore.registerField( + "val", (object, value) => object.val = value, (object) => object.val); + NoMirrorsMapStore.registerField( + "testProperty", + (object, value) => object.testProperty = value, + (object) => object.testProperty); + NoMirrorsMapStore.registerField("value", + (object, value) => object.value = value, (object) => object.value); + NoMirrorsMapStore.registerField( + "customList", + (object, value) => object.customList = value, + (object) => object.customList); + NoMirrorsMapStore.registerField( + "firstName", + (object, value) => object.firstName = value, + (object) => object.firstName); + NoMirrorsMapStore.registerField( + "lastName", + (object, value) => object.lastName = value, + (object) => object.lastName); + NoMirrorsMapStore.registerField( + "emailAddress", + (object, value) => object.emailAddress = value, + (object) => object.emailAddress); + NoMirrorsMapStore.registerField( + "mobilePhone", + (object, value) => object.mobilePhone = value, + (object) => object.mobilePhone); + NoMirrorsMapStore.registerField("umpire", + (object, value) => object.umpire = value, (object) => object.umpire); + NoMirrorsMapStore.registerField( + "teamUsers", + (object, value) => object.teamUsers = value, + (object) => object.teamUsers); + NoMirrorsMapStore.registerField( + "securityRole", + (object, value) => object.securityRole = value, + (object) => object.securityRole); + NoMirrorsMapStore.registerField("role", + (object, value) => object.role = value, (object) => object.role); + NoMirrorsMapStore.registerField("user", + (object, value) => object.user = value, (object) => object.user); + NoMirrorsMapStore.registerField( + "description", + (object, value) => object.description = value, + (object) => object.description); + NoMirrorsMapStore.registerField( + "associationLevel", + (object, value) => object.associationLevel = value, + (object) => object.associationLevel); + NoMirrorsMapStore.registerField("time", + (object, value) => object.time = value, (object) => object.time); + NoMirrorsMapStore.registerField( + "duration", + (object, value) => object.duration = value, + (object) => object.duration); + NoMirrorsMapStore.registerField( + "stringProperty", + (object, value) => object.stringProperty = value, + (object) => object.stringProperty); + NoMirrorsMapStore.registerField( + "intProperty", + (object, value) => object.intProperty = value, + (object) => object.intProperty); + NoMirrorsMapStore.registerField( + "dateTimeProperty", + (object, value) => object.dateTimeProperty = value, + (object) => object.dateTimeProperty); + NoMirrorsMapStore.registerField( + "doubleProperty", + (object, value) => object.doubleProperty = value, + (object) => object.doubleProperty); + NoMirrorsMapStore.registerField( + "boolProperty", + (object, value) => object.boolProperty = value, + (object) => object.boolProperty); + NoMirrorsMapStore.registerField( + "numProperty", + (object, value) => object.numProperty = value, + (object) => object.numProperty); + NoMirrorsMapStore.registerField("test", + (object, value) => object.test = value, (object) => object.test); + NoMirrorsMapStore.registerField("list", + (object, value) => object.list = value, (object) => object.list); + NoMirrorsMapStore.registerField( + "extraProperty", + (object, value) => object.extraProperty = value, + (object) => object.extraProperty); + } + + static void _registerClasses() { + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.InheritedClass", + test_test_objects_dart.InheritedClass, + const TypeOf>().type, + () => new test_test_objects_dart.InheritedClass(), { + 'data': const TypeOf>().type + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.TypeWithNoProperties", + test_test_objects_dart.TypeWithNoProperties, + const TypeOf>().type, + () => new test_test_objects_dart.TypeWithNoProperties(), {}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.SimpleTypeUsingDollarRef", + test_test_objects_dart.SimpleTypeUsingDollarRef, + const TypeOf>() + .type, + () => new test_test_objects_dart.SimpleTypeUsingDollarRef(), + { + 'name': String, + 'people': + const TypeOf>() + .type + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.NewtonSoftTest", + test_test_objects_dart.NewtonSoftTest, + const TypeOf>().type, + () => new test_test_objects_dart.NewtonSoftTest(), + {'age': int, 'gender': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.Person", + test_test_objects_dart.Person, + const TypeOf>().type, + () => new test_test_objects_dart.Person(), { + 'id': int, + 'parents': const TypeOf>().type, + 'children': const TypeOf>().type + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.ClassWithDouble", + test_test_objects_dart.ClassWithDouble, + const TypeOf>().type, + () => new test_test_objects_dart.ClassWithDouble(), + {'val': double}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.CustomConverterParentTest", + test_test_objects_dart.CustomConverterParentTest, + const TypeOf>() + .type, + () => new test_test_objects_dart.CustomConverterParentTest(), + {'testProperty': test_test_objects_dart.CustomConverterTest}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.CustomConverterTest", + test_test_objects_dart.CustomConverterTest, + const TypeOf>().type, + () => new test_test_objects_dart.CustomConverterTest(), + {'id': int, 'value': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.NoTypeTestClass", + test_test_objects_dart.NoTypeTestClass, + const TypeOf>().type, + () => new test_test_objects_dart.NoTypeTestClass(), { + 'id': int, + 'firstName': String, + 'testProperty': test_test_objects_dart.NoTypeTestPropertyClass + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.NoTypeTestPropertyClass", + test_test_objects_dart.NoTypeTestPropertyClass, + const TypeOf>() + .type, + () => new test_test_objects_dart.NoTypeTestPropertyClass(), + {'id': int, 'name': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.User", + test_test_objects_dart.User, + const TypeOf>().type, + () => new test_test_objects_dart.User(), { + 'id': int, + 'firstName': String, + 'lastName': String, + 'emailAddress': String, + 'mobilePhone': String, + 'umpire': bool, + 'teamUsers': const TypeOf>().type, + 'securityRole': test_test_objects_dart.SecurityRole + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.TeamMember", + test_test_objects_dart.TeamMember, + const TypeOf>().type, + () => new test_test_objects_dart.TeamMember(), const { + 'role': test_test_objects_dart.Role, + 'user': test_test_objects_dart.User + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.Role", + test_test_objects_dart.Role, + const TypeOf>().type, + () => new test_test_objects_dart.Role(), + const {'id': int, 'name': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.SecurityRole", + test_test_objects_dart.SecurityRole, + const TypeOf>().type, + () => new test_test_objects_dart.SecurityRole(), const { + 'id': int, + 'name': String, + 'description': String, + 'associationLevel': test_test_objects_dart.AssociationLevel + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.AssociationLevel", + test_test_objects_dart.AssociationLevel, + const TypeOf>().type, + () => new test_test_objects_dart.AssociationLevel(), + const {'id': int, 'value': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.ClassWithDateTime", + test_test_objects_dart.ClassWithDateTime, + const TypeOf>().type, + () => new test_test_objects_dart.ClassWithDateTime(), + const {'time': DateTime}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.PersonGeneric", + test_test_objects_dart.PersonGeneric, + const TypeOf>().type, + () => new test_test_objects_dart.PersonGeneric(), + const {'val': test_test_objects_dart.Person}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.TypeWithDuration", + test_test_objects_dart.TypeWithDuration, + const TypeOf>().type, + () => new test_test_objects_dart.TypeWithDuration(), + const {'duration': Duration}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.GenericType", + test_test_objects_dart.GenericType, + const TypeOf>().type, + () => new test_test_objects_dart.GenericType(), + const {'id': dynamic}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.TestEntity", + test_type_to_type_objects_dart.TestEntity, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.TestEntity(), const { + 'stringProperty': String, + 'intProperty': int, + 'dateTimeProperty': DateTime, + 'doubleProperty': double, + 'boolProperty': bool, + 'numProperty': num + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.TestDto", + test_type_to_type_objects_dart.TestDto, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.TestDto(), const { + 'stringProperty': String, + 'intProperty': int, + 'dateTimeProperty': DateTime, + 'doubleProperty': double, + 'boolProperty': bool, + 'numProperty': num + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.TestEntity2", + test_type_to_type_objects_dart.TestEntity2, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.TestEntity2(), + const {'test': test_type_to_type_objects_dart.TestEntity}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.TestDto2", + test_type_to_type_objects_dart.TestDto2, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.TestDto2(), + const {'test': test_type_to_type_objects_dart.TestDto}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.ListEntity", + test_type_to_type_objects_dart.ListEntity, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.ListEntity(), + {'list': const TypeOf>().type}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.ListDto", + test_type_to_type_objects_dart.ListDto, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.ListDto(), + {'list': const TypeOf>().type}); + NoMirrorsMapStore.registerClass("dart.core.String", String, + const TypeOf>().type, null, {}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.NonPrimitiveListEntity", + test_type_to_type_objects_dart.NonPrimitiveListEntity, + const TypeOf< + List>().type, + () => new test_type_to_type_objects_dart.NonPrimitiveListEntity(), + {'list': const TypeOf>().type}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.NonPrimitiveListDto", + test_type_to_type_objects_dart.NonPrimitiveListDto, + const TypeOf>() + .type, + () => new test_type_to_type_objects_dart.NonPrimitiveListDto(), + {'list': const TypeOf>().type}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.InheritedDto", + test_type_to_type_objects_dart.InheritedDto, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.InheritedDto(), { + 'extraProperty': int, + 'stringProperty': String, + 'intProperty': int, + 'dateTimeProperty': DateTime, + 'doubleProperty': double, + 'boolProperty': bool, + 'numProperty': num + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.InheritedEntity", + test_type_to_type_objects_dart.InheritedEntity, + const TypeOf>() + .type, + () => new test_type_to_type_objects_dart.InheritedEntity(), + { + 'extraProperty': int, + 'stringProperty': String, + 'intProperty': int, + 'dateTimeProperty': DateTime, + 'doubleProperty': double, + 'boolProperty': bool, + 'numProperty': num + }); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.ConcreteEntity", + test_type_to_type_objects_dart.ConcreteEntity, + const TypeOf>() + .type, + () => new test_type_to_type_objects_dart.ConcreteEntity(), + {'id': int, 'name': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.ConcreteDto", + test_type_to_type_objects_dart.ConcreteDto, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.ConcreteDto(), + {'id': int, 'name': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.ConcreteWithNoMapEntity", + test_type_to_type_objects_dart.ConcreteWithNoMapEntity, + const TypeOf< + List>() + .type, + () => new test_type_to_type_objects_dart.ConcreteWithNoMapEntity(), + {'id': int, 'name': String}); + + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.SourceType", + test_type_to_type_objects_dart.SourceType, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.SourceType(), + {'intProperty': int, 'stringProperty': String}); + NoMirrorsMapStore.registerClass( + "nomirrorsmap.test_objects.DestType", + test_type_to_type_objects_dart.DestType, + const TypeOf>().type, + () => new test_type_to_type_objects_dart.DestType(), + {'intProperty': int}); + } + + static void _registerEnums() { + NoMirrorsMapStore.registerEnum(test_test_objects_dart.TestEnum, + test_test_objects_dart.TestEnum.values); + } +} diff --git a/test/test_objects.dart b/test/test_objects.dart new file mode 100644 index 0000000..04658d8 --- /dev/null +++ b/test/test_objects.dart @@ -0,0 +1,126 @@ +library nomirrorsmap.test_objects; + +import 'package:nomirrorsmap/nomirrorsmap.dart'; + +@Mappable() +enum TestEnum { One, Two } + +abstract class TheAbstractClass { + List data; +} + +@Mappable() +class InheritedClass extends TheAbstractClass {} + +@Mappable() +class TypeWithNoProperties {} + +@Mappable() +class SimpleTypeUsingDollarRef { + String name; + List people; +} + +@Mappable() +class NewtonSoftTest { + int age; + String gender; +} + +@Mappable() +class Person { + int id; + List parents; + List children; +} + +@Mappable() +class ClassWithDouble { + double val; +} + +//{"1|Matthew"} + +@Mappable() +class CustomConverterParentTest { + CustomConverterTest testProperty; +} + +@Mappable() +class CustomConverterTest { + int id; + String value; +} + +@Mappable() +class NoTypeTestClass { + int id; + String firstName; + NoTypeTestPropertyClass testProperty; +} + +@Mappable() +class NoTypeTestPropertyClass { + int id; + String name; +} + +@Mappable() +class User { + int id; + String firstName; + String lastName; + String emailAddress; + String mobilePhone; + bool umpire; + + List teamUsers; + SecurityRole securityRole; +} + +@Mappable() +class TeamMember { + Role role; + User user; +} + +@Mappable() +class Role { + int id; + String name; +} + +@Mappable() +class SecurityRole { + int id; + String name; + String description; + AssociationLevel associationLevel; +} + +@Mappable() +class AssociationLevel { + int id; + String value; +} + +@Mappable() +class ClassWithDateTime { + DateTime time; +} + +@Mappable() +class GenericBase { + T val; +} + +@Mappable() +class PersonGeneric extends GenericBase {} + +class TypeWithDuration { + Duration duration; +} + +class GenericType { + T id; +} diff --git a/test/tests.dart b/test/tests.dart deleted file mode 100644 index 136266a..0000000 --- a/test/tests.dart +++ /dev/null @@ -1,523 +0,0 @@ -library nomirrorsmap.tests; - -import 'dart:io' as io; - -import 'package:unittest/unittest.dart'; -import 'package:nomirrorsmap/nomirrorsmap.dart'; -import 'package:nomirrorsmap/src/conversion_objects/conversion_objects.dart'; -import 'package:nomirrorsmap/src/shared/shared.dart'; -import 'package:nomirrorsmap/src/transformer/transformer.dart'; -import 'dart:collection'; -import 'package:code_transformers/tests.dart' as codeTransformerTests; - -import 'package:barback/barback.dart'; -import 'package:code_transformers/resolver.dart'; -import 'package:code_transformers/tests.dart'; -import 'package:code_transformers/src/dart_sdk.dart'; - -import 'dart:async'; - -import 'nomirrorsmap_generated_maps.dart'; -import 'type_to_type_objects.dart' as objects; - -part 'transformer_tests.dart'; -part 'type_to_type_tests.dart'; - -String _getFileContent( String fileName ) -{ - return new io.File.fromUri( new Uri.file( "test/test_json/" + fileName ) ).readAsStringSync( ); -} - -main( ) -{ - GeneratedMapProvider.addMaps(NoMirrorsMapGeneratedMaps.load()); - - TransformerTests.run(); - TypeToTypeTests.run(); - - group( "Serialization Tests", ( ) - { - test( "Can serialize an object that is null", ( ) - { - var result = new NoMirrorsMap( ).convert( null, new ClassConverter( ), new JsonConverter( ) ); - expect( result, "null" ); - } ); - - test( "Can serialize to Pascal case", ( ) - { - var result = new NoMirrorsMap( ).convert( new Person( ) - ..id = 1 - ..children = [] - ..parents = [], new ClassConverter( ), new JsonConverter( ), [new PascalCaseManipulator( )] ); - expect( result, endsWith( '''"Id":1,"Parents":[],"Children":[]}''' ) ); - } ); - } ); - - group( "Deserialization Tests", ( ) - { - test( "Can deserialize simple object structure", ( ) - { - var json = _getFileContent( "simple_object.json" ); - - var result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( ) ) as Person; - - expect( result.id, 1 ); - expect( result.children, isNotNull ); - expect( result.parents, isNotNull ); - } ); - - test( "Can deserialize objects with circular references", ( ) - { - var json = _getFileContent("hashcode_test.json"); - - var result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( ) ) as Person; - - var parent = result.parents[0]; - - expect( parent.children[0].parents[0], parent ); - } ); - - test( "Can deserialize objects with circular references, even if properties are seen after reference", ( ) - { - var json = _getFileContent("reversed_hashcode_json.json"); - - var result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( ) ) as Person; - - var parent = result.parents[0]; - - expect( parent, parent.children[0].parents[0] ); - expect( parent.id, 1 ); - } ); - - test( "Can deserialize objects that do not have \$type", ( ) - { - var json = "{\"id\": 1, \"children\": [], \"parents\": []}"; - - Person result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( startType: Person ) ); - - expect( result.id, 1 ); - expect( result.children, isNotNull ); - expect( result.parents, isNotNull ); - } ); - - test( "Can deserialize null", ( ) - { - var result = new NoMirrorsMap( ).convert( "null", new JsonConverter( ), new ClassConverter( ) ); - expect( result, null ); - } ); - - test( "Can deserialize using CamelCaseManipulator", ( ) - { - var json = '''{"\$type":"nomirrorsmap.tests.Person","\$hashcode":"511757599","Id":1,"Parents":[],"Children":[]}'''; - - Person result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( ), [new CamelCaseManipulator( )] ); - expect( result.id, 1 ); - } ); - - test( "Can deserialize type that contains a list", ( ) - { - var json = "{\"id\": 1, \"children\": [{\"id\": 2,\"children\": [], \"parents\": []}], \"parents\": []}"; - - Person result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( startType: Person ) ); - - expect( result.id, 1 ); - expect( result.children.length, 1 ); - expect( result.children[0].id, 2 ); - } ); - - test( "Can deserialize type that contains a list", ( ) - { - var json = _getFileContent("list.json"); - - User result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( startType: User ), [new CamelCaseManipulator( )] ); - - expect( result.id, 2 ); - expect( result.teamUsers[0].role.id, 1 ); - } ); - - test( "Can deserialize type that contains a DateTime", ( ) - { - ClassWithDateTime result = new NoMirrorsMap( ).convert( "{\"time\": \"2055-02-03T15:57:12\"}", new JsonConverter( ), new ClassConverter( startType: ClassWithDateTime ) ); - - expect( result.time, new isInstanceOf( ) ); - } ); - } ); - - - group( "ClassConverter test", ( ) - { - - setUp( ( ) - { - ClassConverter.converters[CustomConverterTest] = new CustomClassConverter( ) - ..to = ( String val ) - { - var values = val.split( "|" ); - var result = new CustomConverterTest( ) - ..id = int.parse( values[0] ) - ..value = values[1]; - return result; - } - ..from = ( CustomConverterTest val ) - => "${val.id}|${val.value}"; - - } ); - test( "When a custom converter is specified for a type, the converter is used when converting to baseObject", ( ) - { - var object = new CustomConverterParentTest( ) - ..testProperty = ( new CustomConverterTest( ) - ..id = 1 - ..value = "Matthew"); - - var classConverter = new ClassConverter( ); - ClassObjectData baseObject = classConverter.toBaseObjectData( object ); - NativeObjectData result = baseObject.properties["testProperty"]; - - expect( result.value, "1|Matthew" ); - } ); - - test( "When a custom converter is specified for a type, the convert is used when converting from baseObject", ( ) - { - var classObjectData = new ClassObjectData( ) - ..properties = { - }; - classObjectData.properties["testProperty"] = new NativeObjectData( ) - ..value = "1|Matthew"; - classObjectData.previousHashCode = "1"; - classObjectData.objectType = CustomConverterParentTest; - - var classConverter = new ClassConverter( ); - CustomConverterParentTest result = classConverter.fromBaseObjectData( classObjectData ); - - expect( result.testProperty.value, "Matthew" ); - expect( result.testProperty.id, 1 ); - - } ); - - - test( "When a property is of a type that inherits from list, the conversion from baseObject to object works", ( ) - { - var classObjectData = new ClassObjectData( ) - ..properties = { - }; - - classObjectData.properties["customList"] = new ListObjectData( ) - ..values = [ new NativeObjectData( ) - ..value = "Hello", new NativeObjectData( ) - ..value = "World"]; - classObjectData.previousHashCode = "1"; - classObjectData.objectType = TestObjectWithCustomList; - - var classConverter = new ClassConverter( ); - TestObjectWithCustomList result = classConverter.fromBaseObjectData( classObjectData ); - - expect( result.customList[0], "Hello" ); - expect( result.customList[1], "World" ); - } ); - - test( "With a json string with no type attributes and a sub property of different type, deserialises correctly", ( ) - { - var json = _getFileContent( "no_type_string_objects.json" ); - NoTypeTestClass result = new NoMirrorsMap( ).convert( json, new JsonConverter( ), new ClassConverter( startType: NoTypeTestClass ) ); - expect( result.testProperty.name, "OtherName" ); - } ); - - test( "With json with int value and setting double does not explode", ( ) - { - var objectData = new NativeObjectData( ) - ..value = 1; - var objectd = new ClassObjectData( ) - ..objectType = ClassWithDouble - ..properties = { - "val": objectData - }; - - var classConverter = new ClassConverter( startType: ClassWithDouble ); - - var result = classConverter.fromBaseObjectData( objectd ); - - expect( result.val, 1.0 ); - } ); - } ); - - group( "JsonConverter tests", ( ) - { - test( "can convert to object using HashCode", ( ) - { - const String hashcode = "1234"; - const String jsonHashcodeName = "\$ref"; - - var data = new ClassObjectData( ) - ..properties = {} - ..previousHashCode = hashcode - ..objectType = CustomConverterTest; - - var converter = new JsonConverter( jsonHashcodeName ); - String jsonResult = converter.fromBaseObjectData( data ); - - var expected = "\"$jsonHashcodeName\":\"$hashcode\""; - expect( jsonResult.contains( expected ), true ); - } ); - - test( "can convert from json to object using custom HashCode", ( ) - { - const String hashcode = "1234"; - const String jsonHashcodeName = "\$ref"; - - var converter = new JsonConverter( jsonHashcodeName ); - var json = '{ "\$type": "nomirrorsmap.tests.CustomConverterTest",\"$jsonHashcodeName\": \"$hashcode\"}'; - var baseObjectData = converter.toBaseObjectData( json ) as ClassObjectData; - - expect( baseObjectData.previousHashCode, hashcode ); - expect( baseObjectData.properties.containsKey( jsonHashcodeName ), true ); - } ); - } ); - - group( "NewtonSoft json test", ( ) - { - test( "For fromBaseObjectData, When called with two objects with same reference, Then returned json should have \$id in first object and \$ref in second object", ( ) - { - var list = new ListObjectData( ); - var klass1 = new ClassObjectData( ); - var klass2 = new ClassObjectData( ); - - klass1.objectType = klass2.objectType = NewtonSoftTest; - klass1.previousHashCode = klass2.previousHashCode = "1"; - klass1.properties = {}; - klass1.properties["age"] = new NativeObjectData( ) - ..value = 14; - klass1.properties["gender"] = new NativeObjectData( ) - ..value = "m"; - - klass2.properties = {}; - - list.values = [klass1, klass2]; - - var converter = new NewtonSoftJsonConverter( ); - String json = converter.fromBaseObjectData( list ); - - expect( json.contains( _getFileContent( "newtonsoft_test.json" ) ), true ); - - } ); - - test( "For toBaseObjectData, When called with two objects with same reference, Then returned objects should restore references", ( ) - { - var converter = new NewtonSoftJsonConverter( ); - ListObjectData json = converter.toBaseObjectData( _getFileContent( "newtonsoft_test.json" ) ); - - expect( (json.values[0] as ClassObjectData).previousHashCode, "1" ); - expect( (json.values[0] as ClassObjectData).previousHashCode, (json.values[1] as ClassObjectData).previousHashCode ); - - } ); - - test( "can deserialize using dollar ref property only", ( ) - { - var converter = new NewtonSoftJsonConverter( ); - var jsonText = _getFileContent( "convert_using_dollarRef.json" ); - var mapper = new NoMirrorsMap( ); - var result = mapper.convert( jsonText, converter, new ClassConverter( ) ); - - expect( result != null, true ); - var simpleType = result as SimpleTypeUsingDollarRef; - expect( result.name, result.people[1].name ); - expect( result.people[1].name, "Test User" ); - } ); - - test( "can serialize object with no properties", ( ) - { - var json = '''[{"\$id":"994910500","\$type":"nomirrorsmap.tests.TypeWithNoProperties"},{"\$ref":"994910500"}]'''; - - var result = new NoMirrorsMap( ).convert( json, new NewtonSoftJsonConverter( ), new ClassConverter( startType: const TypeOf>( ).type ) ); - - - } ); - - test( "can serialize enum", ( ) - { - var json = '''1'''; - - var result = new NoMirrorsMap( ).convert( json, new NewtonSoftJsonConverter( ), new ClassConverter( startType: TestEnum ) ); - - expect(result, TestEnum.Two); - } ); - - test( "Can deserialize", ( ) - { - var converter = new NewtonSoftJsonConverter( ); - var jsonText = _getFileContent( "abstract_class_and_inheritence.json" ); - BaseObjectData result = converter.toBaseObjectData(jsonText); - - assertClassObjectDataTypeNotNull(result); - } ); - } ); - -} - - - -void assertClassObjectDataTypeNotNull(BaseObjectData objectData){ - if(objectData is ClassObjectData){ - var classObjectData = objectData as ClassObjectData; - if(classObjectData.objectType == null) - { - expect( classObjectData.objectType, isNotNull ); - } - classObjectData.properties.forEach((k,v){ - assertClassObjectDataTypeNotNull(v); - }); - }else if (objectData is ListObjectData){ - var listObjectData = objectData as ListObjectData; - listObjectData.values.forEach((v){ - assertClassObjectDataTypeNotNull(v); - }); - }else{ - - } -} - -enum TestEnum{ - One, - Two -} - -abstract class TheAbstractClass -{ - List data; -} - -class InheritedClass extends TheAbstractClass -{ - -} - -class TypeWithNoProperties -{ - -} - -class SimpleTypeUsingDollarRef -{ - String name; - List people; -} - -class NewtonSoftTest -{ - int age; - String gender; -} - -class Person -{ - int id; - List parents; - List children; -} - -class ClassWithDouble -{ - double val; -} - -//{"1|Matthew"} - -class CustomConverterParentTest -{ - CustomConverterTest testProperty; -} - -class CustomConverterTest -{ - int id; - String value; -} - -class CustomList extends ListBase -{ - var innerList = new List( ); - - int get length - => innerList.length; - - void set length( int length ) - { - innerList.length = length; - } - - void operator []=( int index, E value ) { - innerList[index] = value; - } - - E operator []( int index ) => innerList[index]; - - // Though not strictly necessary, for performance reasons - // you should implement add and addAll. - - void add( E value ) - => innerList.add( value ); - - void addAll( Iterable all ) - => innerList.addAll( all ); -} - -class TestObjectWithCustomList -{ - CustomList customList = new CustomList( ); -} - -class NoTypeTestClass -{ - int id; - String firstName; - NoTypeTestPropertyClass testProperty; -} - -class NoTypeTestPropertyClass -{ - int id; - String name; -} - -class User -{ - int id; - String firstName; - String lastName; - String emailAddress; - String mobilePhone; - bool umpire; - - List teamUsers; - SecurityRole securityRole; -} - -class TeamMember -{ - Role role; - User user; -} - -class Role -{ - int id; - String name; -} - -class SecurityRole -{ - int id; - String name; - String description; - AssociationLevel associationLevel; -} - -class AssociationLevel -{ - int id; - String value; -} - -class ClassWithDateTime -{ - DateTime time; -} \ No newline at end of file diff --git a/test/transformer_tests.dart b/test/transformer_tests.dart deleted file mode 100644 index 51c52b1..0000000 --- a/test/transformer_tests.dart +++ /dev/null @@ -1,70 +0,0 @@ -part of nomirrorsmap.tests; - - -class TransformerTests -{ - static String MAP_LIBRARY = ''' - library nomirrorsmap; - - class MapType{ - const MapType(); - } - '''; - - static void run( ) - { - var resolvers = new Resolvers(dartSdkDirectory); - - var phases = [ - [new MapGeneratorTransformer( resolvers)] - ]; - - test("Test",(){ - return applyTransformers( phases, inputs: { - 'nomirrorsmap|lib/nomirrorsmap.dart': MAP_LIBRARY, - 'a|web/main.dart': '''import 'package:nomirrorsmap/nomirrorsmap.dart'; - - @MapType() - class Car extends CarBase{ - int id; - String name; - List values; - List previousVersions; - } - - abstract class CarBase{ - CarType carType; - } - - enum CarType{ - Sport, - Coupe - } - - main() {}''' - }, results: { - 'a|web/main_nomirrorsmap_generated_maps.dart': '''library main_nomirrorsmap_generated_maps_nomirrorsmap_generated_maps; - -import \'package:nomirrorsmap/src/shared/shared.dart\' as nomirrorsmap; -import "main.dart" as main_dart; - -class NoMirrorsMapGeneratedMaps{ - static List load(){ - return [ -new nomirrorsmap.ClassGeneratedMap( main_dart.Car, "Car", () => new main_dart.Car(), { -\'id\': new nomirrorsmap.GeneratedPropertyMap( int, (obj) => obj.id, (obj, value) => obj.id = value ), -\'name\': new nomirrorsmap.GeneratedPropertyMap( String, (obj) => obj.name, (obj, value) => obj.name = value ), -\'values\': new nomirrorsmap.GeneratedPropertyMap( const nomirrorsmap.TypeOf>().type, (obj) => obj.values, (obj, value) => obj.values = value ), -\'previousVersions\': new nomirrorsmap.GeneratedPropertyMap( const nomirrorsmap.TypeOf>().type, (obj) => obj.previousVersions, (obj, value) => obj.previousVersions = value ), -\'carType\': new nomirrorsmap.GeneratedPropertyMap( main_dart.CarType, (obj) => obj.carType, (obj, value) => obj.carType = value ), -}), -new nomirrorsmap.ListGeneratedMap( const nomirrorsmap.TypeOf>().type, String, () => new List() ), -new nomirrorsmap.ListGeneratedMap( const nomirrorsmap.TypeOf>().type, main_dart.Car, () => new List() ), -new nomirrorsmap.EnumGeneratedMap( main_dart.CarType, main_dart.CarType.values ), -]; - } -}''' - } ); - }); - } -} \ No newline at end of file diff --git a/test/type_to_type_objects.dart b/test/type_to_type_objects.dart index 8e182d9..abaccdb 100644 --- a/test/type_to_type_objects.dart +++ b/test/type_to_type_objects.dart @@ -1,130 +1,76 @@ library nomirrorsmap.type_to_type_objects; -import 'dart:collection'; - -class TestEntity -{ - String stringProperty; - int intProperty; - DateTime dateTimeProperty; - double doubleProperty; - bool boolProperty; - num numProperty; +class TestEntity { + String stringProperty; + int intProperty; + DateTime dateTimeProperty; + double doubleProperty; + bool boolProperty; + num numProperty; } -class TestDto -{ - String stringProperty; - int intProperty; - DateTime dateTimeProperty; - double doubleProperty; - bool boolProperty; - num numProperty; +class TestDto { + String stringProperty; + int intProperty; + DateTime dateTimeProperty; + double doubleProperty; + bool boolProperty; + num numProperty; } -class TestEntity2 -{ - TestEntity test; +class TestEntity2 { + TestEntity test; } -class TestDto2 -{ - TestDto test; +class TestDto2 { + TestDto test; } -class ListEntity -{ - List list; +class ListEntity { + List list; } -class ListDto -{ - List list; +class ListDto { + List list; } -class NonPrimitiveListEntity -{ - List list; +class NonPrimitiveListEntity { + List list; } -class NonPrimitiveListDto -{ - List list; +class NonPrimitiveListDto { + List list; } -class CustomListEntity -{ - CustomList list; +class InheritedDto extends TestDto { + int extraProperty; } -class CustomListDto -{ - CustomList list; +class InheritedEntity extends TestEntity { + int extraProperty; } -class CustomList extends ListBase -{ - var innerList = new List( ); - - int get length - => innerList.length; - - void set length( int length ) - { - innerList.length = length; - } - - void operator []=( int index, E value ) { - innerList[index] = value; - } - - E operator []( int index ) => innerList[index]; - - // Though not strictly necessary, for performance reasons - // you should implement add and addAll. - - void add( E value ) - => innerList.add( value ); - - void addAll( Iterable all ) - => innerList.addAll( all ); -} - -class InheritedDto extends TestDto -{ - int extraProperty; -} - -class InheritedEntity extends TestEntity -{ - int extraProperty; +abstract class BaseEntity { + int id; + String name; } - -abstract class BaseEntity -{ - int id; - String name; +abstract class BaseDto { + int id; + String name; } +class ConcreteEntity extends BaseEntity {} -abstract class BaseDto -{ - int id; - String name; -} +class ConcreteDto extends BaseDto {} -class ConcreteEntity extends BaseEntity -{ +class ConcreteWithNoMapEntity extends BaseEntity {} +class SourceType { + int intProperty; + String stringProperty; } -class ConcreteDto extends BaseDto -{ - +class DestType { + int intProperty; } - -class ConcreteWithNoMapEntity extends BaseEntity -{ - -} \ No newline at end of file diff --git a/test/type_to_type_tests.dart b/test/type_to_type_tests.dart index b68ba28..073e684 100644 --- a/test/type_to_type_tests.dart +++ b/test/type_to_type_tests.dart @@ -1,138 +1,134 @@ part of nomirrorsmap.tests; -class TypeToTypeTests -{ - static dynamic map(dynamic obj, Type type, [Map typeMaps = null]){ - return new NoMirrorsMap().convert(obj, new ClassConverter(), new ClassConverter(), [new TypeToTypeManipulator(type, typeMaps)]); - } - - static void run( ) - { - group( 'AutoMapper', ( ) - { - - test( 'Mapping from one type to another with only primitive properties correctly copies string property over', ( ) - { - var dateTime = new DateTime.now( ); - var testEntity = new objects.TestEntity( ) - ..stringProperty = "test" - ..intProperty = 1 - ..dateTimeProperty = dateTime - ..doubleProperty = 1.1 - ..boolProperty = true - ..numProperty = 1; - - objects.TestDto testDto = map( testEntity, objects.TestDto ); - - expect( testDto.stringProperty, "test" ); - expect( testDto.intProperty, 1 ); - expect( testDto.dateTimeProperty, dateTime ); - expect( testDto.doubleProperty, 1.1 ); - expect( testDto.boolProperty, true ); - expect( testDto.numProperty, 1 ); - } ); - - test( 'Mapping from one type to another with a non primitive property correctly maps the non primitive properties', ( ) - { - - var testEntity = new objects.TestEntity( ) - ..stringProperty = "test"; - var testEntity2 = new objects.TestEntity2( ) - ..test = testEntity; - - objects.TestDto2 testDto = map( testEntity2, objects.TestDto2 ); - - expect( "test", testDto.test.stringProperty ); - } ); - - test( 'Mapping an object with a list of primitive types maps correctly', ( ) - { - - var listEntity = new objects.ListEntity( ) - ..list = [ "Hello", "World" ]; - - var result = map( listEntity, objects.ListDto ); - - expect( result.list, isNotNull ); - expect( result.list, new isInstanceOf>( ) ); - expect( result.list[0], "Hello" ); - - } ); - - - test( 'Mapping an object with a list of non primitive types maps correctly', ( ) - { - var nonPrimitiveListEntity = new objects.NonPrimitiveListEntity( ) - ..list = [ new objects.TestEntity( ) - ..stringProperty = "Hello", new objects.TestEntity( ) - ..stringProperty = "World"]; - - var result = map( nonPrimitiveListEntity, objects.NonPrimitiveListDto ); - expect( result.list[0], new isInstanceOf( ) ); - expect( result.list[0].stringProperty, "Hello" ); - } ); - - test( 'Mapping an object with a list maps to custom list', ( ) - { - var customListEntity = new objects.CustomListEntity( ) - ..list = (new objects.CustomList( ) - ..addAll( [ new objects.TestEntity( ) - ..stringProperty = "Hello", new objects.TestEntity( ) - ..stringProperty = "World"] ) ); - - var result = map( customListEntity, objects.CustomListDto ); - expect( result.list, new isInstanceOf>( ) ); - expect( result.list[0].stringProperty, "Hello" ); - } ); - - - test( 'Mapping from one inherited type to another with only primitive properties correctly copies base and extended properties', ( ) - { - var dateTime = new DateTime.now( ); - var testEntity = new objects.InheritedEntity( ) - ..stringProperty = "test" - ..intProperty = 1 - ..dateTimeProperty = dateTime - ..doubleProperty = 1.1 - ..boolProperty = true - ..numProperty = 1 - ..extraProperty = 1; - - objects.InheritedDto testDto = map( testEntity, objects.InheritedDto ); - - expect( testDto.stringProperty, "test" ); - expect( testDto.intProperty, 1 ); - expect( testDto.dateTimeProperty, dateTime ); - expect( testDto.doubleProperty, 1.1 ); - expect( testDto.boolProperty, true ); - expect( testDto.numProperty, 1 ); - expect( testDto.extraProperty, 1 ); - } ); - - - test( 'Mapping from a specific type to a list of works when a type mapping has been declared', () { - var concreteEntity = new objects.ConcreteEntity(); - //TODO: Add type mappings here: - - var testDto = map( concreteEntity, objects.BaseDto, { - objects.ConcreteEntity: objects.ConcreteDto - } ); - - expect(testDto, new isInstanceOf()); - }); - - test( 'Mapping from a specific type to an abstract type throws use exception if no map exists', () { - var concreteEntity = new objects.ConcreteWithNoMapEntity(); - //TODO: Add type mappings here: - - try{ - map( concreteEntity, objects.BaseDto ); - throw "Should throw exception about map"; - } - catch(ex){ - expect(ex, "Are you missing a type map from \"class ${(objects.ConcreteWithNoMapEntity).toString( )}\" to \"abstract class ${(objects.BaseDto).toString( )}\""); - } - }); - } ); - } -} \ No newline at end of file +class TypeToTypeTests { + static dynamic map(dynamic obj, Type type, + [Map typeMaps = null]) { + return new NoMirrorsMap().convert(obj, new ClassConverter(), + new ClassConverter(), [new TypeToTypeManipulator(type, typeMaps)]); + } + + static void run() { + group('AutoMapper', () { + test( + 'Mapping from one type to another with only primitive properties correctly copies string property over', + () { + var dateTime = new DateTime.now(); + var testEntity = new objects.TestEntity() + ..stringProperty = "test" + ..intProperty = 1 + ..dateTimeProperty = dateTime + ..doubleProperty = 1.1 + ..boolProperty = true + ..numProperty = 1; + + objects.TestDto testDto = map(testEntity, objects.TestDto); + + expect(testDto.stringProperty, "test"); + expect(testDto.intProperty, 1); + expect(testDto.dateTimeProperty, dateTime); + expect(testDto.doubleProperty, 1.1); + expect(testDto.boolProperty, true); + expect(testDto.numProperty, 1); + }); + + test( + 'Mapping from one type to another with a non primitive property correctly maps the non primitive properties', + () { + var testEntity = new objects.TestEntity()..stringProperty = "test"; + var testEntity2 = new objects.TestEntity2()..test = testEntity; + + objects.TestDto2 testDto = map(testEntity2, objects.TestDto2); + + expect("test", testDto.test.stringProperty); + }); + + test('Mapping an object with a list of primitive types maps correctly', + () { + var listEntity = new objects.ListEntity()..list = ["Hello", "World"]; + + var result = map(listEntity, objects.ListDto); + + expect(result.list, isNotNull); + expect(result.list, new isInstanceOf>()); + expect(result.list[0], "Hello"); + }); + + test( + 'Mapping an object with a list of non primitive types maps correctly', + () { + var nonPrimitiveListEntity = new objects.NonPrimitiveListEntity() + ..list = [ + new objects.TestEntity()..stringProperty = "Hello", + new objects.TestEntity()..stringProperty = "World" + ]; + + var result = map(nonPrimitiveListEntity, objects.NonPrimitiveListDto); + expect(result.list[0], new isInstanceOf()); + expect(result.list[0].stringProperty, "Hello"); + }); + + test( + 'Mapping from one inherited type to another with only primitive properties correctly copies base and extended properties', + () { + var dateTime = new DateTime.now(); + var testEntity = new objects.InheritedEntity() + ..stringProperty = "test" + ..intProperty = 1 + ..dateTimeProperty = dateTime + ..doubleProperty = 1.1 + ..boolProperty = true + ..numProperty = 1 + ..extraProperty = 1; + + objects.InheritedDto testDto = map(testEntity, objects.InheritedDto); + + expect(testDto.stringProperty, "test"); + expect(testDto.intProperty, 1); + expect(testDto.dateTimeProperty, dateTime); + expect(testDto.doubleProperty, 1.1); + expect(testDto.boolProperty, true); + expect(testDto.numProperty, 1); + expect(testDto.extraProperty, 1); + }); + + test( + 'Mapping from a specific type to a list of works when a type mapping has been declared', + () { + var concreteEntity = new objects.ConcreteEntity(); + + var testDto = map(concreteEntity, objects.BaseDto, + {objects.ConcreteEntity: objects.ConcreteDto}); + + expect(testDto, new isInstanceOf()); + }); + + test( + 'Mapping from a specific type to an abstract type throws use exception if no map exists', + () { + var concreteEntity = new objects.ConcreteWithNoMapEntity(); + + try { + map(concreteEntity, objects.BaseDto); + throw "Should throw exception about map"; + } catch (ex) { + expect( + ex, + contains( + "Are you missing a type map from \"class ${(objects.ConcreteWithNoMapEntity).toString( )}\" to \"abstract class ${(objects.BaseDto).toString( )}\"")); + } + }); + + test( + 'Mapping from one type to another type that dosent have dest property', + () { + var source = new objects.SourceType() + ..intProperty = 1 + ..stringProperty = "2"; + + var result = map(source, objects.DestType); + + expect(result.intProperty, 1); + }); + }); + } +} diff --git a/travis.sh b/travis.sh new file mode 100644 index 0000000..026773c --- /dev/null +++ b/travis.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Fast fail the script on failures. +set -e + +$(dirname -- "$0")/ensure_dartfmt.sh + +# Run the tests. +pub run unittest + +# Install dart_coveralls; gather and send coverage data. +if [ "$COVERALLS_TOKEN" ] && [ "$TRAVIS_DART_VERSION" = "stable" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ]; then + pub global activate dart_coveralls + pub global run dart_coveralls report \ + --retry 2 \ + --exclude-test-files \ + test/all_test.dart +fi