Skip to content

Commit

Permalink
坑爹的反射机制
Browse files Browse the repository at this point in the history
  • Loading branch information
huanghaiyang committed Dec 14, 2019
1 parent ae7fa21 commit 30ace4f
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 35 deletions.
1 change: 1 addition & 0 deletions lib/src/exception/Exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export 'package:Q/src/exception/RequiredFieldNotFoundException.dart';
export 'package:Q/src/exception/RequiredMethodNotFoundException.dart';
export 'package:Q/src/exception/RouterNotFoundException.dart';
export 'package:Q/src/exception/RouterNotFoundOfRouterChainException.dart';
export 'package:Q/src/exception/SideEffectModelReflectFunctionParameterNotMatchClassFieldException.dart';
export 'package:Q/src/exception/SizeUnitParseException.dart';
export 'package:Q/src/exception/TimeUnitParseException.dart';
export 'package:Q/src/exception/UnExpectedRequestApplicationJsonException.dart';
Expand Down
17 changes: 17 additions & 0 deletions lib/src/exception/RequiredInitialValueMustBeProvidedException.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class RequiredInitialValueMustBeProvidedException extends Exception {
factory RequiredInitialValueMustBeProvidedException({String message, String name}) =>
_RequiredInitialValueMustBeProvidedException(message: message, name: name);
}

class _RequiredInitialValueMustBeProvidedException implements RequiredInitialValueMustBeProvidedException {
final message;

final String name;

_RequiredInitialValueMustBeProvidedException({this.message, this.name});

String toString() {
if (message == null) return "Exception: required initial value must be provided: '${this.name}'";
return "Exception: $message";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class SideEffectModelReflectFunctionParameterNotMatchClassFieldException extends Exception {
factory SideEffectModelReflectFunctionParameterNotMatchClassFieldException({String message, String name}) =>
_SideEffectModelReflectFunctionParameterNotMatchClassFieldException(message: message, name: name);
}

class _SideEffectModelReflectFunctionParameterNotMatchClassFieldException
implements SideEffectModelReflectFunctionParameterNotMatchClassFieldException {
final message;

final String name;

_SideEffectModelReflectFunctionParameterNotMatchClassFieldException({this.message, this.name});

String toString() {
if (message == null) return "Exception: The field named: '${this.name}' not found.";
return "Exception: $message";
}
}
46 changes: 19 additions & 27 deletions lib/src/helpers/reflect/ClassTransformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,25 @@ import 'dart:mirrors';

import 'package:Q/Q.dart';
import 'package:Q/src/exception/RequiredAnnotationNotFoundException.dart';
import 'package:Q/src/exception/RequiredInitialValueMustBeProvidedException.dart';
import 'package:Q/src/utils/SymbolUtil.dart';

class ClassTransformer {
static final String FUNC_FIELD = 'func';

static dynamic fromMap(Map data, Type clazz) {
ClassMirror classMirror = reflectClass(clazz);
InstanceMirror instanceMirror = classMirror.newInstance(Symbol.empty, []);
InstanceMirror sideEffectModelMirror = classMirror.metadata.firstWhere((InstanceMirror instanceMirror) {
return instanceMirror.type == reflectClass(SideEffectModel);
});
if (sideEffectModelMirror != null) {
InstanceMirror func = sideEffectModelMirror.getField(Symbol(FUNC_FIELD));
if (func != null) {
FunctionTypeMirror functionTypeMirror = func.type;
List<ParameterMirror> positionalParameters = List.from(functionTypeMirror.parameters.where((ParameterMirror parameterMirror) {
return !parameterMirror.isOptional && !parameterMirror.isNamed;
}));
List<ParameterMirror> namedParameters = List.from(functionTypeMirror.parameters.where((ParameterMirror parameterMirror) {
return parameterMirror.isNamed;
}));
List positionalArguments = List.from(positionalParameters.map((ParameterMirror parameterMirror) {
return reflectParameterValue(data, parameterMirror);
}));
Map namedArguments = Map();
namedParameters.forEach((ParameterMirror parameterMirror) {
namedArguments[parameterMirror.simpleName] = reflectParameterValue(data, parameterMirror);
functionTypeMirror.parameters.forEach((ParameterMirror parameterMirror) {
instanceMirror.setField(parameterMirror.simpleName, reflectParameterValue(data, parameterMirror, instanceMirror));
});
return Function.apply(func.reflectee, positionalArguments, Map<Symbol, dynamic>.from(namedArguments));
} else {
throw RequiredFieldNotFoundException(name: FUNC_FIELD);
}
Expand All @@ -42,7 +33,6 @@ class ClassTransformer {
}
Model model = modelAnnotationMirror.reflectee;
Map<String, Type> rules = model.rules;
InstanceMirror instanceMirror = classMirror.newInstance(Symbol.empty, []);
for (var key in data.keys) {
if (rules.containsKey(key)) {
dynamic value = data[key];
Expand All @@ -51,11 +41,14 @@ class ClassTransformer {
instanceMirror.setField(Symbol(key), value);
}
}
return instanceMirror.reflectee;
}
return instanceMirror.reflectee;
}

static dynamic reflectParameterValue(Map data, ParameterMirror parameterMirror) {
static dynamic reflectParameterValue(Map data, ParameterMirror parameterMirror, InstanceMirror instanceMirror) {
if (!instanceMirror.type.declarations.containsKey(parameterMirror.simpleName)) {
throw SideEffectModelReflectFunctionParameterNotMatchClassFieldException(name: SymbolUtil.toChars(parameterMirror.simpleName));
}
for (String key in data.keys) {
if (SymbolUtil.toChars(parameterMirror.simpleName) == key) {
dynamic value = data[key];
Expand All @@ -64,21 +57,20 @@ class ClassTransformer {
if (parameterClassMirror == reflectClass(List) || parameterClassMirror == reflectClass(Set)) {
Type subType = ReflectHelper.getSubType(parameterType);
if (hasModel(subType)) {
/**
* TODO type 'List<dynamic>' is not a subtype of type 'List<Person>'
*
* fuck
*/
dynamic initialValue = instanceMirror.getField(Symbol(key)).reflectee;
if (initialValue == null) {
throw RequiredInitialValueMustBeProvidedException(name: key);
}
if (parameterClassMirror == reflectClass(List)) {
List collection = parameterClassMirror.newInstance(Symbol.empty, []).reflectee;
List.from(value).forEach((dynamic val) {
collection.add(fromMap(val, subType));
initialValue.add(fromMap(val, subType));
});
return collection;
return initialValue;
} else if (parameterClassMirror == reflectClass(Set)) {
return Set.from(Set.from(value).map((dynamic val) {
return fromMap(val, subType);
}));
Set.from(value).forEach((dynamic val) {
initialValue.add(fromMap(val, subType));
});
return initialValue;
}
} else {
return ReflectHelper.reflectCollection(parameterMirror.type.reflectedType, subType, value);
Expand Down
13 changes: 13 additions & 0 deletions lib/src/helpers/reflect/ReflectHelper.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:mirrors';

import 'package:Q/src/utils/SymbolUtil.dart';

typedef ParameterAnnotationCallback = void Function(ParameterMirror parameterMirror, InstanceMirror instanceMirror);

class ReflectHelper {
Expand Down Expand Up @@ -144,4 +146,15 @@ class ReflectHelper {
}
});
}

static DeclarationMirror getDeclaration(Type clazz, String name) {
Map<Symbol, DeclarationMirror> mirrors = reflectClass(clazz).declarations;
Symbol symbol = mirrors.keys.firstWhere((Symbol symbol) {
return SymbolUtil.toChars(symbol) == name;
});
if (symbol != null) {
return mirrors[symbol];
}
return null;
}
}
27 changes: 19 additions & 8 deletions test/ClassTransformer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,38 @@ import 'package:Q/Q.dart';
import 'package:Q/src/helpers/reflect/ClassTransformer.dart';
import 'package:test/test.dart';

Person createPerson(String name, int id, int age, List<Person> friends, {List<String> nicknames, List<Person> girlFriends}) {
return Person._(name: name, id: id, age: age, friends: friends, nicknames: nicknames, girlFriends: girlFriends);
}
void createPerson(String name, int id, int age, List<Person> friends, {List<String> nicknames, List<Person> girlFriends}) {}

//@Model({'name': String, 'int': int, 'age': num, 'friends': List, 'nicknames': List})
@SideEffectModel(createPerson)
class Person {
int id;
String name;
num age;
List<Person> friends;
List<String> nicknames;
List<Person> girlFriends;
List<Person> friends = List();
List<String> nicknames = List();
List<Person> girlFriends = List();

Person();

Person._({this.name, this.id, this.age, this.friends, this.nicknames, this.girlFriends});

@override
String toString() {
return 'Person{id: $id, name: $name, age: $age, friends: $friends, nicknames: $nicknames, girlFriends: $girlFriends}';
}
}

void main() {
group('ClassTransformer tests', () {
test('verify', () async {
Person spiderMan = Person._(name: 'peter');
Person spiderMan = Person._(
name: 'peter',
id: 10,
age: 17,
friends: [Person._(id: 0, name: 'iron man'), Person._(id: 1, name: 'thor')],
nicknames: ["spider", "kid", 'monkey man'],
girlFriends: [Person._(id: 11, name: 'spider girl')]);

dynamic result = await ClassTransformer.fromMap({
"name": "peter",
Expand All @@ -38,7 +49,7 @@ void main() {
]
}, Person);
if (result is Person) {
expect(result.name, spiderMan.name);
expect(result.toString(), spiderMan.toString());
}
});
});
Expand Down

0 comments on commit 30ace4f

Please # to comment.