createMorphy function
String
createMorphy(
- bool isAbstract,
- List<
NameTypeClassComment> allFields, - String className,
- String classComment,
- List<
Interface> interfacesFromImplements, - List<
Interface> interfacesAllInclSubInterfaces, - List<
NameTypeClassComment> classGenerics, - bool hasConstContructor,
- bool generateJson,
- bool hidePublicConstructor,
- List<
Interface> explicitForJson, - bool nonSealed,
- bool explicitToJson,
- bool generateCompareTo,
- bool generateCopyWithFn,
- List<
FactoryMethodInfo> factoryMethods, - Map<
String, dynamic> allAnnotatedClasses,
Implementation
String createMorphy(
bool isAbstract,
List<NameTypeClassComment> allFields,
String className,
String classComment,
List<Interface> interfacesFromImplements,
List<Interface> interfacesAllInclSubInterfaces,
List<NameTypeClassComment> classGenerics,
bool hasConstContructor,
bool generateJson,
bool hidePublicConstructor,
List<Interface> explicitForJson,
bool nonSealed,
bool explicitToJson,
bool generateCompareTo,
bool generateCopyWithFn,
List<FactoryMethodInfo> factoryMethods,
Map<String, dynamic> allAnnotatedClasses,
) {
//recursively go through otherClasses and get my fieldnames &
var sb = StringBuffer();
var classNameTrimmed = className.replaceAll("\$", "");
sb.write(getClassComment(interfacesFromImplements, classComment));
if (generateJson) {
sb.writeln(createJsonSingleton(classNameTrimmed, classGenerics));
sb.writeln(
createJsonHeader(
className,
classGenerics,
hidePublicConstructor,
explicitToJson,
generateCompareTo,
isAbstract,
),
);
}
sb.write(
getClassDefinition(
isAbstract: isAbstract,
nonSealed: nonSealed,
className: className,
),
);
if (classGenerics.isNotEmpty) {
sb.write(getClassGenerics(classGenerics));
}
// Handle extends and implements
if (!isAbstract || (isAbstract && nonSealed)) {
// For concrete classes or non-sealed abstract classes ($-prefixed)
// If factory methods exist, implement instead of extend
if (factoryMethods.isNotEmpty) {
// Consolidate the main class and interfaces into single implements clause
var mainClassName = className;
if (classGenerics.isNotEmpty) {
// For implements clause, use generic type parameters without bounds
var typeParams = classGenerics.map((g) => g.name).join(', ');
mainClassName += '<$typeParams>';
}
var allImplements = <String>[
mainClassName,
]; // Keep the original $ClassName with generics
if (interfacesFromImplements.isNotEmpty) {
allImplements.addAll(
interfacesFromImplements.map((e) {
var type = e.interfaceName.replaceAll("\$", "");
if (e.typeParams.isEmpty) {
return type;
}
return "${type}<${e.typeParams.map((e) => e.type).join(", ")}>";
}),
);
}
sb.write(" implements ${allImplements.join(', ')}");
} else {
sb.write(" extends ${className}");
if (classGenerics.isNotEmpty) {
sb.write(getExtendsGenerics(classGenerics));
}
if (interfacesFromImplements.isNotEmpty) {
sb.write(getImplements(interfacesFromImplements, className));
}
}
} else {
// For sealed abstract classes, only add implements for interfaces
if (interfacesFromImplements.isNotEmpty) {
sb.write(getImplements(interfacesFromImplements, className));
}
}
sb.writeln(" {");
if (isAbstract) {
sb.writeln(getPropertiesAbstract(allFields));
// Add abstract copyWith methods for sealed classes
var selfInterface = Interface.fromGenerics(
className,
classGenerics.map((e) => NameType(e.name, e.type)).toList(),
allFields,
);
sb.writeln(
MethodGenerator.generateAbstractCopyWithMethods(
interfaceFields: selfInterface.fields,
interfaceName: selfInterface.interfaceName,
interfaceGenerics: selfInterface.typeParams,
generateCopyWithFn: generateCopyWithFn,
),
);
} else {
sb.writeln(getProperties(allFields));
sb.write(getClassComment(interfacesFromImplements, classComment));
//constructor
// var constructorName = getConstructorName(classNameTrimmed, hidePublicConstructor);
if (allFields.isEmpty) {
if (!hidePublicConstructor) {
sb.writeln("${classNameTrimmed}();");
sb.writeln('\n');
} else {
sb.writeln("${classNameTrimmed}._();");
}
} else {
//public constructor
if (!hidePublicConstructor) {
sb.writeln("${classNameTrimmed}({");
sb.writeln(getConstructorRows(allFields));
sb.writeln("}) ${getInitializer(allFields)};");
}
//the json needs a public constructor, we add this if public constructor is hidden
if (hidePublicConstructor && generateJson) {
sb.writeln("${classNameTrimmed}.forJsonDoNotUse({");
sb.writeln(getConstructorRows(allFields));
sb.writeln("}) ${getInitializer(allFields)};");
}
//only write a private constructor when hidePublicConstructor is true
if (hidePublicConstructor) {
sb.writeln("${classNameTrimmed}._({");
sb.writeln(getConstructorRows(allFields));
sb.writeln("}) ${getInitializer(allFields)};");
sb.writeln('\n');
}
if (hasConstContructor) {
sb.writeln("const ${classNameTrimmed}.constant({");
sb.writeln(getConstructorRows(allFields));
sb.writeln("}) ${getInitializer(allFields)};");
sb.writeln('\n');
}
// Generate factory methods
for (var factory in factoryMethods) {
sb.writeln(generateFactoryMethod(factory, classNameTrimmed, allFields));
}
sb.writeln(getToString(allFields, classNameTrimmed));
}
sb.writeln('\n');
sb.writeln(getHashCode(allFields));
sb.writeln('\n');
sb.writeln(getEquals(allFields, classNameTrimmed));
sb.writeln('\n');
}
//
var interfacesX = [
...interfacesAllInclSubInterfaces,
Interface.fromGenerics(
className,
classGenerics.map((e) => NameType(e.name, e.type ?? e.name)).toList(),
allFields,
),
];
// Create list of known Morphy classes early so it can be used in method generators
var knownClasses = [
...interfacesAllInclSubInterfaces.map(
(i) => i.interfaceName.replaceAll("\$", ""),
),
classNameTrimmed,
...allAnnotatedClasses.keys.map((name) => name.replaceAll("\$", "")),
].toSet().toList();
if (!isAbstract || (isAbstract && nonSealed)) {
interfacesX.where((element) => !element.isExplicitSubType).forEach((x) {
sb.writeln(
MethodGenerator.generateCopyWithMethods(
classFields: allFields,
interfaceFields: x.fields,
interfaceName: x.interfaceName,
className: className,
isClassAbstract: isAbstract,
interfaceGenerics: x.typeParams,
generateCopyWithFn: generateCopyWithFn,
knownClasses: knownClasses,
classGenerics: classGenerics
.map((e) => NameType(e.name, e.type))
.toList(),
nonSealed: nonSealed,
hidePublicConstructor: hidePublicConstructor,
),
);
// Generate changeTo methods for inherited interfaces (upward conversion: child to parent)
// Skip generation for abstract interfaces that can't be instantiated unless it's for non-sealed classes
if ((x.interfaceName != classNameTrimmed &&
!NameCleaner.isAbstract(x.interfaceName)) ||
(nonSealed && NameCleaner.isAbstract(x.interfaceName))) {
sb.writeln(
MethodGenerator.generateChangeToMethods(
classFields: allFields,
interfaceFields: x.fields,
interfaceName: x.interfaceName,
className: className,
isClassAbstract: isAbstract,
interfaceGenerics: x.typeParams,
knownClasses: knownClasses,
isInterfaceSealed: x.isSealed,
classGenerics: classGenerics
.map((e) => NameType(e.name, e.type))
.toList(),
nonSealed: nonSealed,
hidePublicConstructor: hidePublicConstructor,
interfaceHidePublicConstructor: x.hidePublicConstructor,
),
);
}
});
}
if (generateJson) {
// sb.writeln("// $classGenerics");
// sb.writeln("//interfacesX");
// sb.writeln("//explicitForJson");
sb.writeln(commentEveryLine(interfacesX.map((e) => e.toString()).join()));
sb.writeln(commentEveryLine(explicitForJson.join("\n").toString()));
sb.writeln(generateFromJsonHeader(className));
sb.writeln(
generateFromJsonBody(
className,
classGenerics,
explicitForJson,
isAbstract,
),
);
sb.writeln(generateToJson(className, classGenerics, isAbstract));
sb.writeln(generateToJsonLean(className, isAbstract));
}
sb.writeln("}");
if ((!isAbstract || (isAbstract && nonSealed)) &&
(!className.startsWith('\$\$') ||
(className.startsWith('\$\$') && !isAbstract)) &&
generateCompareTo) {
// Create a list of all known classes from the interfaces
sb.writeln(
generateCompareExtension(
isAbstract,
className,
classNameTrimmed,
allFields,
interfacesAllInclSubInterfaces, // Pass all known interfaces
knownClasses, // Pass all known classes
generateCompareTo,
),
);
}
// Generate changeTo methods for explicitSubTypes (downward conversion: parent to child)
if (interfacesX.any((element) => element.isExplicitSubType)) {
sb.writeln(
"extension ${classNameTrimmed}ChangeToE on ${classNameTrimmed} {",
);
interfacesX.where((element) => element.isExplicitSubType).forEach((x) {
sb.writeln(
MethodGenerator.generateChangeToMethods(
classFields: allFields,
interfaceFields: x.fields,
interfaceName: x.interfaceName,
className: className,
isClassAbstract: isAbstract,
interfaceGenerics: x.typeParams,
knownClasses: knownClasses,
isInterfaceSealed: x.isSealed,
classGenerics: classGenerics
.map((e) => NameType(e.name, e.type))
.toList(),
hidePublicConstructor: hidePublicConstructor,
interfaceHidePublicConstructor: x.hidePublicConstructor,
),
);
});
sb.writeln("}");
}
sb.writeln(getEnumPropertyList(allFields, className));
sb.writeln(
getPatchClass(
allFields,
className,
knownClasses,
classGenerics.map((e) => e.name).toList(),
),
);
// return commentEveryLine(sb.toString());
return sb.toString();
}