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));
     }

Reply via email to