private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent) {
    if (node.type == ClassNode.CLASS_ANONYMOUS) {
      buffer.append(" {").appendLineSeparator();
      return;
    }

    ClassWrapper wrapper = node.getWrapper();
    StructClass cl = wrapper.getClassStruct();

    int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;
    boolean isDeprecated = cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);
    boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);
    boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0;
    boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0;
    boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0;

    if (isDeprecated) {
      appendDeprecation(buffer, indent);
    }

    if (interceptor != null) {
      String oldName = interceptor.getOldName(cl.qualifiedName);
      appendRenameComment(buffer, oldName, MType.CLASS, indent);
    }

    if (isSynthetic) {
      appendComment(buffer, "synthetic class", indent);
    }

    appendAnnotations(buffer, indent, cl);

    buffer.appendIndent(indent);

    if (isEnum) {
      // remove abstract and final flags (JLS 8.9 Enums)
      flags &= ~CodeConstants.ACC_ABSTRACT;
      flags &= ~CodeConstants.ACC_FINAL;
    }

    List<StructRecordComponent> components = cl.getRecordComponents();
    List<String> permittedSubclassQualifiedNames = cl.getPermittedSubclasses();

    if (components != null) {
      // records are implicitly final
      flags &= ~CodeConstants.ACC_FINAL;
    }

    appendModifiers(buffer, flags, CLASS_ALLOWED, isInterface, CLASS_EXCLUDED);

    if (permittedSubclassQualifiedNames != null && !isEnum) {
      buffer.append("sealed ");
    }
    else if (node.isNonSealed()) {
      buffer.append("non-sealed ");
    }

    if (isEnum) {
      buffer.append("enum ");
    }
    else if (isInterface) {
      if (isAnnotation) {
        buffer.append('@');
      }
      buffer.append("interface ");
    }
    else if (components != null) {
      buffer.append("record ");
    }
    else {
      buffer.append("class ");
    }
    buffer.append(node.simpleName);

    List<TypeAnnotation> typeAnnotations = TypeAnnotation.listFrom(cl);

    GenericClassDescriptor descriptor = getGenericClassDescriptor(cl);
    if (descriptor != null && !descriptor.fparameters.isEmpty()) {
      appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds, typeAnnotations);
    }

    if (components != null) {
      buffer.append('(');
      for (int i = 0; i < components.size(); i++) {
        StructRecordComponent cd = components.get(i);
        if (i > 0) {
          buffer.append(", ");
        }
        boolean varArgComponent = i == components.size() - 1 && isVarArgRecord(cl);
        recordComponentToJava(cd, buffer, varArgComponent);
      }
      buffer.append(')');
    }

    buffer.append(' ');

    if (!isEnum && !isInterface && components == null && cl.superClass != null) {
      VarType supertype = new VarType(cl.superClass.getString(), true);
      List<TypeAnnotation> extendsTypeAnnotations = TargetInfo.SupertypeTarget.extractExtends(typeAnnotations);
      if (!VarType.VARTYPE_OBJECT.equals(supertype)) {
        buffer.append("extends ");
        if (descriptor != null) {
          buffer.append(GenericMain.getGenericCastTypeName(
            descriptor.superclass,
            TypeAnnotationWriteHelper.create(extendsTypeAnnotations))
          );
        }
        else {
          buffer.append(ExprProcessor.getCastTypeName(supertype, TypeAnnotationWriteHelper.create(extendsTypeAnnotations)));
        }
        buffer.append(' ');
      }
    }

    if (!isAnnotation) {
      int[] interfaces = cl.getInterfaces();
      if (interfaces.length > 0) {
        buffer.append(isInterface ? "extends " : "implements ");
        for (int i = 0; i < interfaces.length; i++) {
          if (i > 0) {
            buffer.append(", ");
          }
          List<TypeAnnotation> superTypeAnnotations = TargetInfo.SupertypeTarget.extract(typeAnnotations, i);
          if (descriptor != null) {
            buffer.append(GenericMain.getGenericCastTypeName(
              descriptor.superinterfaces.get(i),
              TypeAnnotationWriteHelper.create(superTypeAnnotations))
            );
          }
          else {
            buffer.append(ExprProcessor.getCastTypeName(
              new VarType(cl.getInterface(i), true),
              TypeAnnotationWriteHelper.create(superTypeAnnotations))
            );
          }
        }
        buffer.append(' ');
      }
    }

    if (permittedSubclassQualifiedNames != null && !permittedSubclassQualifiedNames.isEmpty()) {
      Set<String> qualifiedNested = node.nested.stream()
        .map(nestedNode -> nestedNode.classStruct.qualifiedName)
        .collect(Collectors.toSet());
      boolean allSubClassesAreNested = qualifiedNested.containsAll(permittedSubclassQualifiedNames);
      if (!allSubClassesAreNested) { // only generate permits lists for non-nested classes
        buffer.append("permits ");
        for (int i = 0; i < permittedSubclassQualifiedNames.size(); i++) {
          String qualifiedName = permittedSubclassQualifiedNames.get(i);
          if (i > 0) {
            buffer.append(", ");
          }
          String nestedName = DecompilerContext.getImportCollector().getNestedName(qualifiedName);
          buffer.append(nestedName);
        }
        buffer.append(' ');
      }
    }
    buffer.append('{').appendLineSeparator();
  }
private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent)