
tech.deplant.java4ever.binding.generator.ParserEngine Maven / Gradle / Ivy
package tech.deplant.java4ever.binding.generator;
import com.fasterxml.jackson.core.JsonProcessingException;
import tech.deplant.commons.Objs;
import tech.deplant.commons.Strings;
import tech.deplant.java4ever.binding.JsonContext;
import tech.deplant.java4ever.binding.generator.jtype.*;
import tech.deplant.java4ever.binding.generator.reference.*;
import tech.deplant.java4ever.binding.io.JsonResource;
import tech.deplant.javapoet.CodeBlock;
import tech.deplant.javapoet.JavaFile;
import tech.deplant.javapoet.MethodSpec;
import tech.deplant.javapoet.TypeSpec;
import javax.lang.model.element.Modifier;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Supplier;
import static java.util.Objects.requireNonNullElse;
public class ParserEngine {
private final static System.Logger logger = System.getLogger(ParserEngine.class.getName());
public static ApiReference ofJsonString(String resourceName) throws JsonProcessingException {
return JsonContext.SDK_JSON_MAPPER().readValue(resourceName,
ApiReference.class);
}
public static void parse(ApiReference parsedApiReference) throws IOException {
final String apiVersion = parsedApiReference.version();
// library to store all parsed types
// they'll be used later to construct correct method params and bodies
final Map typeLibrary = new HashMap<>();
// library to store references to child classes from EnumOfTypes
final Map eotReferences = new HashMap<>();
// type parsing loop
// we parse all modules to get info about all possible types
// that's because function of one module can depend on type from other module
// so we need to know about other modules types
for (var module : parsedApiReference.modules()) {
String moduleCapitalName = ParserUtils.capitalize(module.name());
final Map appObjects = new HashMap<>();
// type parsing loop
for (ApiType type : module.types()) {
switch (type) {
case EnumOfConsts en -> {
var jEnum = new SdkEnum(en.name(),
en.enum_consts(),
new SdkDocs(en.summary(), en.description()));
typeLibrary.put(new SdkType(moduleCapitalName, en.name()), jEnum);
}
case StructType struct -> {
boolean isParams = false;
boolean isResult = false;
if (struct.name().length() >= 8) {
isParams = "ParamsOf".equals(struct.name().substring(0, 8));
isResult = "ResultOf".equals(struct.name().substring(0, 8));
}
boolean isSimpleWrapper = struct.struct_fields().length == 1;
typeLibrary.put(new SdkType(moduleCapitalName, struct.name()), new SdkRecord(struct,
struct.name(),
null,
isSimpleWrapper,
isParams,
isResult,
typeLibrary));
}
case EnumOfTypes eot -> {
List records = new ArrayList<>();
// patch for 'Abi' interface that messes with module with the same name
String interfaceName = "Abi".equals(eot.name()) ? "ABI" : eot.name();
boolean isAppObject = Strings.safeSubstrEquals(interfaceName, 0, 11, false, "ParamsOfApp") ||
Strings.safeSubstrEquals(interfaceName, 0, 11, false, "ResultOfApp");
String appObjectName = "";
if (isAppObject) {
appObjectName = Strings.substr(interfaceName,8);
if (!appObjects.containsKey(appObjectName)) {
logger.log(System.Logger.Level.WARNING,appObjectName);
appObjects.put(appObjectName,TypeSpec.interfaceBuilder(appObjectName));
} else {
logger.log(System.Logger.Level.WARNING,"Has appObject: " + appObjectName);
}
}
for (ApiType eotChildType : eot.enum_types()) {
switch (eotChildType) {
// case StructType str when isAppObject -> {
// var appObjectBuilder = appObjects.get(appObjectName);
// //if (interfaceName.equals("ParamsOf" + appObjectName)) {
// //var aoMethodBuilder = MethodSpec.methodBuilder(eotChildType.name());
// //aoMethodBuilder.
// //appObjectBuilder.addMethod(aoMethodBuilder.build());
// //}
//// records.add(SdkRecord.ofApiType(str,
//// typeLibrary,
//// new SdkInterfaceParent(
//// moduleCapitalName,
//// interfaceName,
//// str.name())));
// }
case StructType str -> records.add(SdkRecord.ofApiType(str,
typeLibrary,
new SdkInterfaceParent(
moduleCapitalName,
interfaceName,
str.name())));
case RefType ref -> eotReferences.put(TypeReference.fromApiType(ref).toSdkType(),
new SdkInterfaceParent(moduleCapitalName,
interfaceName,
ref.name()));
default -> throw new IllegalStateException(
"Unexpected value: " + eotChildType);
}
}
//if (!isAppObject) {
var javaInterface = new SdkInterface(eot,
interfaceName,
new SdkDocs(eot.summary(), eot.description()),
records);
typeLibrary.put(new SdkType(moduleCapitalName, interfaceName), javaInterface);
//}
}
default -> typeLibrary.put(new SdkType(moduleCapitalName, type.name()), new SdkDummy(type));
}
}
}
// add references from EOT interfaces
eotReferences.forEach((typ, sup) ->
Objs.notNullDo(
typeLibrary.get(typ),
jtype -> typeLibrary.put(typ,
((SdkRecord) jtype).withSuperInterface(sup))
));
// main file building loop
// loops modules again, now to write them
for (var module : parsedApiReference.modules()) {
if ("debot".equals(module.name())) {
continue;
}
final TypeSpec.Builder moduleBuilder = moduleToBuilder(module,
ParserUtils.capitalize(module.name()),
apiVersion);
String moduleCapitalName = ParserUtils.capitalize(module.name());
// let's find types that correspond to module in library
var types = typeLibrary.entrySet().stream().filter(
entry -> moduleCapitalName.equals(entry.getKey().module())
).map(Map.Entry::getValue).toList();
eotReferences.forEach((typ, sup) -> {
});
// let's write these types
for (SdkObject typeRef : types) {
var builder = typeRef.poeticize();
if (!Objects.isNull(builder)) {
moduleBuilder.addType(builder.build());
}
}
// function writing loop
// functions receive full lib info
for (ApiFunction function : module.functions()) {
moduleBuilder.addMethod(new SdkFunction(module.name().toLowerCase(),
function,
typeLibrary).poeticize()
.build());
}
// file writing loop
JavaFile javaFile = JavaFile
.builder("tech.deplant.java4ever.binding", moduleBuilder.build())
.build();
javaFile.writeTo(Paths.get("src/gen/java"));
}
}
public static TypeSpec.Builder moduleToBuilder(ApiModule module, String moduleNameCapitalized, String version) {
return TypeSpec
.classBuilder(moduleNameCapitalized)
.addJavadoc(moduleDocs(module, moduleNameCapitalized, version).build())
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
}
public static CodeBlock.Builder moduleDocs(ApiModule module, String moduleNameCapitalized, String version) {
return CodeBlock
.builder()
.add(String.format("""
%s
Contains methods of "%s" module of EVER-SDK API
%s %s
@version %s
""",
moduleNameCapitalized,
module.name(),
requireNonNullElse(module.summary(), ""),
requireNonNullElse(module.description(), ""),
version));
}
public record SdkType(String module, String name) {
}
public record SdkInterfaceParent(String module, String name, String variantName) {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy