Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80,788 changes: 74,575 additions & 6,213 deletions codegen/input/meos-idl.json

Large diffs are not rendered by default.

76 changes: 76 additions & 0 deletions codegen/src/main/java/FunctionsGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,71 @@ public class FunctionsGenerator {
// so we force long when the interface type resolved to int for these names.
private static final Set<String> SIZE_PARAM_NAMES = Set.of("size", "wkb_size");

// -------------------------------------------------------------------------
// Optional MEOS type families, gated by build flags mirroring the
// MobilityDB/MEOS flag names and ON|OFF (also 1|0) values: -DCBUFFER=OFF,
// -DNPOINT=OFF, -DPOSE=OFF, -DRGEO=OFF, -DH3=OFF. Each family maps to the
// public headers that declare its functions; a function whose header belongs
// to an excluded family is omitted from the generated binding, so a subset
// jar ships without it. The shared binding jar includes every family by
// default; pass -D<FAMILY>=OFF|0 to drop one (RGEO needs POSE).
// -------------------------------------------------------------------------
// The canonical family of a function is the IDL ``family`` field, which
// MEOS-API derives from the declaring header's ``meos/include/<family>/``
// subdirectory — the single source of truth. This basename map is only the
// fallback for IDLs generated before that field existed, and covers the
// public headers of the families present at the time.
private static final Map<String, String> HEADER_FAMILY = Map.ofEntries(
Map.entry("meos_cbuffer.h", "CBUFFER"),
Map.entry("meos_npoint.h", "NPOINT"),
Map.entry("meos_pose.h", "POSE"),
Map.entry("meos_rgeo.h", "RGEO"),
Map.entry("meos_h3.h", "H3"),
Map.entry("th3index.h", "H3"),
Map.entry("th3index_internal.h", "H3"),
Map.entry("th3index_boxops.h", "H3"),
Map.entry("h3index.h", "H3"),
Map.entry("h3index_sets.h", "H3"),
Map.entry("h3_generated.h", "H3"));
private static final Set<String> OPTIONAL_FAMILIES =
Set.of("CBUFFER", "NPOINT", "POSE", "RGEO", "H3",
"QUADBIN", "POINTCLOUD", "JSON", "ARROW", "RASTER");

// Families enabled for this generation run; core headers are always emitted.
private Set<String> enabledFamilies;

private static Set<String> resolveEnabledFamilies() {
Set<String> enabled = new LinkedHashSet<>();
for (String family : OPTIONAL_FAMILIES) {
String v = System.getProperty(family);
boolean off = v != null
&& (v.equalsIgnoreCase("OFF") || v.equals("0") || v.equalsIgnoreCase("false"));
if (!off) {
enabled.add(family); // included by default, dropped only by -D<FAMILY>=OFF|0
}
}
return enabled;
}

// Resolve a function's family: the canonical IDL ``family`` field when
// present, else the header-basename fallback for pre-field IDLs.
private String familyOf(JsonNode fn) {
JsonNode familyNode = fn.get("family");
if (familyNode != null) {
return familyNode.asText();
}
JsonNode fileNode = fn.get("file");
return fileNode == null ? null : HEADER_FAMILY.get(fileNode.asText());
}

private boolean familyEnabled(String family) {
// CORE (and any family not in the optional set) is always emitted; an
// optional family is emitted only while it stays enabled.
return family == null
|| !OPTIONAL_FAMILIES.contains(family)
|| enabledFamilies.contains(family);
}

// Output-only size parameters that must not appear in the public
// static wrapper signature.
//
Expand Down Expand Up @@ -121,7 +186,14 @@ private void run(String inputPath, String outputPath) throws IOException {
List<FunctionDef> functions = new ArrayList<>();
Set<String> seen = new LinkedHashSet<>(); // deduplicate by name#arity

enabledFamilies = resolveEnabledFamilies();
System.out.println("Enabled optional families: " + enabledFamilies
+ " (core always included)");

for (JsonNode fn : functionsNode) {
if (!familyEnabled(familyOf(fn))) {
continue; // function belongs to a disabled type family
}
FunctionDef def = parseFunctionDef(fn);
String key = def.name + "#" + def.params.size();
if (seen.add(key)) {
Expand Down Expand Up @@ -266,6 +338,10 @@ private String mapCTypeToJava(String cType) {
// DateADT is int32 under the hood; Timestamp/TimestampTz are int64.
case "DateADT" -> "int";
case "Timestamp", "TimestampTz" -> "long";
// H3Index is a uint64 cell identifier (DGGRID/H3), not an opaque
// struct pointer; without this it hits the default branch and
// becomes Pointer, breaking every h3index/th3index binding.
case "H3Index" -> "long";

// Explicit enum names (in case not in JSON enums section)
case "interpType", "RTreeSearchOp",
Expand Down
65 changes: 65 additions & 0 deletions codegen/src/test/java/FunctionsGeneratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class MapCTypeToJavaTests {
@Test void DateADT() throws Exception { assertEquals("int", mapJava("DateADT")); }
@Test void Timestamp() throws Exception { assertEquals("long", mapJava("Timestamp")); }
@Test void TimestampTz() throws Exception { assertEquals("long", mapJava("TimestampTz")); }
@Test void H3Index() throws Exception { assertEquals("long", mapJava("H3Index")); }

@Test void charPointer() throws Exception { assertEquals("String", mapJava("char *")); }
@Test void structPointer() throws Exception { assertEquals("Pointer", mapJava("STBox *")); }
Expand Down Expand Up @@ -694,4 +695,68 @@ private String writeJson(String json) throws IOException {
return p.toString();
}
}

// =========================================================================
// Family gating: -D<FAMILY>=OFF drops an optional family
// =========================================================================

@Nested
@DisplayName("family gating")
class FamilyGatingTests {

// A core function and a pointcloud function, tagged by the IDL family field.
private static final String FIELD_JSON = """
{
"functions": [
{"name": "temporal_num_instants", "family": "CORE",
"returnType": {"c": "int"},
"params": [{"name": "temp", "cType": "Temporal *"}]},
{"name": "pcpatch_num_points", "family": "POINTCLOUD",
"returnType": {"c": "int"},
"params": [{"name": "patch", "cType": "Pointer"}]}
]
}
""";

@Test
@DisplayName("family field: all families emitted by default")
void fieldDefaultEmitsAll() throws Exception {
String out = generateFromJson(FIELD_JSON);
assertTrue(out.contains("temporal_num_instants"));
assertTrue(out.contains("pcpatch_num_points"));
}

@Test
@DisplayName("family field: -DPOINTCLOUD=OFF drops pointcloud, keeps core")
void fieldPointcloudOff() throws Exception {
System.setProperty("POINTCLOUD", "OFF");
try {
String out = generateFromJson(FIELD_JSON);
assertTrue(out.contains("temporal_num_instants"), "core stays");
assertFalse(out.contains("pcpatch_num_points"), "pointcloud dropped");
} finally {
System.clearProperty("POINTCLOUD");
}
}

@Test
@DisplayName("fallback: header basename gates when the family field is absent")
void headerFallbackGates() throws Exception {
String json = """
{
"functions": [
{"name": "cbuffer_out", "file": "meos_cbuffer.h",
"returnType": {"c": "char *"},
"params": [{"name": "cb", "cType": "Pointer"}]}
]
}
""";
System.setProperty("CBUFFER", "OFF");
try {
assertFalse(generateFromJson(json).contains("cbuffer_out"));
} finally {
System.clearProperty("CBUFFER");
}
}
}
}
51 changes: 51 additions & 0 deletions jmeos-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,57 @@
</plugin>


<!-- Generate functions.GeneratedFunctions from the MEOS IDL at build
time. The optional type families are selected with the same flag
names and ON|OFF (also 1|0) values as the MobilityDB/MEOS build:
every family is included by default; pass -DCBUFFER=OFF (or
-DNPOINT/-DPOSE/-DRGEO/-DH3=OFF|0) to drop one (RGEO needs POSE).
exec:java runs in-process so the -D flags are inherited. -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>generate-meos-facade</id>
<phase>generate-sources</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>FunctionsGenerator</mainClass>
<additionalClasspathElements>
<additionalClasspathElement>${project.basedir}/../codegen/target/classes</additionalClasspathElement>
</additionalClasspathElements>
<arguments>
<argument>${project.basedir}/../codegen/input/meos-idl.json</argument>
<argument>${project.build.directory}/generated-sources/jmeos/functions/GeneratedFunctions.java</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>add-generated-facade</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/jmeos</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
Expand Down
Loading
Loading