This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/main by this push:
new 1ebc91bc99a CAUSEWAY-3964: Metamodel Inspector Support for Type
Hierarchy
1ebc91bc99a is described below
commit 1ebc91bc99a38a9c2ead7c3727d7aba96ebc2bcb
Author: andi-huber <[email protected]>
AuthorDate: Sun Feb 8 16:45:33 2026 +0100
CAUSEWAY-3964: Metamodel Inspector Support for Type Hierarchy
---
.../inspect/model/InterfaceGroupNode.java | 62 +++++++++++++++++
.../core/metamodel/inspect/model/MMNode.java | 1 +
.../metamodel/inspect/model/MMNodeFactory.java | 19 ++++-
.../MetamodelInspectView-InterfaceGroupNode.svg | 80 ++++++++++++++++++++++
.../inspect/model/MetamodelInspectView.java | 10 ++-
.../core/metamodel/inspect/model/TypeNode.java | 26 ++++++-
6 files changed, 194 insertions(+), 4 deletions(-)
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/InterfaceGroupNode.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/InterfaceGroupNode.java
new file mode 100644
index 00000000000..ef87c9e6e1d
--- /dev/null
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/InterfaceGroupNode.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.metamodel.inspect.model;
+import java.util.stream.Stream;
+
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+
+@RequiredArgsConstructor
+final class InterfaceGroupNode implements MMNode {
+
+ private final Can<ObjectSpecification> interfaces;
+
+ @Override
+ public String title() {
+ return "Interfaces";
+ }
+
+ @Override
+ public String iconName() {
+ return "";
+ }
+
+ @Override
+ public void putDetails(Details details) {
+ interfaces.stream()
+ .forEach(interfc->details.put(
+ interfc.getCorrespondingClass().getSimpleName(),
+ interfc.getCorrespondingClass().getName()));
+ }
+
+ // -- TREE NODE STUFF
+
+ @Getter @Setter
+ private MMNode parentNode;
+
+ @Override
+ public Stream<MMNode> streamChildNodes() {
+ return
interfaces.stream().map(interfc->MMNodeFactory.superType(interfc, this));
+ }
+
+}
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNode.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNode.java
index e6e71efc697..15e897f1578 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNode.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNode.java
@@ -27,6 +27,7 @@
sealed interface MMNode
permits
+ InterfaceGroupNode,
MemberNode,
FacetGroupNode,
FacetNode,
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNodeFactory.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNodeFactory.java
index 3b972edb37c..f0f7481a58d 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNodeFactory.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MMNodeFactory.java
@@ -20,6 +20,8 @@
import java.util.stream.Stream;
+import org.jspecify.annotations.Nullable;
+
import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.commons.internal.base._Strings;
import org.apache.causeway.core.metamodel.facetapi.Facet;
@@ -35,9 +37,24 @@
class MMNodeFactory {
TypeNode type(final ObjectSpecification objSpec) {
- return new TypeNode(objSpec.logicalTypeName());
+ return new TypeNode(objSpec);
}
+ TypeNode superType(@Nullable ObjectSpecification superSpec, MMNode
parentNodeNotUsed) {
+ if(superSpec==null
+ ||
superSpec.getCorrespondingClass().equals(Object.class))
+ return null;
+ return new TypeNode(superSpec);
+ }
+
+ MMNode interfaceGroup(Can<ObjectSpecification> interfaces, TypeNode
parentNode) {
+ if(interfaces.isEmpty())
+ return null;
+ var node = new InterfaceGroupNode(interfaces);
+ node.setParentNode(parentNode);
+ return node;
+ }
+
MMNode facet(final Facet facet, final FacetGroupNode parentNode) {
var node = new FacetNode(facet);
node.setParentNode(parentNode);
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView-InterfaceGroupNode.svg
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView-InterfaceGroupNode.svg
new file mode 100644
index 00000000000..e9032220645
--- /dev/null
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView-InterfaceGroupNode.svg
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<svg
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="FacetGroupNode.svg"
+ id="svg4"
+ version="1.1"
+ viewBox="0 0 512 512"
+ role="img"
+ class="svg-inline--fa fa-folder fa-w-16"
+ data-icon="folder"
+ data-prefix="far"
+ focusable="false"
+ aria-hidden="true"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ inkscape:current-layer="svg4"
+ inkscape:window-maximized="0"
+ inkscape:window-y="76"
+ inkscape:window-x="748"
+ inkscape:cy="170.60354"
+ inkscape:cx="249.97908"
+ inkscape:zoom="1.3921165"
+ showgrid="false"
+ id="namedview6"
+ inkscape:window-height="1133"
+ inkscape:window-width="1557"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0"
+ guidetolerance="10"
+ gridtolerance="10"
+ objecttolerance="10"
+ borderopacity="1"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ inkscape:pagecheckerboard="0" />
+ <path
+ style="fill:#777777;fill-opacity:1"
+ id="path2"
+ d="M464 128H272l-54.63-54.63c-6-6-14.14-9.37-22.63-9.37H48C21.49 64 0
85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49
48-48V176c0-26.51-21.49-48-48-48zm0 272H48V112h140.12l54.63 54.63c6 6 14.14
9.37 22.63 9.37H464v224z"
+ fill="currentColor" />
+ <path
+
style="opacity:0.981;fill:#eeeeee;fill-opacity:1;stroke:none;stroke-width:10.1587;stroke-linejoin:round"
+ d="M 48.761905,256 V 112.7619 h 69.614525 69.61452 l 28.41722,28.58395 c
15.62948,15.72117 31.16008,30.12117 34.51246,32 5.44567,3.052 17.08388,3.47948
109.20635,4.01123 l 103.11111,0.59518 V 288.59518 399.23809 H 256 48.761905 Z"
+ id="path6245" />
+ <text
+ xml:space="preserve"
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;line-height:0.75;font-family:Bahnschrift;-inkscape-font-specification:Bahnschrift;letter-spacing:0px;word-spacing:0px;fill:#777777;fill-opacity:1;stroke:none;stroke-width:4.13695"
+ x="85.655441"
+ y="366.07877"
+ id="text857-3-6"><tspan
+ sodipodi:role="line"
+ x="85.655441"
+ y="366.07877"
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:Bahnschrift;-inkscape-font-specification:Bahnschrift;fill:#777777;fill-opacity:1;stroke-width:4.13695"
+ id="tspan11999-8">ifc..</tspan></text>
+</svg>
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
index 2121d9e4bdf..f18acdf4131 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/MetamodelInspectView.java
@@ -21,6 +21,7 @@
import jakarta.inject.Named;
import org.apache.causeway.applib.CausewayModuleApplib;
+import org.apache.causeway.applib.annotation.Action;
import org.apache.causeway.applib.annotation.DomainObject;
import org.apache.causeway.applib.annotation.Editing;
import org.apache.causeway.applib.annotation.Introspection;
@@ -46,7 +47,7 @@ public class MetamodelInspectView extends
MasterDetailTreeView<MMNode, Metamodel
// -- FACTORY
public static MetamodelInspectView root(final ObjectSpecification spec) {
- return new MetamodelInspectView(new TypeNode(spec.logicalTypeName()),
TreePath.root());
+ return new MetamodelInspectView(new TypeNode(spec), TreePath.root());
}
// -- CONSTRUCTION
@@ -89,6 +90,13 @@ public Markup getDetails() {
return activeNode().details();
}
+ @Action
+ public MetamodelInspectView superType() {
+ return activeNode() instanceof TypeNode typeNode
+ ? typeNode.superType()
+ : null;
+ }
+
// -- IMPLEMENTATION DETAILS
@Override
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/TypeNode.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/TypeNode.java
index b818bdb6f5f..18f81a60983 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/TypeNode.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/inspect/model/TypeNode.java
@@ -19,6 +19,7 @@
package org.apache.causeway.core.metamodel.inspect.model;
import java.io.Serializable;
+import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
@@ -32,6 +33,14 @@ record TypeNode(
String logicalName)
implements MMNode, Serializable {
+ TypeNode(ObjectSpecification objSpec) {
+ // for security mapping, abstract spec's may share their
logical type name with sub-types
+ // however, for proper mementos, we should used fully qualified
class names instead (when abstract)
+ this(objSpec.isAbstract()
+ ? objSpec.getCorrespondingClass().getName()
+ : objSpec.logicalTypeName());
+ }
+
@Override
public String title() {
var spec = spec().orElse(null);
@@ -53,6 +62,7 @@ public void putDetails(Details details) {
details.put("Simple Name", spec.logicalType().logicalSimpleName());
details.put("Namespace", spec.logicalType().namespace());
details.put("Corresponding Class",
spec.getCorrespondingClass().getName());
+ details.put("Super Type",
spec.superclass().getCorrespondingClass().toString());
}
@Override
@@ -62,7 +72,10 @@ public Stream<MMNode> streamChildNodes() {
return _Streams.<MMNode>concat(
Stream.of(
- MMNodeFactory.facetGroup(spec.streamFacets(), this)),
+ MMNodeFactory.superType(spec.superclass(), this),
+ MMNodeFactory.interfaceGroup(spec.interfaces(), this),
+ MMNodeFactory.facetGroup(spec.streamFacets(), this))
+ .filter(Objects::nonNull),
spec.streamActions(ActionScope.PRODUCTION_ONLY, MixedIn.INCLUDED)
.map(action->MMNodeFactory.action(action, this)),
spec.streamProperties(MixedIn.INCLUDED)
@@ -71,10 +84,19 @@ public Stream<MMNode> streamChildNodes() {
.map(coll->MMNodeFactory.collection(coll, this)));
}
+ // -- UTIL
+
+ MetamodelInspectView superType() {
+ return spec()
+ .map(ObjectSpecification::superclass)
+ .map(MetamodelInspectView::root)
+ .orElse(null);
+ }
+
// -- HELPER
private Optional<ObjectSpecification> spec() {
- return MetaModelContext.instance()
+ return MetaModelContext.instance()
.map(MetaModelContext::getSpecificationLoader)
.flatMap(specLoader->specLoader.specForLogicalTypeName(logicalName));
}