in src/org/jetbrains/java/decompiler/main/ClassWriter.java [428:572]
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 = cl.getSignature();
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
DecompilerContext.setProperty(DecompilerContext.IN_CLASS_TYPE_PARAMS, "1");
try {
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds, typeAnnotations);
} finally {
DecompilerContext.setProperty(DecompilerContext.IN_CLASS_TYPE_PARAMS, "0");
}
}
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 ");
buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? supertype : descriptor.superclass, 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);
buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? new VarType(cl.getInterface(i), true) : descriptor.superinterfaces.get(i), 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();
}