generateForAnnotatedElement method
Future<String>
generateForAnnotatedElement(
- covariant ClassElement element,
- ConstantReader cr,
- BuildStep buildStep
Implement to return source code to generate for element
.
This method is invoked based on finding elements annotated with an
instance of T
. The annotation
is provided as a ConstantReader
.
Supported return values include a single String or multiple String instances within an Iterable or Stream. It is also valid to return a Future of String, Iterable, or Stream.
Implementations should return null
when no content is generated. Empty
or whitespace-only String instances are also ignored.
Implementation
@override
Future<String> generateForAnnotatedElement(
covariant ClassElement element, ConstantReader cr, BuildStep buildStep) async {
var superTypes = element.allSupertypes.where((st) =>
!{'Object', 'SerializableMap', 'Map'}.contains(st.element.name));
var stAccessors = superTypes.expand((st) => st.accessors);
var stMethods = superTypes.expand((st) => st.methods);
var stFields = superTypes.expand((st) => st.element.fields.where((f) => !f.isStatic));
var methodCheck = (MethodElement e) => !e.isStatic && !e.isOperator;
var accessorCheck = (ElementKind type) => (a) => a.kind == type && !a.isStatic && !a.isOperator && a.name != 'keys';
var className = element.name;
var fields = []
..addAll(_distinctByName<FieldElement>(element.fields.where((f) => !f.isStatic))
..addAll(stFields));
var accessors = _distinctByName<PropertyAccessorElement>([...element.accessors, ...stAccessors]);
var getters = _distinctByName<PropertyAccessorElement>(accessors.where(accessorCheck(ElementKind.GETTER)));
var setters = _distinctByName<PropertyAccessorElement>(accessors.where(accessorCheck(ElementKind.SETTER)));
var methods = _distinctByName<MethodElement>([...element.methods.where(methodCheck), ...stMethods.where(methodCheck)]);
var typeGenerics = _distinctByName<TypeParameterElement>(element.typeParameters);
return '''mixin _\$${className}Serializable${typeGenerics.isNotEmpty ? '<' + typeGenerics.map((x) => x.declaration.name).join(',') + '>' : ''} on SerializableMap {
${getters.map((g) => '${g.returnType.getDisplayString(withNullability: true)} get ${g.name};').join('\n')}
${setters.map((s) => 'set ${s.displayName}(${s.type.normalParameterTypes[0].getDisplayString(withNullability: true)} v);').join('\n')}
${methods.map((m) => '${m.returnType.getDisplayString(withNullability: true)} ${m.name}(${_renderParameters(m.parameters)});').join('\n')}
operator [](Object? __key) {
switch(__key) {
${getters.map((a) => "case '${_getFieldName(a)}': return ${a.name};").join('\n')}
${methods.map((a) => "case '${a.name}': return ${a.name};").join('\n')}
}
throwFieldNotFoundException(__key, '$className');
}
operator []=(Object? __key, __value) {
switch(__key) {
${setters.map((a) => "case '${_getFieldName(a)}': ${a.displayName} = ${_renderSetterValue(a)}; return;").join('\n')}
}
throwFieldNotFoundException(__key, '$className');
}
Iterable<String> get keys => ${useClassMirrors ? '${className}ClassMirror.fields?.keys ?? [];' : 'const [${fields.map((f) => "'${f.name}'").join(',')}];'}
}''';
}