Discover Model paths and compatible binding targets in both hosts

Add non-constructing target metadata and a shared read-only browser for the Person fixtures. Negotiate binding discovery independently, preserve FXML, and explain direction/type mismatches. Record remaining manual navigation checks as explicitly skipped.
This commit is contained in:
2026-11-13 08:57:35 +01:00
parent 2156894fa8
commit 2fe408bb4d
38 changed files with 1087 additions and 37 deletions
@@ -1,6 +1,10 @@
package fixture.model;
public class Person {
static {
System.setProperty("fixture.person.initialized", "yes");
}
private java.util.List<Address> visits = new java.util.ArrayList<>();
public java.util.List<Address> getVisits() {
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import fixture.components.NoteForm?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml/1"><Label text="A long address explanation that should wrap across several lines when the allocated width is narrow" wrapText="true"/><NoteForm fx:id="nested"/></fx:root>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import javafx.scene.control.TextField?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml/1"><fx:define><BindingList><DirectBinding control="street" property="text" path="street" direction="both"/></BindingList></fx:define><Label fx:id="captionLabel"/><TextField fx:id="street"/></fx:root>
@@ -1,3 +1,4 @@
<?neitzel-model fixture.model.Person?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.TextField?><?import javafx.scene.control.Button?><?import fixture.components.AddressForm?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?><?import de.neitzel.fx.view.Repeat?><?import de.neitzel.fx.view.Table?><?import de.neitzel.fx.view.Column?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="fixture.components.PersonController" prefWidth="640" prefHeight="700">
<fx:define><BindingList><DirectBinding control="rankInput" property="text" path="rank" direction="both" converter="fixture.components.RankConverter"/><DirectBinding control="home" property="model" path="home" direction="both"/><Repeat pane="visits" component="fixture.components.AddressForm" collection="visits"/></BindingList></fx:define>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml/1"><fx:define><BindingList><DirectBinding control="note" property="text" path="street" direction="both"/></BindingList></fx:define><Label fx:id="note" wrapText="true" text="Note"/></fx:root>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Person?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import javafx.scene.control.TextField?><?import fixture.components.Card?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?><?import de.neitzel.fx.view.ComputedBinding?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="fixture.components.PersonController" prefWidth="420" prefHeight="240"><fx:define><BindingList><DirectBinding control="name" property="text" path="name" direction="both"/><DirectBinding control="card" property="model" path="home" direction="both"/><DirectBinding control="second" property="model" path="home" direction="both"/><ComputedBinding control="greeting" property="text" method="#greeting"/></BindingList></fx:define><TextField fx:id="name"/><Label fx:id="greeting"/><Card fx:id="card" caption="FXML caption" prefWidth="310" alignment="CENTER_LEFT"/><Card fx:id="second" caption="Second"/></VBox>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.HBox?><?import javafx.scene.control.TextField?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?>
<fx:root type="javafx.scene.layout.HBox" xmlns:fx="http://javafx.com/fxml/1"><fx:define><BindingList><DirectBinding control="street" property="text" path="street" direction="both"/></BindingList></fx:define><TextField fx:id="street"/></fx:root>
@@ -60,6 +60,39 @@ public final class PreviewChecks {
System.out.println("PASS discovery " + checks);
return;
}
if (mode.equals("bindings")) {
String xml;
try (var in = loader.getResourceAsStream("fixture/components/Person.fxml")) {
xml = new String(in.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8);
}
var metadata = BindingDiscovery.inspect(xml, "home", "both", loader);
check(
metadata.attributes().stream().anyMatch(a -> a.path().equals("home.street")),
"Nested metadata");
check(
metadata.attributes().stream()
.anyMatch(
a ->
a.path().equals("visits") && a.elementType().equals("fixture.model.Address")),
"Collection element metadata");
check(
metadata.targets().stream()
.anyMatch(
t ->
t.control().equals("card")
&& t.property().equals("model")
&& t.status().equals("COMPATIBLE")),
"Component Model target metadata");
check(
System.getProperty("fixture.person.initialized") == null, "Discovery initialized Person");
check(System.getProperty("fixture.models") == null, "Discovery constructed Model");
check(System.getProperty("fixture.controllers") == null, "Discovery constructed Controller");
check(
System.getProperty("fixture.provider.initialized") == null,
"Discovery initialized provider");
System.out.println("PASS bindings " + checks);
return;
}
var started = new CountDownLatch(1);
Platform.startup(
() -> {
@@ -1,6 +1,10 @@
package fixture.model;
public class Person {
static {
System.setProperty("fixture.person.initialized", "yes");
}
private java.util.List<Address> visits = new java.util.ArrayList<>();
public java.util.List<Address> getVisits() {
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import fixture.components.NoteForm?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml/1"><Label text="A long address explanation that should wrap across several lines when the allocated width is narrow" wrapText="true"/><NoteForm fx:id="nested"/></fx:root>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import javafx.scene.control.TextField?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml/1"><fx:define><BindingList><DirectBinding control="street" property="text" path="street" direction="both"/></BindingList></fx:define><Label fx:id="captionLabel"/><TextField fx:id="street"/></fx:root>
@@ -1,3 +1,4 @@
<?neitzel-model fixture.model.Person?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.TextField?><?import javafx.scene.control.Button?><?import fixture.components.AddressForm?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?><?import de.neitzel.fx.view.Repeat?><?import de.neitzel.fx.view.Table?><?import de.neitzel.fx.view.Column?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="fixture.components.PersonController" prefWidth="640" prefHeight="700">
<fx:define><BindingList><DirectBinding control="rankInput" property="text" path="rank" direction="both" converter="fixture.components.RankConverter"/><DirectBinding control="home" property="model" path="home" direction="both"/><Repeat pane="visits" component="fixture.components.AddressForm" collection="visits"/></BindingList></fx:define>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml/1"><fx:define><BindingList><DirectBinding control="note" property="text" path="street" direction="both"/></BindingList></fx:define><Label fx:id="note" wrapText="true" text="Note"/></fx:root>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Person?>
<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?><?import javafx.scene.control.TextField?><?import fixture.components.Card?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?><?import de.neitzel.fx.view.ComputedBinding?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="fixture.components.PersonController" prefWidth="420" prefHeight="240"><fx:define><BindingList><DirectBinding control="name" property="text" path="name" direction="both"/><DirectBinding control="card" property="model" path="home" direction="both"/><DirectBinding control="second" property="model" path="home" direction="both"/><ComputedBinding control="greeting" property="text" method="#greeting"/></BindingList></fx:define><TextField fx:id="name"/><Label fx:id="greeting"/><Card fx:id="card" caption="FXML caption" prefWidth="310" alignment="CENTER_LEFT"/><Card fx:id="second" caption="Second"/></VBox>
@@ -1,2 +1,3 @@
<?neitzel-model fixture.model.Address?>
<?import javafx.scene.layout.HBox?><?import javafx.scene.control.TextField?><?import de.neitzel.fx.view.BindingList?><?import de.neitzel.fx.view.DirectBinding?>
<fx:root type="javafx.scene.layout.HBox" xmlns:fx="http://javafx.com/fxml/1"><fx:define><BindingList><DirectBinding control="street" property="text" path="street" direction="both"/></BindingList></fx:define><TextField fx:id="street"/></fx:root>
@@ -60,6 +60,39 @@ public final class PreviewChecks {
System.out.println("PASS discovery " + checks);
return;
}
if (mode.equals("bindings")) {
String xml;
try (var in = loader.getResourceAsStream("fixture/components/Person.fxml")) {
xml = new String(in.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8);
}
var metadata = BindingDiscovery.inspect(xml, "home", "both", loader);
check(
metadata.attributes().stream().anyMatch(a -> a.path().equals("home.street")),
"Nested metadata");
check(
metadata.attributes().stream()
.anyMatch(
a ->
a.path().equals("visits") && a.elementType().equals("fixture.model.Address")),
"Collection element metadata");
check(
metadata.targets().stream()
.anyMatch(
t ->
t.control().equals("card")
&& t.property().equals("model")
&& t.status().equals("COMPATIBLE")),
"Component Model target metadata");
check(
System.getProperty("fixture.person.initialized") == null, "Discovery initialized Person");
check(System.getProperty("fixture.models") == null, "Discovery constructed Model");
check(System.getProperty("fixture.controllers") == null, "Discovery constructed Controller");
check(
System.getProperty("fixture.provider.initialized") == null,
"Discovery initialized provider");
System.out.println("PASS bindings " + checks);
return;
}
var started = new CountDownLatch(1);
Platform.startup(
() -> {