Upgrade dependencies, remove old generator class
This commit is contained in:
parent
e2d167613d
commit
5956302d6f
2 changed files with 5 additions and 286 deletions
|
@ -1,282 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
|
||||||
import 'package:analyzer/dart/element/type.dart';
|
|
||||||
import 'package:angel_serialize/angel_serialize.dart';
|
|
||||||
import 'package:build/build.dart';
|
|
||||||
import 'package:code_builder/code_builder.dart';
|
|
||||||
import 'package:code_builder/dart/core.dart';
|
|
||||||
import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
|
|
||||||
import 'build_context.dart';
|
|
||||||
import 'context.dart';
|
|
||||||
|
|
||||||
class JsonModelGenerator extends GeneratorForAnnotation<Serializable> {
|
|
||||||
final bool autoSnakeCaseNames;
|
|
||||||
final bool autoIdAndDateFields;
|
|
||||||
|
|
||||||
const JsonModelGenerator(
|
|
||||||
{this.autoSnakeCaseNames: true, this.autoIdAndDateFields: true});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> generateForAnnotatedElement(
|
|
||||||
Element element, ConstantReader reader, BuildStep buildStep) async {
|
|
||||||
if (element.kind != ElementKind.CLASS)
|
|
||||||
throw 'Only classes can be annotated with a @Serializable() annotation.';
|
|
||||||
var ctx = await buildContext(
|
|
||||||
element,
|
|
||||||
serializable,
|
|
||||||
buildStep,
|
|
||||||
await buildStep.resolver,
|
|
||||||
autoSnakeCaseNames != false,
|
|
||||||
autoIdAndDateFields != false);
|
|
||||||
var lib = generateSerializerLibrary(ctx);
|
|
||||||
return prettyToSource(lib.buildAst());
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryBuilder generateSerializerLibrary(BuildContext ctx) {
|
|
||||||
var lib = new LibraryBuilder();
|
|
||||||
lib.addMember(generateBaseModelClass(ctx));
|
|
||||||
return lib;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassBuilder generateBaseModelClass(BuildContext ctx) {
|
|
||||||
if (!ctx.originalClassName.startsWith('_'))
|
|
||||||
throw 'Classes annotated with @Serializable() must have names starting with a leading underscore.';
|
|
||||||
|
|
||||||
var genClassName = ctx.modelClassName;
|
|
||||||
var genClass = new ClassBuilder(genClassName,
|
|
||||||
asExtends: new TypeBuilder(ctx.originalClassName));
|
|
||||||
var modelType = new TypeBuilder(genClassName);
|
|
||||||
|
|
||||||
// Now, add all fields to the base class
|
|
||||||
ctx.fields.forEach((field) {
|
|
||||||
genClass.addField(
|
|
||||||
varField(field.name, type: new TypeBuilder(field.type.displayName))
|
|
||||||
..addAnnotation(reference('override')));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create convenience constructor
|
|
||||||
var convenienceConstructor = constructor(ctx.fields.map((field) {
|
|
||||||
return thisField(named(parameter(field.name)));
|
|
||||||
}));
|
|
||||||
genClass.addConstructor(convenienceConstructor);
|
|
||||||
|
|
||||||
// Create toJson
|
|
||||||
Map<String, ExpressionBuilder> toJsonFields = {};
|
|
||||||
|
|
||||||
ctx.fields.forEach((field) {
|
|
||||||
var resolvedName = ctx.resolveFieldName(field.name);
|
|
||||||
ExpressionBuilder value;
|
|
||||||
|
|
||||||
// DateTime
|
|
||||||
if (field.type.name == 'DateTime') {
|
|
||||||
value = reference(field.name).equals(literal(null)).ternary(
|
|
||||||
literal(null), reference(field.name).invoke('toIso8601String', []));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize models
|
|
||||||
else if (serializableTypeChecker.firstAnnotationOf(field.type.element) !=
|
|
||||||
null) {
|
|
||||||
value = reference(field.name).invoke('toJson', []);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anything else
|
|
||||||
else {
|
|
||||||
value = reference(field.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJsonFields[resolvedName] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
var toJson = new MethodBuilder('toJson',
|
|
||||||
returnType: new TypeBuilder('Map', genericTypes: [
|
|
||||||
new TypeBuilder('String'),
|
|
||||||
new TypeBuilder('dynamic')
|
|
||||||
]),
|
|
||||||
returns: map(toJsonFields));
|
|
||||||
genClass.addMethod(toJson);
|
|
||||||
|
|
||||||
// Create factory [name].fromJson
|
|
||||||
var fromJson = new ConstructorBuilder(name: 'fromJson', asFactory: true);
|
|
||||||
fromJson.addPositional(parameter('data', [new TypeBuilder('Map')]));
|
|
||||||
var namedParams =
|
|
||||||
ctx.fields.fold<Map<String, ExpressionBuilder>>({}, (out, field) {
|
|
||||||
var resolvedName = ctx.resolveFieldName(field.name);
|
|
||||||
var mapKey = reference('data')[literal(resolvedName)];
|
|
||||||
ExpressionBuilder value = mapKey;
|
|
||||||
var type = field.type;
|
|
||||||
|
|
||||||
// DateTime
|
|
||||||
if (type.name == 'DateTime') {
|
|
||||||
// map['foo'] is DateTime ? map['foo'] : (map['foo'] is String ? DateTime.parse(map['foo']) : null)
|
|
||||||
var dt = new TypeBuilder('DateTime');
|
|
||||||
value = mapKey.isInstanceOf(dt).ternary(
|
|
||||||
mapKey,
|
|
||||||
(mapKey.isInstanceOf(new TypeBuilder('String')).ternary(
|
|
||||||
new TypeBuilder('DateTime').invoke('parse', [mapKey]),
|
|
||||||
literal(null)))
|
|
||||||
.parentheses());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool done = false;
|
|
||||||
|
|
||||||
// Handle List
|
|
||||||
if (type.toString().contains('List') && type is ParameterizedType) {
|
|
||||||
var listType = type.typeArguments.first;
|
|
||||||
if (listType.element is ClassElement) {
|
|
||||||
var genericClass = listType.element as ClassElement;
|
|
||||||
String fromJsonClassName;
|
|
||||||
bool hasFromJson =
|
|
||||||
genericClass.constructors.any((c) => c.name == 'fromJson');
|
|
||||||
|
|
||||||
if (hasFromJson) {
|
|
||||||
fromJsonClassName = genericClass.displayName;
|
|
||||||
} else {
|
|
||||||
// If it has a serializable annotation, act accordingly.
|
|
||||||
if (serializableTypeChecker.firstAnnotationOf(genericClass) != null) {
|
|
||||||
fromJsonClassName = genericClass.displayName.substring(1);
|
|
||||||
hasFromJson = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for fromJson
|
|
||||||
if (hasFromJson) {
|
|
||||||
var outputType = new TypeBuilder(fromJsonClassName);
|
|
||||||
var x = reference('x');
|
|
||||||
value = mapKey.isInstanceOf(lib$core.List).ternary(
|
|
||||||
mapKey.invoke('map', [
|
|
||||||
new MethodBuilder.closure(
|
|
||||||
returns: x.equals(literal(null)).ternary(
|
|
||||||
literal(null),
|
|
||||||
(x.isInstanceOf(outputType).ternary(
|
|
||||||
x,
|
|
||||||
outputType.newInstance([reference('x')],
|
|
||||||
constructor: 'fromJson')))
|
|
||||||
.parentheses()))
|
|
||||||
..addPositional(parameter('x'))
|
|
||||||
]).invoke('toList', []),
|
|
||||||
literal(null));
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for fromJson
|
|
||||||
if (!done && type.element is ClassElement) {
|
|
||||||
String fromJsonClassName;
|
|
||||||
var genericClass = type.element as ClassElement;
|
|
||||||
bool hasFromJson =
|
|
||||||
genericClass.constructors.any((c) => c.name == 'fromJson');
|
|
||||||
|
|
||||||
if (hasFromJson) {
|
|
||||||
fromJsonClassName = type.displayName;
|
|
||||||
} else {
|
|
||||||
// If it has a serializable annotation, act accordingly.
|
|
||||||
if (serializableTypeChecker.firstAnnotationOf(genericClass) != null) {
|
|
||||||
fromJsonClassName = type.displayName.substring(1);
|
|
||||||
hasFromJson = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for fromJson
|
|
||||||
if (hasFromJson) {
|
|
||||||
var outputType = new TypeBuilder(fromJsonClassName);
|
|
||||||
value = mapKey.equals(literal(null)).ternary(
|
|
||||||
literal(null),
|
|
||||||
(mapKey.isInstanceOf(outputType).ternary(
|
|
||||||
mapKey,
|
|
||||||
outputType
|
|
||||||
.newInstance([mapKey], constructor: 'fromJson')))
|
|
||||||
.parentheses());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle Map...
|
|
||||||
if (!done &&
|
|
||||||
type.toString().contains('Map') &&
|
|
||||||
type is ParameterizedType &&
|
|
||||||
type.typeArguments.length >= 2) {
|
|
||||||
var targetType = type.typeArguments[1];
|
|
||||||
if (targetType.element is ClassElement) {
|
|
||||||
String fromJsonClassName;
|
|
||||||
var genericClass = targetType.element as ClassElement;
|
|
||||||
bool hasFromJson =
|
|
||||||
genericClass.constructors.any((c) => c.name == 'fromJson');
|
|
||||||
|
|
||||||
if (hasFromJson) {
|
|
||||||
fromJsonClassName = targetType.displayName;
|
|
||||||
} else {
|
|
||||||
// If it has a serializable annotation, act accordingly.
|
|
||||||
if (serializableTypeChecker.firstAnnotationOf(genericClass) != null) {
|
|
||||||
fromJsonClassName = targetType.displayName.substring(1);
|
|
||||||
hasFromJson = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for fromJson
|
|
||||||
if (hasFromJson) {
|
|
||||||
var outputType = new TypeBuilder(fromJsonClassName);
|
|
||||||
var v = mapKey[reference('k')];
|
|
||||||
value = mapKey.isInstanceOf(lib$core.Map).ternary(
|
|
||||||
mapKey.property('keys').invoke('fold', [
|
|
||||||
map({}),
|
|
||||||
new MethodBuilder.closure()
|
|
||||||
..addStatements([
|
|
||||||
v
|
|
||||||
.equals(literal(null))
|
|
||||||
.ternary(
|
|
||||||
literal(null),
|
|
||||||
(v.isInstanceOf(outputType).ternary(
|
|
||||||
v,
|
|
||||||
outputType.newInstance([v],
|
|
||||||
constructor: 'fromJson')))
|
|
||||||
.parentheses())
|
|
||||||
.asAssign(reference('out')[reference('k')]),
|
|
||||||
reference('out').asReturn()
|
|
||||||
])
|
|
||||||
..addPositional(parameter('out'))
|
|
||||||
..addPositional(parameter('k'))
|
|
||||||
]),
|
|
||||||
literal(null));
|
|
||||||
} else {
|
|
||||||
value = mapKey
|
|
||||||
.isInstanceOf(lib$core.Map)
|
|
||||||
.ternary(mapKey, literal(null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deerialize models
|
|
||||||
if (!done &&
|
|
||||||
serializableTypeChecker.firstAnnotationOf(field.type.element) !=
|
|
||||||
null) {
|
|
||||||
var typeName = field.type.name;
|
|
||||||
typeName.startsWith('_') ? typeName = typeName.substring(1) : null;
|
|
||||||
var typeBuilder = new TypeBuilder(typeName);
|
|
||||||
value = mapKey.isInstanceOf(typeBuilder).ternary(
|
|
||||||
mapKey, typeBuilder.newInstance([mapKey], constructor: 'fromJson'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return out..[field.name] = value;
|
|
||||||
});
|
|
||||||
fromJson
|
|
||||||
.addStatement(modelType.newInstance([], named: namedParams).asReturn());
|
|
||||||
genClass.addConstructor(fromJson);
|
|
||||||
|
|
||||||
// Create `parse` to just forward
|
|
||||||
var parseMethod = new MethodBuilder('parse',
|
|
||||||
returnType: modelType,
|
|
||||||
returns:
|
|
||||||
modelType.newInstance([reference('map')], constructor: 'fromJson'));
|
|
||||||
parseMethod.addPositional(parameter('map', [new TypeBuilder('Map')]));
|
|
||||||
genClass.addMethod(parseMethod, asStatic: true);
|
|
||||||
|
|
||||||
// Create clone() method...
|
|
||||||
var cloneMethod = new MethodBuilder('clone', returnType: modelType);
|
|
||||||
cloneMethod.addStatement(modelType.newInstance(
|
|
||||||
[reference('toJson').call([])],
|
|
||||||
constructor: 'fromJson').asReturn());
|
|
||||||
genClass.addMethod(cloneMethod);
|
|
||||||
|
|
||||||
return genClass;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,18 @@
|
||||||
name: angel_serialize_generator
|
name: angel_serialize_generator
|
||||||
version: 1.0.0
|
version: 2.0.0-alpha
|
||||||
description: Model serialization generators, designed for use with Angel.
|
description: Model serialization generators, designed for use with Angel.
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/serialize
|
homepage: https://github.com/angel-dart/serialize
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_serialize: ^1.0.0-alpha
|
angel_serialize:
|
||||||
code_builder: ^1.0.0
|
path: ../angel_serialize
|
||||||
|
code_builder: ^2.0.0
|
||||||
id: ^1.0.0
|
id: ^1.0.0
|
||||||
recase: ^1.0.0
|
recase: ^1.0.0
|
||||||
source_gen: ^0.7.0
|
source_gen: ^0.7.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
angel_framework: ^1.0.0
|
angel_framework: ^1.0.0
|
||||||
build_runner: ^0.5.0
|
build_runner: ^0.6.0
|
||||||
test: ">= 0.12.13 < 0.13.0"
|
test: ">= 0.12.13 < 0.13.0"
|
Loading…
Reference in a new issue