generateForAnnotatedElement method
FutureOr<String>
generateForAnnotatedElement(
- covariant ClassElement element,
- ConstantReader annotation,
- 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
FutureOr<String> generateForAnnotatedElement(
covariant ClassElement element,
ConstantReader annotation,
BuildStep buildStep,
) async {
final className = element.name;
final classConstructor = element.unnamedConstructor!;
final combineAspects = classConstructor.parameters.any(_isAspectParameter);
final objectValue = annotation.objectValue;
final baseClassType = objectValue.getField('base')!.toTypeValue()!;
final realBaseClassName = baseClassType.element!.name;
final isBaseClassEntityProcessingSystem =
realBaseClassName == 'EntityProcessingSystem';
final baseClassTypeParameters =
(baseClassType.element! as ClassElement).typeParameters;
final mapper = _getValues(objectValue, 'mapper');
final systems = _getValues(objectValue, 'systems');
final managers = _getValues(objectValue, 'manager');
final allOfAspects = _getValues(objectValue, 'allOf');
final oneOfAspects = _getValues(objectValue, 'oneOf');
final excludedAspects = _getValues(objectValue, 'exclude');
final shouldCreateCustomProcessEntity = isBaseClassEntityProcessingSystem &&
(allOfAspects.isNotEmpty || oneOfAspects.isNotEmpty);
final baseClassName =
shouldCreateCustomProcessEntity ? 'EntitySystem' : realBaseClassName;
final baseClassConstructor =
(annotation.read('base').typeValue.element! as ClassElement)
.unnamedConstructor!;
final hasGeneratedAspects = allOfAspects.isNotEmpty ||
oneOfAspects.isNotEmpty ||
excludedAspects.isNotEmpty;
final useSuperParameters = !hasGeneratedAspects;
final constructorParameter = [
baseClassConstructor.parameters
.where((parameterElement) => parameterElement.isRequiredPositional)
.where(
(parameterElement) =>
!_isAspectParameter(parameterElement) ||
combineAspects ||
useSuperParameters,
)
.map(
(parameterElement) => useSuperParameters
? 'super.${parameterElement.name}'
: '''${parameterElement.type.getDisplayString()} ${parameterElement.name}''',
)
.join(', '),
];
final superCallParameter = useSuperParameters
? ''
: baseClassConstructor.parameters
.where((parameterElement) => parameterElement.isPositional)
.map(
(parameterElement) => _isAspectParameter(parameterElement)
? _createAspectParameter(
allOfAspects,
oneOfAspects,
excludedAspects,
combineAspects,
)
: parameterElement.name,
)
.join(', ');
final needsConstructor =
constructorParameter[0].isNotEmpty || superCallParameter.isNotEmpty;
if (needsConstructor) {
final optionalPositionalParameters = baseClassConstructor.parameters
.where((parameter) => parameter.isOptionalPositional)
.map((parameter) => 'super.${parameter.name}')
.join(', ');
if (optionalPositionalParameters.isNotEmpty) {
constructorParameter.add('[$optionalPositionalParameters]');
}
final optionalNamedParameters = baseClassConstructor.parameters
.where((parameter) => parameter.isNamed)
.map(
(parameter) =>
'''${parameter.isRequired ? 'required ' : ''}super.${parameter.name}''',
)
.join(', ');
if (optionalNamedParameters.isNotEmpty) {
constructorParameter.add('{$optionalNamedParameters}');
}
}
final components = {...allOfAspects, ...mapper};
final optionalComponents = {...oneOfAspects};
final mapperDeclarations = components
.map(
(component) =>
' late final Mapper<$component> ${_toMapperName(component)};',
)
.join('\n');
final optionalMapperDeclarations = optionalComponents
.map(
(component) =>
''' late final OptionalMapper<$component> ${_toMapperName(component)};''',
)
.join('\n');
final systemDeclarations = systems
.map((system) => ' late final $system ${_toVariableName(system)};')
.join('\n');
final managerDeclarations = managers
.map((manager) => ' late final $manager ${_toVariableName(manager)};')
.join('\n');
final mapperInitializations = components
.map(
(component) =>
' ${_toMapperName(component)} = Mapper<$component>(world);',
)
.join('\n');
final optionalMapperInitializations = optionalComponents
.map(
(component) =>
''' ${_toMapperName(component)} = OptionalMapper<$component>(world);''',
)
.join('\n');
final systemInitializations = systems
.map(
(system) =>
' ${_toVariableName(system)} = world.getSystem<$system>();',
)
.join('\n');
final managerInitializations = managers
.map(
(manager) =>
' ${_toVariableName(manager)} = world.getManager<$manager>();',
)
.join('\n');
final result = baseClassTypeParameters.isEmpty
? StringBuffer('abstract class _\$$className extends $baseClassName {')
: StringBuffer(
'''abstract class _\$$className<${_baseClassBoundedTypeParameters(baseClassTypeParameters)}> extends $baseClassName<${_baseClassUnboundedTypeParameters(baseClassTypeParameters)}> {''',
);
final hasFields =
_declaresFields(components, optionalComponents, systems, managers);
if (hasFields) {
result.writeln();
if (components.isNotEmpty) {
result.writeln(mapperDeclarations);
}
if (optionalComponents.isNotEmpty) {
result.writeln(optionalMapperDeclarations);
}
if (systems.isNotEmpty) {
result.writeln(systemDeclarations);
}
if (managers.isNotEmpty) {
result.writeln(managerDeclarations);
}
}
if (needsConstructor) {
if (!hasFields) {
result.writeln();
}
final constructorParameterString =
constructorParameter.where((param) => param.isNotEmpty).join(', ');
result.write(' _\$$className($constructorParameterString)');
if (!useSuperParameters) {
result.write(' : super($superCallParameter)');
}
result.writeln(';');
}
if (hasFields) {
result
..writeln(' @override')
..writeln(' void initialize(World world) {')
..writeln(' super.initialize(world);');
if (components.isNotEmpty) {
result.writeln(mapperInitializations);
}
if (optionalComponents.isNotEmpty) {
result.writeln(optionalMapperInitializations);
}
if (systems.isNotEmpty) {
result.writeln(systemInitializations);
}
if (managers.isNotEmpty) {
result.writeln(managerInitializations);
}
result.writeln(' }');
}
if (shouldCreateCustomProcessEntity) {
result
..writeln(' @override')
..writeln(' void processEntities(Iterable<Entity> entities) {');
for (final aspect in allOfAspects.followedBy(oneOfAspects)) {
final mapperName = _toMapperName(aspect);
result.writeln(' final $mapperName = this.$mapperName;');
}
final arguments = allOfAspects
.followedBy(oneOfAspects)
.map((e) => '${_toMapperName(e)}[entity]')
.join(', ');
result
..writeln(' for (final entity in entities) {')
..writeln(' processEntity(entity, $arguments);')
..writeln(' }')
..writeln(' }');
final parameters = [
...allOfAspects.map((e) => '$e ${_toVariableName(e)}'),
...oneOfAspects.map((e) => '$e? ${_toVariableName(e)}'),
].join(', ');
result.writeln(' void processEntity(Entity entity, $parameters);');
}
result.writeln('}');
return result.toString();
}