Files
NeitzelLib/fx/src/main/java/de/neitzel/fx/view/FxmlControllers.java
T
konradandCursor d26f0c3b21 Let a Controller name the ViewModel class and receive it before bindings.
A second View reuses that one instance, and fails the load when it names a different class.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-30 18:25:38 +01:00

70 lines
2.4 KiB
Java

package de.neitzel.fx.view;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* Reads the Controller class named by an FXML document.
*/
final class FxmlControllers {
private static final String FXML_NAMESPACE = "http://javafx.com/fxml";
private FxmlControllers() {
}
/**
* Returns the Controller class named on the document root, or {@code null} when the document
* names none.
*
* @param fxml the FXML document
* @return the Controller class, or {@code null}
*/
static Class<?> controllerClass(URL fxml) {
String name = controllerName(fxml);
if (name == null || name.isEmpty()) {
return null;
}
try {
return Class.forName(name);
} catch (ClassNotFoundException failure) {
throw new IllegalArgumentException("No controller " + name, failure);
}
}
private static String controllerName(URL fxml) {
try (InputStream input = fxml.openStream()) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Element root = factory.newDocumentBuilder().parse(input).getDocumentElement();
String named = root.getAttributeNS(FXML_NAMESPACE, "controller");
if (named != null && !named.isEmpty()) {
return named;
}
NamedNodeMap attributes = root.getAttributes();
for (int index = 0; index < attributes.getLength(); index++) {
Node attribute = attributes.item(index);
if ("controller".equals(attribute.getLocalName())
|| "fx:controller".equals(attribute.getNodeName())) {
return attribute.getNodeValue();
}
}
return null;
} catch (IOException | ParserConfigurationException | SAXException failure) {
throw new IllegalStateException("Could not read controller from " + fxml, failure);
}
}
}