generateForAnnotatedElement method

  1. @override
FutureOr<String> generateForAnnotatedElement(
  1. covariant ClassElement element,
  2. ConstantReader annotation,
  3. 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) =>
            !_isAspectParameter(parameterElement) ||
            combineAspects ||
            useSuperParameters,
      )
      .map(
        (parameterElement) => useSuperParameters
            ? 'super.${parameterElement.name}'
            : '''${parameterElement.type.getDisplayString(withNullability: false)} ${parameterElement.name}''',
      )
      .join(', ');
  final superCallParameter = useSuperParameters
      ? ''
      : baseClassConstructor.parameters
          .map(
            (parameterElement) => _isAspectParameter(parameterElement)
                ? _createAspectParameter(
                    allOfAspects,
                    oneOfAspects,
                    excludedAspects,
                    combineAspects,
                  )
                : parameterElement.name,
          )
          .join(', ');
  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 (constructorParameter.isNotEmpty || superCallParameter.isNotEmpty) {
    if (!hasFields) {
      result.writeln();
    }
    result.write('  _\$$className($constructorParameter)');
    if (!useSuperParameters) {
      result.write(' : super($superCallParameter)');
    }
    result.writeln(';');
  }

  if (hasFields) {
    result
      ..writeln('  @override')
      ..writeln('  void initialize() {')
      ..writeln('    super.initialize();');
    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<int> 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(int entity, $parameters);');
  }

  result.writeln('}');

  return result.toString();
}