diff --git a/codegen/src/main/java/FunctionsGenerator.java b/codegen/src/main/java/FunctionsGenerator.java index f1f95aa9f..27b11bc9e 100644 --- a/codegen/src/main/java/FunctionsGenerator.java +++ b/codegen/src/main/java/FunctionsGenerator.java @@ -23,6 +23,8 @@ public class FunctionsGenerator { // Enum names extracted from the JSON "enums" section. // These are mapped to int in Java (JNR-FFI represents C enums as int). private final Set enumNames = new HashSet<>(); + /** The enums of the catalog, in its order, each written as a Java enum by {@link #writeEnumClasses}. */ + private final List enumDefs = new ArrayList<>(); /** Struct names the catalog defines, used to spot a by-value struct return. */ private final Set structNames = new HashSet<>(); /** Fields of each struct the catalog defines, keyed by struct name. */ @@ -241,6 +243,7 @@ private void run(String inputPath, String outputPath) throws IOException { Files.createDirectories(out.getParent()); Files.writeString(out, content); System.out.println("Written: " + out.toAbsolutePath()); + writeEnumClasses(out.getParent()); System.out.println("=== Done ==="); } @@ -481,11 +484,61 @@ private void collectEnumNames(JsonNode root) { JsonNode nameNode = e.get("name"); if (nameNode != null) { enumNames.add(nameNode.asText()); + enumDefs.add(e); } } } } + /** + * Writes one Java enum per enum of the catalog a binding reaches, under its C name and with + * the values MEOS gives its constants, beside {@code GeneratedFunctions}: the enums of the + * public headers of an enabled family, neither those of {@code meos_internal.h} nor those of + * a vendored library. + */ + private void writeEnumClasses(Path dir) throws IOException { + int written = 0; + for (JsonNode e : enumDefs) { + if (emitsEnum(e)) { + Files.writeString(dir.resolve(e.get("name").asText() + ".java"), enumSource(e)); + written++; + } + } + System.out.println("Enums written: " + written + " of " + enumDefs.size()); + } + + /** Whether the enum belongs to the public surface this run generates. */ + private boolean emitsEnum(JsonNode e) { + JsonNode file = e.get("file"); + JsonNode vendored = e.get("vendored"); + return (vendored == null || !vendored.asBoolean()) + && (file == null || !file.asText().startsWith("meos_internal")) + && familyEnabled(familyOf(e)); + } + + /** The Java source of an enum of the catalog. */ + static String enumSource(JsonNode e) { + String name = e.get("name").asText(); + JsonNode file = e.get("file"); + StringBuilder sb = new StringBuilder("package functions;\n\n"); + sb.append("/** {@code ").append(name).append("} as ") + .append(file == null ? "MEOS" : "{@code " + file.asText() + "}") + .append(" declares it. */\n"); + sb.append("public enum ").append(name).append(" {\n"); + JsonNode values = e.get("values"); + for (int i = 0; i < values.size(); i++) { + JsonNode v = values.get(i); + sb.append("\t").append(v.get("name").asText()).append("(").append(v.get("value").asLong()) + .append(i + 1 < values.size() ? "),\n" : ");\n"); + } + sb.append("\n\tprivate final int value;\n\n"); + sb.append("\t").append(name).append("(int value) {\n\t\tthis.value = value;\n\t}\n\n"); + sb.append("\t/** The value MEOS gives this constant. */\n"); + sb.append("\tpublic int getValue() {\n\t\treturn value;\n\t}\n"); + sb.append("}\n"); + return sb.toString(); + } + private FunctionDef parseFunctionDef(JsonNode fn) { String name = fn.get("name").asText(); @@ -609,10 +662,6 @@ private String mapCTypeToJava(String cType) { // becomes Pointer, breaking every h3index/th3index binding. case "H3Index" -> "long"; - // Explicit enum names (in case not in JSON enums section) - case "interpType", "RTreeSearchOp", - "tempSubtype", "spatialRel", - "errorCode" -> "int"; default -> "Pointer"; }; } @@ -781,8 +830,6 @@ private String generateFile(List functions) { import jnr.ffi.byref.PointerByReference; import jnr.ffi.Struct; import utils.JarLibraryLoader; - import utils.meosCatalog.MeosEnums.meosType; - import utils.meosCatalog.MeosEnums.meosOper; import functions.MeosErrorHandler; import java.time.*; diff --git a/codegen/src/test/java/FunctionsGeneratorTest.java b/codegen/src/test/java/FunctionsGeneratorTest.java index e83782009..192fd2240 100644 --- a/codegen/src/test/java/FunctionsGeneratorTest.java +++ b/codegen/src/test/java/FunctionsGeneratorTest.java @@ -163,10 +163,10 @@ void unknownType() throws Exception { } @ParameterizedTest - @ValueSource(strings = {"interpType", "RTreeSearchOp", "tempSubtype", "spatialRel", "errorCode"}) - @DisplayName("explicit enum names → int") - void explicitEnums(String cType) throws Exception { - assertEquals("int", mapJava(cType)); + @ValueSource(strings = {"interpType", "IndexSearchOp", "tempSubtype", "spatialRel", "errorCode"}) + @DisplayName("a name the catalog states no enum of is not an enum") + void enumOnlyFromCatalog(String cType) throws Exception { + assertEquals("Pointer", mapJava(cType)); } @Test @@ -376,7 +376,7 @@ void enumsPresent() throws Exception { String json = """ { "enums": [ - {"name": "RTreeSearchOp"}, + {"name": "IndexSearchOp"}, {"name": "interpType"} ], "functions": [] @@ -396,7 +396,7 @@ void enumsPresent() throws Exception { @SuppressWarnings("unchecked") Set names = (Set) enumNamesField.get(generator); - assertTrue(names.contains("RTreeSearchOp")); + assertTrue(names.contains("IndexSearchOp")); assertTrue(names.contains("interpType")); assertEquals(2, names.size()); } @@ -418,6 +418,76 @@ void noEnumsSection() throws Exception { } } + // ========================================================================= + // The Java enums of the catalog + // ========================================================================= + + @Nested + @DisplayName("enum classes") + class EnumClassTests { + + private JsonNode enumNode(String json) throws Exception { + return new ObjectMapper().readTree(json); + } + + private boolean emits(JsonNode e) throws Exception { + Method m = FunctionsGenerator.class.getDeclaredMethod("emitsEnum", JsonNode.class); + m.setAccessible(true); + return (boolean) m.invoke(generator, e); + } + + @Test + @DisplayName("an enum keeps its C name and the value MEOS gives each constant") + void enumSourceKeepsNamesAndValues() throws Exception { + String src = FunctionsGenerator.enumSource(enumNode(""" + {"name": "MeosType", "file": "meos_catalog.h", "family": "CORE", + "values": [{"name": "T_UNKNOWN", "value": 0}, {"name": "T_DOUBLE2", "value": 8}, + {"name": "T_FLOAT8", "value": 11}]} + """)); + assertTrue(src.startsWith("package functions;")); + assertTrue(src.contains("public enum MeosType {")); + assertTrue(src.contains("\tT_UNKNOWN(0),\n\tT_DOUBLE2(8),\n\tT_FLOAT8(11);\n")); + assertTrue(src.contains("public int getValue()")); + } + + @Test + @DisplayName("the enums of a public header are written, internal and vendored ones are not") + void emitsPublicEnumsOnly() throws Exception { + assertTrue(emits(enumNode(""" + {"name": "IndexSearchOp", "file": "meos.h", "family": "CORE", "vendored": false, + "values": [{"name": "INDEX_OVERLAPS", "value": 0}]} + """))); + assertFalse(emits(enumNode(""" + {"name": "SkipListType", "file": "meos_internal.h", "family": "CORE", "vendored": false, + "values": [{"name": "TIMESTAMPTZ", "value": 0}]} + """))); + assertFalse(emits(enumNode(""" + {"name": "ENDIANS", "file": "pc_api.h", "family": "POINTCLOUD", "vendored": true, + "values": [{"name": "PC_XDR", "value": 0}]} + """))); + } + + @Test + @DisplayName("run writes each enum beside the generated functions") + void runWritesEnumFiles() throws Exception { + Path input = tempDir.resolve("idl.json"); + Files.writeString(input, """ + {"enums": [{"name": "IndexSearchOp", "file": "meos.h", "family": "CORE", + "values": [{"name": "INDEX_OVERLAPS", "value": 0}, + {"name": "INDEX_ADJACENT", "value": 20}]}, + {"name": "SkipListType", "file": "meos_internal.h", "family": "CORE", + "values": [{"name": "TIMESTAMPTZ", "value": 0}]}], + "functions": []} + """); + Path out = tempDir.resolve("functions/GeneratedFunctions.java"); + run.invoke(generator, input.toString(), out.toString()); + Path written = tempDir.resolve("functions/IndexSearchOp.java"); + assertTrue(Files.exists(written)); + assertTrue(Files.readString(written).contains("INDEX_ADJACENT(20);")); + assertFalse(Files.exists(tempDir.resolve("functions/SkipListType.java"))); + } + } + // ========================================================================= // run(): end-to-end / integration // ========================================================================= @@ -940,7 +1010,7 @@ void fileHeaderCorrect() throws Exception { void enumFromJsonSection() throws Exception { String json = """ { - "enums": [{"name": "MyOp"}], + "enums": [{"name": "MyOp", "file": "meos.h", "family": "CORE", "values": [{"name": "MY_OP", "value": 0}]}], "functions": [{ "name": "do_op", "returnType": {"c": "void"}, diff --git a/jmeos-core/src/main/java/functions/RTreeSearchOp.java b/jmeos-core/src/main/java/functions/RTreeSearchOp.java deleted file mode 100644 index cd00d8b14..000000000 --- a/jmeos-core/src/main/java/functions/RTreeSearchOp.java +++ /dev/null @@ -1,20 +0,0 @@ -package functions; - -public enum RTreeSearchOp { - /** Find stored boxes that overlap the query */ - RTREE_OVERLAPS(0), - /** Find stored boxes that contain the query */ - RTREE_CONTAINS(1), - /** Find stored boxes contained by the query */ - RTREE_CONTAINED_BY(2); - - private final int value; - - RTreeSearchOp(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/jmeos-core/src/main/java/utils/meosCatalog/MeosEnums.java b/jmeos-core/src/main/java/utils/meosCatalog/MeosEnums.java deleted file mode 100644 index d088e2b46..000000000 --- a/jmeos-core/src/main/java/utils/meosCatalog/MeosEnums.java +++ /dev/null @@ -1,130 +0,0 @@ -package utils.meosCatalog; - - -/** - * Meos enumeration storing the type and operators through MEOS integer values. - * - * @author Nidhal Mareghni - * @since 18/10/2023 - */ -public class MeosEnums { - public enum meosType { - T_UNKNOWN(0), - T_BOOL(1), - T_DOUBLE2(2), - T_DOUBLE3(3), - T_DOUBLE4(4), - T_FLOAT8(5), - T_FLOATSET(6), - T_FLOATSPAN(7), - T_FLOATSPANSET(8), - T_INT4(9), - T_INT4RANGE(10), - T_INT4MULTIRANGE(11), - T_INTSET(12), - T_INTSPAN(13), - T_INTSPANSET(14), - T_INT8(15), - T_BIGINTSET(16), - T_BIGINTSPAN(17), - T_BIGINTSPANSET(18), - T_STBOX(19), - T_TBOOL(20), - T_TBOX(21), - T_TDOUBLE2(22), - T_TDOUBLE3(23), - T_TDOUBLE4(24), - T_TEXT(25), - T_TEXTSET(26), - T_TFLOAT(27), - T_TIMESTAMPTZ(28), - T_TINT(29), - T_TSTZMULTIRANGE(30), - T_TSTZRANGE(31), - T_TSTZSET(32), - T_TSTZSPAN(33), - T_TSTZSPANSET(34), - T_TTEXT(35), - T_GEOMETRY(36), - T_GEOMSET(37), - T_GEOGRAPHY(38), - T_GEOGSET(39), - T_TGEOMPOINT(40), - T_TGEOGPOINT(41), - T_NPOINT(42), - T_NPOINTSET(43), - T_NSEGMENT(44), - T_TNPOINT(45); - - private final int value; - - meosType(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - - - - public enum meosOper { - UNKNOWN_OP(0), - EQ_OP(1), - NE_OP(2), - LT_OP(3), - LE_OP(4), - GT_OP(5), - GE_OP(6), - ADJACENT_OP(7), - UNION_OP(8), - MINUS_OP(9), - INTERSECT_OP(10), - OVERLAPS_OP(11), - CONTAINS_OP(12), - CONTAINED_OP(13), - SAME_OP(14), - LEFT_OP(15), - OVERLEFT_OP(16), - RIGHT_OP(17), - OVERRIGHT_OP(18), - BELOW_OP(19), - OVERBELOW_OP(20), - ABOVE_OP(21), - OVERABOVE_OP(22), - FRONT_OP(23), - OVERFRONT_OP(24), - BACK_OP(25), - OVERBACK_OP(26), - BEFORE_OP(27), - OVERBEFORE_OP(28), - AFTER_OP(29), - OVERAFTER_OP(30), - EVEREQ_OP(31), - EVERNE_OP(32), - EVERLT_OP(33), - EVERLE_OP(34), - EVERGT_OP(35), - EVERGE_OP(36), - ALWAYSEQ_OP(37), - ALWAYSNE_OP(38), - ALWAYSLT_OP(39), - ALWAYSLE_OP(40), - ALWAYSGT_OP(41), - ALWAYSGE_OP(42); - - private final int value; - - meosOper(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - -} diff --git a/jmeos-core/src/main/java/utils/meosCatalog/SettypeCatalogStruct.java b/jmeos-core/src/main/java/utils/meosCatalog/SettypeCatalogStruct.java index 7bf31af83..300a2d37b 100644 --- a/jmeos-core/src/main/java/utils/meosCatalog/SettypeCatalogStruct.java +++ b/jmeos-core/src/main/java/utils/meosCatalog/SettypeCatalogStruct.java @@ -1,27 +1,27 @@ package utils.meosCatalog; -import utils.meosCatalog.MeosEnums.meosType; +import functions.MeosType; /** - * Class for setting set meosType in an object. + * Class for setting set MeosType in an object. * * @author Nidhal Mareghni * @since 18/10/2023 */ public class SettypeCatalogStruct { - private final meosType settype; - private final meosType basetype; + private final MeosType settype; + private final MeosType basetype; - public SettypeCatalogStruct(meosType settype, meosType basetype) { + public SettypeCatalogStruct(MeosType settype, MeosType basetype) { this.settype = settype; this.basetype = basetype; } - public meosType getSettype() { + public MeosType getSettype() { return settype; } - public meosType getBasetype() { + public MeosType getBasetype() { return basetype; } } diff --git a/jmeos-core/src/main/java/utils/meosCatalog/SpansettypeCatalogStruct.java b/jmeos-core/src/main/java/utils/meosCatalog/SpansettypeCatalogStruct.java index a7ac2df9f..633d558c9 100644 --- a/jmeos-core/src/main/java/utils/meosCatalog/SpansettypeCatalogStruct.java +++ b/jmeos-core/src/main/java/utils/meosCatalog/SpansettypeCatalogStruct.java @@ -1,27 +1,27 @@ package utils.meosCatalog; -import utils.meosCatalog.MeosEnums.meosType; +import functions.MeosType; /** - * Class for setting spanSet meosType in an object. + * Class for setting spanSet MeosType in an object. * * @author Nidhal Mareghni * @since 18/10/2023 */ public class SpansettypeCatalogStruct { - private final meosType spansettype; - private final meosType spantype; + private final MeosType spansettype; + private final MeosType spantype; - public SpansettypeCatalogStruct(meosType spansettype, meosType spantype) { + public SpansettypeCatalogStruct(MeosType spansettype, MeosType spantype) { this.spansettype = spansettype; this.spantype = spantype; } - public meosType getSpansettype() { + public MeosType getSpansettype() { return spansettype; } - public meosType getSpantype() { + public MeosType getSpantype() { return spantype; } } diff --git a/jmeos-core/src/main/java/utils/meosCatalog/SpantypeCatalogStruct.java b/jmeos-core/src/main/java/utils/meosCatalog/SpantypeCatalogStruct.java index 792c4bc71..75e30cc5c 100644 --- a/jmeos-core/src/main/java/utils/meosCatalog/SpantypeCatalogStruct.java +++ b/jmeos-core/src/main/java/utils/meosCatalog/SpantypeCatalogStruct.java @@ -1,27 +1,27 @@ package utils.meosCatalog; -import utils.meosCatalog.MeosEnums.meosType; +import functions.MeosType; /** - * Class for setting span meosType in an object. + * Class for setting span MeosType in an object. * * @author Nidhal Mareghni * @since 18/10/2023 */ public class SpantypeCatalogStruct { - private final meosType spantype; - private final meosType basetype; + private final MeosType spantype; + private final MeosType basetype; - public SpantypeCatalogStruct(meosType spantype, meosType basetype) { + public SpantypeCatalogStruct(MeosType spantype, MeosType basetype) { this.spantype = spantype; this.basetype = basetype; } - public meosType getSpantype() { + public MeosType getSpantype() { return spantype; } - public meosType getBasetype() { + public MeosType getBasetype() { return basetype; } } diff --git a/jmeos-core/src/main/java/utils/meosCatalog/TemptypeCatalogStruct.java b/jmeos-core/src/main/java/utils/meosCatalog/TemptypeCatalogStruct.java index 22e794e59..00f6bff38 100644 --- a/jmeos-core/src/main/java/utils/meosCatalog/TemptypeCatalogStruct.java +++ b/jmeos-core/src/main/java/utils/meosCatalog/TemptypeCatalogStruct.java @@ -1,27 +1,27 @@ package utils.meosCatalog; -import utils.meosCatalog.MeosEnums.meosType; +import functions.MeosType; /** - * Class for setting temporal meosType in an object. + * Class for setting temporal MeosType in an object. * * @author Nidhal Mareghni * @since 18/10/2023 */ public class TemptypeCatalogStruct { - private final meosType temptype; - private final meosType basetype; + private final MeosType temptype; + private final MeosType basetype; - public TemptypeCatalogStruct(meosType temptype, meosType basetype) { + public TemptypeCatalogStruct(MeosType temptype, MeosType basetype) { this.temptype = temptype; this.basetype = basetype; } - public meosType getTemptype() { + public MeosType getTemptype() { return temptype; } - public meosType getBasetype() { + public MeosType getBasetype() { return basetype; } }