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
|
||||
version: 1.0.0
|
||||
version: 2.0.0-alpha
|
||||
description: Model serialization generators, designed for use with Angel.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/serialize
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
angel_serialize: ^1.0.0-alpha
|
||||
code_builder: ^1.0.0
|
||||
angel_serialize:
|
||||
path: ../angel_serialize
|
||||
code_builder: ^2.0.0
|
||||
id: ^1.0.0
|
||||
recase: ^1.0.0
|
||||
source_gen: ^0.7.0
|
||||
dev_dependencies:
|
||||
angel_framework: ^1.0.0
|
||||
build_runner: ^0.5.0
|
||||
build_runner: ^0.6.0
|
||||
test: ">= 0.12.13 < 0.13.0"
|
Loading…
Reference in a new issue