You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/12/10 12:01:44 UTC

[isis] branch master updated: ISIS-2473: adds support for j-doc processing on constructors

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 8e1f174  ISIS-2473: adds support for j-doc processing on constructors
8e1f174 is described below

commit 8e1f174073cbe4e36e1b14be2477599d45e64134
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Dec 10 13:01:33 2020 +0100

    ISIS-2473: adds support for j-doc processing on constructors
---
 .../modules/generated/pages/index/RoleMemento.adoc |   8 +
 .../modules/generated/pages/index/UserMemento.adoc |   5 +
 .../apache/isis/tooling/cli/doclet/Adoclet.java    |  26 +++-
 .../isis/tooling/cli/doclet/AdocletContext.java    |  11 +-
 .../apache/isis/tooling/cli/doclet/Adoclets.java   |  10 ++
 .../apache/isis/tooling/cli/doclet/ToAsciiDoc.java |  54 ++++++-
 .../isis/tooling/cli/test/doclet/AdocletTest.java  |   5 +-
 .../cli/test/doclet/samples/RoleMemento.java       |  68 +++++++++
 .../cli/test/doclet/samples/UserMemento.java       | 169 ++++++++++++++++++++-
 .../javamodel/ConstructorDeclarations.java}        |  24 +--
 .../apache/isis/tooling/javamodel/Javadocs.java    |   9 ++
 .../isis/tooling/javamodel/TypeDeclarations.java   |  15 ++
 12 files changed, 383 insertions(+), 21 deletions(-)

diff --git a/antora/components/system/modules/generated/pages/index/RoleMemento.adoc b/antora/components/system/modules/generated/pages/index/RoleMemento.adoc
index 3a0294e..c1026bc 100644
--- a/antora/components/system/modules/generated/pages/index/RoleMemento.adoc
+++ b/antora/components/system/modules/generated/pages/index/RoleMemento.adoc
@@ -4,3 +4,11 @@
 
 Immutable serializable value held by xref:system:generated:index/UserMemento.adoc[UserMemento] .
 
+* `[teal]#*RoleMemento*#(String name)` : `<self>`
++
+Creates a new role with the specified name. Description is left blank.
+
+
+* `[teal]#*RoleMemento*#(String name, String description)` : `<self>`
++
+Creates a new role with the specified name and description.
diff --git a/antora/components/system/modules/generated/pages/index/UserMemento.adoc b/antora/components/system/modules/generated/pages/index/UserMemento.adoc
index a7031f8..5df7630 100644
--- a/antora/components/system/modules/generated/pages/index/UserMemento.adoc
+++ b/antora/components/system/modules/generated/pages/index/UserMemento.adoc
@@ -4,6 +4,11 @@
 
 Immutable serializable value holding details about a user and its roles.
 
+* `[teal]#*UserMemento*#(String name, Stream<xref:system:generated:index/RoleMemento.adoc[RoleMemento]> roles)` : `<self>`
++
+Creates a new user with the specified name and assigned roles.
+
+
 * `[teal]#*_system_*#()` : `xref:system:generated:index/UserMemento.adoc[UserMemento]`
 +
 The framework's internal user with unrestricted privileges.
diff --git a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclet.java b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclet.java
index 6326024..9ef2956 100644
--- a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclet.java
+++ b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclet.java
@@ -92,6 +92,11 @@ public class Adoclet {
                 .filter(Javadocs::presentAndNotHidden)
                 .collect(Can.toCan());
         
+        val cds = TypeDeclarations.streamPublicConstructorDeclarations(td)
+                .filter(Javadocs::presentAndNotHidden)
+                .collect(Can.toCan());
+        
+        
         val toAdocConverter = ToAsciiDoc.of(docletContext);
 
         // -- intro
@@ -110,6 +115,14 @@ public class Adoclet {
                     getDeclarationKeyword(), 
                     td.getName().asString()));
             
+            
+            cds.forEach(cd->{
+                
+                java.append(String.format("\n  %s // <.>\n", 
+                        Adoclets.toNormalizedConstructorDeclaration(cd)));
+                
+            });
+            
             mds.forEach(md->{
     
                 java.append(String.format("\n  %s // <.>\n", 
@@ -123,10 +136,21 @@ public class Adoclet {
                     AsciiDocFactory.SourceFactory.java(java.toString(), td.getName().asString()));
         }
             
-        // -- method descriptions
+        // -- constructor and method descriptions
         
         val methodDescriptions = new StringBuilder();
         
+        cds.forEach(cd->{
+            
+            cd.getJavadoc()
+            .ifPresent(javadoc->{
+                methodDescriptions.append(String.format(docletContext.getMethodDescriptionFormat(),
+                        toAdocConverter.constructorDeclaration(cd),
+                        toAdocConverter.javadoc(javadoc, 1)));
+            });
+            
+        });
+        
         mds.forEach(md->{
             
             md.getJavadoc()
diff --git a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/AdocletContext.java b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/AdocletContext.java
index c77815b..85e3032 100644
--- a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/AdocletContext.java
+++ b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/AdocletContext.java
@@ -39,10 +39,16 @@ public class AdocletContext {
     private final @NonNull String xrefPageIdFormat;
     
     /**
+     * return-type | name | param-list
+     */
+    @Builder.Default
+    private final @NonNull String methodFormat = "`%s %s(%s)`";
+    
+    /**
      * method-generic-type | return-type | name | param-list
      */
     @Builder.Default
-    private final @NonNull String methodFormat = "`%s %s %s(%s)`";
+    private final @NonNull String genericMethodFormat = "`%s %s %s(%s)`";
     
     @Builder.Default
     private final @NonNull String methodNameFormat = "[teal]#*%s*#";
@@ -100,7 +106,8 @@ public class AdocletContext {
     
     public static AdocletContextBuilder compactFormat() {
         return AdocletContext.builder()
-                .methodFormat("`%3$s%1$s(%4$s)` : `%2$s`") //  method-generic-type | return-type | name | param-list)
+                .methodFormat("`%2$s(%3$s)` : `%1$s`") //  return-type | name | param-list)
+                .genericMethodFormat("`%3$s%1$s(%4$s)` : `%2$s`") //  method-generic-type | return-type | name | param-list)
                 .methodDescriptionFormat("\n* %s\n%s\n") // method | description
                 .includeJavaSource(false)
                 ;        
diff --git a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclets.java b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclets.java
index 40a4183..8f67f2c 100644
--- a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclets.java
+++ b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/Adoclets.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.tooling.cli.doclet;
 
+import com.github.javaparser.ast.body.ConstructorDeclaration;
 import com.github.javaparser.ast.body.MethodDeclaration;
 import com.github.javaparser.ast.body.TypeDeclaration;
 
@@ -48,6 +49,13 @@ final class Adoclets {
     }
     
     /**
+     * Returns given {@link ConstructorDeclaration} as normal text, without formatting.
+     */
+    static String toNormalizedConstructorDeclaration(final @NonNull ConstructorDeclaration cd) {
+        return cd.getDeclarationAsString(false, false, true).trim();
+    }
+    
+    /**
      * Returns given {@link MethodDeclaration} as normal text, without formatting.
      */
     static String toNormalizedMethodDeclaration(final @NonNull MethodDeclaration md) {
@@ -55,4 +63,6 @@ final class Adoclets {
     }
     
     
+    
+    
 }
diff --git a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/ToAsciiDoc.java b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/ToAsciiDoc.java
index f8aa58e..fbe61dd 100644
--- a/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/ToAsciiDoc.java
+++ b/tooling/cli/src/main/java/org/apache/isis/tooling/cli/doclet/ToAsciiDoc.java
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 
 import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.body.ConstructorDeclaration;
 import com.github.javaparser.ast.body.MethodDeclaration;
 import com.github.javaparser.ast.body.Parameter;
 import com.github.javaparser.ast.expr.SimpleName;
@@ -37,6 +38,8 @@ import com.github.javaparser.javadoc.description.JavadocSnippet;
 import org.asciidoctor.ast.Document;
 import org.jsoup.Jsoup;
 
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal._Constants;
 import org.apache.isis.tooling.javamodel.Javadocs;
 import org.apache.isis.tooling.model4adoc.AsciiDocFactory;
 import org.apache.isis.tooling.model4adoc.AsciiDocWriter;
@@ -49,6 +52,40 @@ import lombok.val;
 class ToAsciiDoc {
 
     private final AdocletContext docletContext;
+    
+    public String constructorDeclaration(final @NonNull ConstructorDeclaration cd) {
+        val isDeprecated = cd.getAnnotations().stream()
+                .anyMatch(a->a.getNameAsString().equals("Deprecated"))
+                || cd.getJavadoc()
+                    .map(Javadocs::hasDeprecated)
+                    .orElse(false);
+        
+        val methodNameFormat = isDeprecated
+                ? cd.isStatic()
+                        ? docletContext.getDeprecatedStaticMethodNameFormat()
+                        : docletContext.getDeprecatedMethodNameFormat()
+                : cd.isStatic()
+                    ? docletContext.getStaticMethodNameFormat()
+                    : docletContext.getMethodNameFormat();
+
+        val isGenericMethod = cd.getTypeParameters().isNonEmpty();
+        
+        val methodFormat = isGenericMethod
+                ? docletContext.getGenericMethodFormat()
+                : docletContext.getMethodFormat();
+        
+        val args = Can.<Object>of(
+                isGenericMethod ? typeParamters(cd.getTypeParameters()) : null,  // Cans do ignored null 
+                "<self>",
+                String.format(methodNameFormat, cd.getNameAsString()), 
+                cd.getParameters()
+                    .stream()
+                    .map(this::parameterDeclaration)
+                    .collect(Collectors.joining(", "))
+                );
+       
+        return String.format(methodFormat, args.toArray(_Constants.emptyObjects));
+    }
 
     public String methodDeclaration(final @NonNull MethodDeclaration md) {
         
@@ -65,16 +102,27 @@ class ToAsciiDoc {
                 : md.isStatic()
                     ? docletContext.getStaticMethodNameFormat()
                     : docletContext.getMethodNameFormat();
+
+        val isGenericMethod = md.getTypeParameters().isNonEmpty();
+        
+        val methodFormat = isGenericMethod
+                ? docletContext.getGenericMethodFormat()
+                : docletContext.getMethodFormat();
         
-        return String.format(docletContext.getMethodFormat(),
-                typeParamters(md.getTypeParameters()),
+        val args = Can.<Object>of(
+                isGenericMethod ? typeParamters(md.getTypeParameters()) : null,  // Cans do ignored null 
                 type(md.getType()),
                 String.format(methodNameFormat, md.getNameAsString()), 
                 md.getParameters()
                     .stream()
                     .map(this::parameterDeclaration)
-                    .collect(Collectors.joining(", ")));
+                    .collect(Collectors.joining(", "))
+                );
+       
+        return String.format(methodFormat, args.toArray(_Constants.emptyObjects));
+                
     }
+
     
     public String typeParamters(final @Nullable NodeList<TypeParameter> typeParamters) {
         if(typeParamters == null
diff --git a/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/AdocletTest.java b/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/AdocletTest.java
index 4cac61d..f18aef7 100644
--- a/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/AdocletTest.java
+++ b/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/AdocletTest.java
@@ -39,7 +39,9 @@ class AdocletTest {
         val projDir = new File("./").getAbsoluteFile();
         val analyzerConfig = AnalyzerConfigFactory.mavenTest(projDir, Language.JAVA).main();
 
-        val docletContext = AdocletContext.compactFormat()
+        val docletContext = AdocletContext
+                //.javaSourceWithFootNotesFormat()
+                .compactFormat()
                 .xrefPageIdFormat("system:generated:index/%s.adoc")
                 .build();
         
@@ -53,6 +55,7 @@ class AdocletTest {
         .forEach(doclet->{
             
             System.out.println(doclet.toAsciiDoc(docletContext));
+            System.out.println();
 
         });
     }
diff --git a/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/RoleMemento.java b/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/RoleMemento.java
new file mode 100644
index 0000000..92882fc
--- /dev/null
+++ b/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/RoleMemento.java
@@ -0,0 +1,68 @@
+/*
+ *  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.isis.tooling.cli.test.doclet.samples;
+
+import java.io.Serializable;
+
+import lombok.Getter;
+import lombok.Value;
+
+/**
+ * Immutable serializable value held by {@link UserMemento}.  
+ * @since 2.0 {@index}
+ */
+@Value
+public final class RoleMemento implements Serializable {
+
+    private static final long serialVersionUID = -3876856609238378274L;
+    
+    /**
+     * Creates a new role with the specified name. Description is left blank.
+     */
+    public RoleMemento(final String name) {
+        this(name, "");
+    }
+
+    /**
+     * Creates a new role with the specified name and description.
+     */
+    public RoleMemento(final String name, final String description) {
+        if (name == null) {
+            throw new IllegalArgumentException("Name not specified");
+        }
+        this.name = name;
+        if (description == null) {
+            throw new IllegalArgumentException("Description not specified");
+        }
+        this.description = description;
+    }
+
+    public String title() {
+        return name;
+    }
+
+    //@MemberOrder(sequence = "1.1")
+    @Getter
+    private final String name;
+
+    //@MemberOrder(sequence = "1.2")
+    @Getter
+    private final String description;
+
+}
diff --git a/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/UserMemento.java b/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/UserMemento.java
index 070741d..739a433 100644
--- a/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/UserMemento.java
+++ b/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/UserMemento.java
@@ -18,14 +18,173 @@
  */
 package org.apache.isis.tooling.cli.test.doclet.samples;
 
-import lombok.Value;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.collections._Lists;
+
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
  * Immutable serializable value holding details about a user and its roles.
  * @since 2.0 {@index}
  */
-@Value
-public class UserMemento {
+public final class UserMemento implements Serializable {
+    
+    private static final long serialVersionUID = 7190090455587885367L;
+    private static final UserMemento SYSTEM_USER = UserMemento.ofName("__system"); 
+//    private static final UserMemento NO_USER = UserMemento.ofName("__nobody");
+    
+    // -- FACTORIES
+    
+    /**
+     * The framework's internal user with unrestricted privileges.
+     */
+    public static UserMemento system() {
+        return SYSTEM_USER;
+    }
+    
+//    /**
+//     * The framework's internal user with no privileges at all, returned by the
+//     * {@link UserService} if no user is logged in.
+//     */
+//    public static UserMemento nobody() {
+//        return NO_USER;
+//    }
     
-    private final String name; 
-}
+    /**
+     * Creates a new user with the specified name and no roles.
+     */
+    public static UserMemento ofName(
+            final @NonNull String name) {
+        return new UserMemento(name, Stream.empty());
+    }
+    
+    /**
+     * Creates a new user with the specified name and assigned roles.
+     */
+    public static UserMemento ofNameAndRoles(
+            final @NonNull String name, 
+            final RoleMemento... roles) {
+        return new UserMemento(name, Stream.of(roles));
+    }
+    
+    /**
+     * Creates a new user with the specified name and assigned role names.
+     */
+    public static UserMemento ofNameAndRoleNames(
+            final @NonNull String name, 
+            final String... roleNames) {
+        return new UserMemento(name, Stream.of(roleNames).map(RoleMemento::new));
+    }
+    
+    /**
+     * Creates a new user with the specified name and assigned role names.
+     */
+    public static UserMemento ofNameAndRoleNames(
+            final @NonNull String name, 
+            final @NonNull Stream<String> roleNames) {
+        return new UserMemento(name, roleNames.map(RoleMemento::new));
+    }
+    
+    // -- CONSTRUCTOR
+
+    /**
+     * Creates a new user with the specified name and assigned roles.
+     */
+    public UserMemento(final String name, final @NonNull Stream<RoleMemento> roles) {
+        if (_Strings.isEmpty(name)) {
+            throw new IllegalArgumentException("Name not specified");
+        }
+        this.name = name;
+        this.roles = roles.collect(_Lists.toUnmodifiable()); 
+    }
+
+    public String title() {
+        return name;
+    }
+
+    /**
+     * The user's login name.
+     */
+    //@MemberOrder(sequence = "1.1")
+    @Getter
+    private final String name;
+
+    /**
+     * The roles associated with this user.
+     */
+    //@MemberOrder(sequence = "1.1")
+    private final List<RoleMemento> roles;
+    public List<RoleMemento> getRoles() {
+        return roles;
+    }
+
+    /**
+     * Determine if the specified name is this user.
+     *
+     * <p>
+     *
+     * @return true if the names match (is case sensitive).
+     */
+    public boolean isCurrentUser(final @Nullable String userName) {
+        return name.equals(userName);
+    }
+    
+    public Stream<String> streamRoleNames() {
+        return roles.stream()
+                .map(RoleMemento::getName);
+    }
+    
+    public boolean hasRoleName(final @Nullable String roleName) {
+        return streamRoleNames().anyMatch(myRoleName->myRoleName.equals(roleName));
+    }
+
+    // -- TO STRING, EQUALS, HASHCODE
+    
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        for (final RoleMemento role : roles) {
+            buf.append(role.getName()).append(" ");
+        }
+        return "User [name=" + getName() + ",roles=" + buf.toString() + "]";
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (obj.getClass() != getClass()) {
+            return false;
+        }
+        return isEqualsTo((UserMemento) obj);
+    }
+
+    private boolean isEqualsTo(final UserMemento other) {
+        if(!Objects.equals(this.getName(), other.getName())) {
+            return false;
+        }
+        if(!Objects.equals(this.getRoles(), other.getRoles())) {
+            return false;
+        }
+        return true;
+    }
+    
+    @Override
+    public int hashCode() {
+        return getName().hashCode(); // its good enough to hash on just the user's name
+    }
+    
+
+}
\ No newline at end of file
diff --git a/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/UserMemento.java b/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/ConstructorDeclarations.java
similarity index 61%
copy from tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/UserMemento.java
copy to tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/ConstructorDeclarations.java
index 070741d..fce8b57 100644
--- a/tooling/cli/src/test/java/org/apache/isis/tooling/cli/test/doclet/samples/UserMemento.java
+++ b/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/ConstructorDeclarations.java
@@ -16,16 +16,22 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.tooling.cli.test.doclet.samples;
+package org.apache.isis.tooling.javamodel;
 
-import lombok.Value;
+import com.github.javaparser.ast.body.ConstructorDeclaration;
 
-/**
- * Immutable serializable value holding details about a user and its roles.
- * @since 2.0 {@index}
- */
-@Value
-public class UserMemento {
+import lombok.NonNull;
+
+public final class ConstructorDeclarations {
+
+    public static boolean isEffectivePublic(
+            final @NonNull ConstructorDeclaration cd) {
+        //TODO effective public requires more context, eg. is the container an interface 
+        return !cd.isPrivate() 
+                && !cd.isAbstract() 
+                && !cd.isProtected()
+                //&& !md.isDefault()
+                ;
+    }
     
-    private final String name; 
 }
diff --git a/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/Javadocs.java b/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/Javadocs.java
index 5458d10..e801cfb 100644
--- a/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/Javadocs.java
+++ b/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/Javadocs.java
@@ -20,6 +20,7 @@ package org.apache.isis.tooling.javamodel;
 
 import java.util.stream.Stream;
 
+import com.github.javaparser.ast.body.ConstructorDeclaration;
 import com.github.javaparser.ast.body.MethodDeclaration;
 import com.github.javaparser.javadoc.Javadoc;
 import com.github.javaparser.javadoc.JavadocBlockTag;
@@ -46,6 +47,14 @@ public final class Javadocs {
     }
     
     public static boolean presentAndNotHidden(
+            final @NonNull ConstructorDeclaration cd) {
+        
+        return cd.getJavadoc()
+        .map(jd->!hasHidden(jd))
+        .orElse(false);
+    }
+    
+    public static boolean presentAndNotHidden(
             final @NonNull MethodDeclaration md) {
         
         return md.getJavadoc()
diff --git a/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/TypeDeclarations.java b/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/TypeDeclarations.java
index 5781a49..6198a4e 100644
--- a/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/TypeDeclarations.java
+++ b/tooling/javamodel/src/main/java/org/apache/isis/tooling/javamodel/TypeDeclarations.java
@@ -20,6 +20,7 @@ package org.apache.isis.tooling.javamodel;
 
 import java.util.stream.Stream;
 
+import com.github.javaparser.ast.body.ConstructorDeclaration;
 import com.github.javaparser.ast.body.MethodDeclaration;
 import com.github.javaparser.ast.body.TypeDeclaration;
 
@@ -27,6 +28,17 @@ import lombok.NonNull;
 
 public final class TypeDeclarations {
 
+    public static <T> Stream<ConstructorDeclaration> streamConstructorDeclarations(
+            final @NonNull TypeDeclaration<?> typeDeclaration) {
+        return typeDeclaration.getConstructors().stream();
+    }
+    
+    public static <T> Stream<ConstructorDeclaration> streamPublicConstructorDeclarations(
+            final @NonNull TypeDeclaration<?> typeDeclaration) {
+        return streamConstructorDeclarations(typeDeclaration)
+                .filter(ConstructorDeclarations::isEffectivePublic);
+    }
+    
     public static <T> Stream<MethodDeclaration> streamMethodDeclarations(
             final @NonNull TypeDeclaration<?> typeDeclaration) {
         return typeDeclaration.getMethods().stream();
@@ -38,6 +50,9 @@ public final class TypeDeclarations {
                 .filter(MethodDeclarations::isEffectivePublic);
     }
     
+    
+    
+    
 
     
 }