private emitInterfaceMembersForProxyOrDatatype()

in packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts [901:1005]


  private emitInterfaceMembersForProxyOrDatatype(
    ifc: spec.InterfaceType | spec.ClassType,
    datatype: boolean,
    proxy: boolean,
  ): void {
    // The key is in the form 'method.name;parameter1;parameter2;' etc
    const methods = new Map<string, MethodDefinition>();
    /*
          Only get the first declaration encountered, and keep it if it is abstract. The list contains ALL
          methods and properties encountered, in the order encountered. An abstract class can have concrete
          implementations. Therefore, we only generate methods/properties if the first member encountered
          is unimplemented.
        */
    const excludedMethod: string[] = []; // Keeps track of the methods we already ran into and don't want to emit
    const excludedProperties: string[] = []; // Keeps track of the properties we already ran into and don't want to emit
    const properties: { [name: string]: PropertyDefinition } = {};
    const collectAbstractMembers = (
      currentType: spec.InterfaceType | spec.ClassType,
    ) => {
      for (const prop of currentType.properties ?? []) {
        if (!excludedProperties.includes(prop.name)) {
          // If we have never run into this property before and it is abstract, we keep it
          if (prop.abstract) {
            properties[prop.name] = { prop, definingType: currentType };
          }
          excludedProperties.push(prop.name);
        }
      }

      for (const method of currentType.methods ?? []) {
        let methodParameters = '';
        if (method.parameters) {
          method.parameters.forEach((param) => {
            methodParameters += `;${this.typeresolver.toDotNetType(
              param.type,
            )}`;
          });
        }
        if (!excludedMethod.includes(`${method.name}${methodParameters}`)) {
          // If we have never run into this method before and it is abstract, we keep it
          if (method.abstract) {
            methods.set(`${method.name}${methodParameters}`, {
              method,
              definingType: currentType,
            });
          }
          excludedMethod.push(`${method.name}${methodParameters}`);
        }
      }

      const bases = new Array<spec.NamedTypeReference>();
      bases.push(
        ...(currentType.interfaces ?? []).map((iface) => this.findType(iface)),
      );
      if (spec.isClassType(currentType) && currentType.base) {
        bases.push(this.findType(currentType.base));
      }
      for (const base of bases) {
        const type = this.findType(base.fqn);
        if (
          type.kind !== spec.TypeKind.Interface &&
          type.kind !== spec.TypeKind.Class
        ) {
          throw new Error(
            `Base interfaces of an interface must be an interface or a class (${base.fqn} is of type ${type.kind})`,
          );
        }
        collectAbstractMembers(type);
      }
    };
    collectAbstractMembers(ifc);

    // emit all properties
    for (const propName of Object.keys(properties)) {
      const prop = clone(properties[propName]);
      prop.prop.abstract = false;
      this.emitProperty(ifc, prop.prop, prop.definingType, datatype, proxy);
    }
    // emit all the methods
    for (const methodNameAndParameters of methods.keys()) {
      const originalMethod = methods.get(methodNameAndParameters);
      if (originalMethod) {
        const method = clone(originalMethod);
        method.method.abstract = false;
        this.emitMethod(
          ifc,
          method.method,
          method.definingType,
          /* emitForProxyOrDatatype */ true,
        );

        for (const overloadedMethod of this.createOverloadsForOptionals(
          method.method,
        )) {
          overloadedMethod.abstract = false;
          this.emitMethod(
            ifc,
            overloadedMethod,
            method.definingType,
            /* emitForProxyOrDatatype */ true,
          );
        }
      }
    }
  }