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/02/14 16:35:17 UTC

[isis] branch master updated: ISIS-2223: use ImmutableEnumSet when applicable

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 b1019c7  ISIS-2223: use ImmutableEnumSet<ActionType> when applicable
b1019c7 is described below

commit b1019c739b8798bcc19e0989b9a04495c22f8965
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Feb 14 17:35:05 2020 +0100

    ISIS-2223: use ImmutableEnumSet<ActionType> when applicable
---
 .../isis/core/metamodel/facets/Annotations.java    | 458 ++++++++++-----------
 .../metamodel/facets/MethodRemoverConstants.java   |   2 +-
 .../encoder/EncodableFacetUsingEncoderDecoder.java |   2 +-
 .../NavigableParentAnnotationFacetFactory.java     |   6 +-
 .../param/DeriveFacetsPostProcessor.java           |  13 +-
 .../isis/core/metamodel/spec/ActionType.java       |  10 +-
 .../spec/feature/ObjectActionContainer.java        |   4 +-
 .../rendering/service/swagger/internal/Util.java   |  17 +-
 .../collection/AssociatedWithActionsHelper.java    |  16 +-
 9 files changed, 262 insertions(+), 266 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Annotations.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Annotations.java
index e1676eb..6a02cf9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Annotations.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/Annotations.java
@@ -28,33 +28,21 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import javax.validation.constraints.Pattern;
-import javax.xml.bind.annotation.XmlTransient;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-import org.apache.isis.applib.annotation.Collection;
-import org.apache.isis.applib.annotation.CollectionLayout;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.annotation.PropertyLayout;
-import org.apache.isis.applib.annotation.Title;
 import org.apache.isis.core.commons.internal.base._Casts;
 import org.apache.isis.core.commons.internal.collections._Lists;
-import org.apache.isis.core.commons.internal.collections._Sets;
 import org.apache.isis.core.commons.internal.reflection._Annotations;
 import org.apache.isis.core.commons.internal.reflection._Reflect;
 import org.apache.isis.core.metamodel.commons.MethodUtil;
 import org.apache.isis.core.metamodel.commons.ThrowableExtensions;
 import org.apache.isis.core.metamodel.exceptions.MetaModelException;
 
-import static org.apache.isis.core.commons.internal.base._NullSafe.stream;
+import lombok.extern.log4j.Log4j2;
 
+@Log4j2
 public final class Annotations  {
 
     private Annotations() {}
@@ -202,135 +190,136 @@ public final class Annotations  {
         //}
     }
 
-    /**
-     * Searches for annotation on provided method, and if not found for any
-     * inherited methods up from the superclass.
-     *
-     * <p>
-     *     WARN: this method does NOT search for meta-annotations; use {@link #getAnnotations(Class, Class)} for that.
-     * </p>
-     */
-    private static <T extends Annotation> T getAnnotation(
-            final Method method,
-            final Class<T> annotationClass) {
-        if (method == null) {
-            return null;
-        }
-        final T annotation = method.getAnnotation(annotationClass);
-        if (annotation != null) {
-            return annotation;
-        }
-
-        final Class<?> methodDeclaringClass = method.getDeclaringClass();
-
-        // search for field
-        if ( shouldSearchForField(annotationClass) ) {
-            final Field field = firstDeclaredField_matching(
-                    methodDeclaringClass, isFieldForGetter(method)); 
-            if(field!=null) {
-                final T fieldAnnotation = field.getAnnotation(annotationClass);
-                if(fieldAnnotation != null) {
-                    return fieldAnnotation;
-                }
-            }
-
-        }
-
-        // search superclasses
-        final Class<?> superclass = methodDeclaringClass.getSuperclass();
-        if (superclass != null) {
-            final Method parentClassMethod = 
-                    firstDeclaredMethod_matching(method, superclass, isSuperMethodFor(method)); 
-
-            if(parentClassMethod!=null) {
-                final T methodAnnotation = getAnnotation(parentClassMethod, annotationClass);
-                if(methodAnnotation != null) {
-                    return methodAnnotation;
-                }
-            }
-        }
-
-        // search implemented interfaces
-        final Class<?>[] interfaces = methodDeclaringClass.getInterfaces();
-        for (final Class<?> iface : interfaces) {
-            final Method ifaceMethod = 
-                    firstDeclaredMethod_matching(method, iface, isSuperMethodFor(method));
-
-            if(ifaceMethod!=null) {
-                final T methodAnnotation = getAnnotation(ifaceMethod, annotationClass);
-                if(methodAnnotation != null) {
-                    return methodAnnotation;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Searches for annotation on provided method, and if not found for any
-     * inherited methods up from the superclass.
-     * @deprecated use {@link _Annotations} instead
-     */
-    private static <T extends Annotation> List<T> getAnnotations(
-            final Method method,
-            final Class<T> annotationClass) {
-        if (method == null) {
-            return Collections.emptyList();
-        }
-
-        final List<AnnotationAndDepth<T>> annotationAndDepths = _Lists.newArrayList();
-        for (final Annotation annotation : method.getAnnotations()) {
-            append(annotation, annotationClass, annotationAndDepths);
-        }
-        if(!annotationAndDepths.isEmpty()) {
-            return AnnotationAndDepth.sorted(annotationAndDepths);
-        }
-
-
-        // search for field
-        if ( shouldSearchForField(annotationClass) ) {
-            declaredFields_matching(method.getDeclaringClass(), isFieldForGetter(method), field->{
-                for(final Annotation annotation: field.getAnnotations()) {
-                    append(annotation, annotationClass, annotationAndDepths);
-                }
-            }); 
-        }
-        if(!annotationAndDepths.isEmpty()) {
-            return AnnotationAndDepth.sorted(annotationAndDepths);
-        }
-
-        // search superclasses
-        final Class<?> superclass = method.getDeclaringClass().getSuperclass();
-        if (superclass != null) {
-            final Method parentClassMethod = 
-                    firstDeclaredMethod_matching(method, superclass, isSuperMethodFor(method)); 
-
-            if(parentClassMethod!=null) {
-                final List<T> annotationsFromSuperclass = 
-                        getAnnotations(parentClassMethod, annotationClass);
-                if(!annotationsFromSuperclass.isEmpty()) {
-                    return annotationsFromSuperclass;
-                }
-            }
-        }
-
-        // search implemented interfaces
-        final Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
-        for (final Class<?> iface : interfaces) {
-            final Method ifaceMethod = 
-                    firstDeclaredMethod_matching(method, iface, isSuperMethodFor(method));
-
-            if(ifaceMethod!=null) {
-                final List<T> annotationsFromInterfaces = getAnnotations(ifaceMethod, annotationClass);
-                if(!annotationsFromInterfaces.isEmpty()) {
-                    return annotationsFromInterfaces;
-                }
-            }
-        }
-
-        return Collections.emptyList();
-    }
+// no longer used    
+//    /**
+//     * Searches for annotation on provided method, and if not found for any
+//     * inherited methods up from the superclass.
+//     *
+//     * <p>
+//     *     WARN: this method does NOT search for meta-annotations; use {@link #getAnnotations(Class, Class)} for that.
+//     * </p>
+//     */
+//    private static <T extends Annotation> T getAnnotation(
+//            final Method method,
+//            final Class<T> annotationClass) {
+//        if (method == null) {
+//            return null;
+//        }
+//        final T annotation = method.getAnnotation(annotationClass);
+//        if (annotation != null) {
+//            return annotation;
+//        }
+//
+//        final Class<?> methodDeclaringClass = method.getDeclaringClass();
+//
+//        // search for field
+//        if ( shouldSearchForField(annotationClass) ) {
+//            final Field field = firstDeclaredField_matching(
+//                    methodDeclaringClass, isFieldForGetter(method)); 
+//            if(field!=null) {
+//                final T fieldAnnotation = field.getAnnotation(annotationClass);
+//                if(fieldAnnotation != null) {
+//                    return fieldAnnotation;
+//                }
+//            }
+//
+//        }
+//
+//        // search superclasses
+//        final Class<?> superclass = methodDeclaringClass.getSuperclass();
+//        if (superclass != null) {
+//            final Method parentClassMethod = 
+//                    firstDeclaredMethod_matching(method, superclass, isSuperMethodFor(method)); 
+//
+//            if(parentClassMethod!=null) {
+//                final T methodAnnotation = getAnnotation(parentClassMethod, annotationClass);
+//                if(methodAnnotation != null) {
+//                    return methodAnnotation;
+//                }
+//            }
+//        }
+//
+//        // search implemented interfaces
+//        final Class<?>[] interfaces = methodDeclaringClass.getInterfaces();
+//        for (final Class<?> iface : interfaces) {
+//            final Method ifaceMethod = 
+//                    firstDeclaredMethod_matching(method, iface, isSuperMethodFor(method));
+//
+//            if(ifaceMethod!=null) {
+//                final T methodAnnotation = getAnnotation(ifaceMethod, annotationClass);
+//                if(methodAnnotation != null) {
+//                    return methodAnnotation;
+//                }
+//            }
+//        }
+//
+//        return null;
+//    }
+//
+//    /**
+//     * Searches for annotation on provided method, and if not found for any
+//     * inherited methods up from the superclass.
+//     * @deprecated use {@link _Annotations} instead
+//     */
+//    private static <T extends Annotation> List<T> getAnnotations(
+//            final Method method,
+//            final Class<T> annotationClass) {
+//        if (method == null) {
+//            return Collections.emptyList();
+//        }
+//
+//        final List<AnnotationAndDepth<T>> annotationAndDepths = _Lists.newArrayList();
+//        for (final Annotation annotation : method.getAnnotations()) {
+//            append(annotation, annotationClass, annotationAndDepths);
+//        }
+//        if(!annotationAndDepths.isEmpty()) {
+//            return AnnotationAndDepth.sorted(annotationAndDepths);
+//        }
+//
+//
+//        // search for field
+//        if ( shouldSearchForField(annotationClass) ) {
+//            declaredFields_matching(method.getDeclaringClass(), isFieldForGetter(method), field->{
+//                for(final Annotation annotation: field.getAnnotations()) {
+//                    append(annotation, annotationClass, annotationAndDepths);
+//                }
+//            }); 
+//        }
+//        if(!annotationAndDepths.isEmpty()) {
+//            return AnnotationAndDepth.sorted(annotationAndDepths);
+//        }
+//
+//        // search superclasses
+//        final Class<?> superclass = method.getDeclaringClass().getSuperclass();
+//        if (superclass != null) {
+//            final Method parentClassMethod = 
+//                    firstDeclaredMethod_matching(method, superclass, isSuperMethodFor(method)); 
+//
+//            if(parentClassMethod!=null) {
+//                final List<T> annotationsFromSuperclass = 
+//                        getAnnotations(parentClassMethod, annotationClass);
+//                if(!annotationsFromSuperclass.isEmpty()) {
+//                    return annotationsFromSuperclass;
+//                }
+//            }
+//        }
+//
+//        // search implemented interfaces
+//        final Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
+//        for (final Class<?> iface : interfaces) {
+//            final Method ifaceMethod = 
+//                    firstDeclaredMethod_matching(method, iface, isSuperMethodFor(method));
+//
+//            if(ifaceMethod!=null) {
+//                final List<T> annotationsFromInterfaces = getAnnotations(ifaceMethod, annotationClass);
+//                if(!annotationsFromInterfaces.isEmpty()) {
+//                    return annotationsFromInterfaces;
+//                }
+//            }
+//        }
+//
+//        return Collections.emptyList();
+//    }
 
     /**
      * Searches for all no-arg methods or fields with a specified title, returning an
@@ -522,35 +511,37 @@ public final class Annotations  {
                 return Optional.ofNullable(
                         _Reflect.getGetter(originatingClass, field.getName())    );
             } catch (IntrospectionException e) {
-                e.printStackTrace();
+                log.warn("failed reflective introspection on {} field {}", 
+                        originatingClass, field.getName(), e);
             }
             return Optional.empty();
         }
 
     }
 
-    private static Set<Class<? extends Annotation>> fieldAnnotationClasses = 
-            _Sets.of(
-                    Property.class,
-                    PropertyLayout.class,
-                    Collection.class,
-                    CollectionLayout.class,
-                    Programmatic.class,
-                    MemberOrder.class,
-                    Pattern.class,
-                    javax.annotation.Nullable.class,
-                    Title.class,
-                    XmlJavaTypeAdapter.class,
-                    XmlTransient.class
-                    //javax.jdo.annotations.Column.class
-                    );
-
-    private static boolean shouldSearchForField(final Class<?> annotationClass) {
-        if(annotationClass.getName().equals("javax.jdo.annotations.Column")) {
-            return true;
-        }
-        return fieldAnnotationClasses.contains(annotationClass);
-    }
+// no longer used    
+//    private static Set<Class<? extends Annotation>> fieldAnnotationClasses = 
+//            _Sets.of(
+//                    Property.class,
+//                    PropertyLayout.class,
+//                    Collection.class,
+//                    CollectionLayout.class,
+//                    Programmatic.class,
+//                    MemberOrder.class,
+//                    Pattern.class,
+//                    javax.annotation.Nullable.class,
+//                    Title.class,
+//                    XmlJavaTypeAdapter.class,
+//                    XmlTransient.class
+//                    //javax.jdo.annotations.Column.class
+//                    );
+//
+//    private static boolean shouldSearchForField(final Class<?> annotationClass) {
+//        if(annotationClass.getName().equals("javax.jdo.annotations.Column")) {
+//            return true;
+//        }
+//        return fieldAnnotationClasses.contains(annotationClass);
+//    }
 
     static List<String> fieldNameCandidatesFor(final String methodName) {
         if(methodName == null) {
@@ -624,72 +615,73 @@ public final class Annotations  {
         return Collections.emptyList();
     }
 
-    // -- HELPER
-
-    private static Method firstDeclaredMethod_matching(
-            Method method,
-            Class<?> type, 
-            Predicate<Method> filter) {
-
-        return stream(type.getDeclaredMethods())
-                .filter(filter)
-                .findFirst()
-                .orElse(null);
-    }
-
-    private static Field firstDeclaredField_matching(
-            Class<?> type, 
-            Predicate<Field> filter) {
-
-        return stream(type.getDeclaredFields())
-                .filter(filter)
-                .findFirst()
-                .orElse(null);
-    }
-
-    private static void declaredFields_matching(
-            Class<?> type, 
-            Predicate<Field> filter, 
-            Consumer<Field> onField) {
-
-        stream(type.getDeclaredFields())
-        .filter(filter)
-        .forEach(onField);
-
-    }
-
-    // -- HELPER - PREDICATES
-
-    private static Predicate<Method> isSuperMethodFor(final Method method) {
-        return m->_Reflect.same(method, m);
-    }
-
-    private static Predicate<Field> isFieldForGetter(final Method getter) {
-        return field->{
-            int beginIndex;
-            final String methodName = getter.getName();
-            if (methodName.startsWith("get")) {
-                beginIndex = 3;
-            } else if (methodName.startsWith("is")) {
-                beginIndex = 2;
-            } else {
-                return false;
-            }
-            if(methodName.length()==beginIndex) {
-                return false;
-            }
-            final String suffix = methodName.substring(beginIndex);
-            final char c = suffix.charAt(0);
-            final char lower = Character.toLowerCase(c);
-            final String candidate = "" + lower + suffix.substring(1);
-            if(field.getName().equals(candidate)) {
-                return true;
-            }
-            if(field.getName().equals("_" + candidate)) {
-                return true;
-            }
-            return false;
-        };
-    }
+// no longer used    
+//    // -- HELPER
+//
+//    private static Method firstDeclaredMethod_matching(
+//            Method method,
+//            Class<?> type, 
+//            Predicate<Method> filter) {
+//
+//        return stream(type.getDeclaredMethods())
+//                .filter(filter)
+//                .findFirst()
+//                .orElse(null);
+//    }
+//
+//    private static Field firstDeclaredField_matching(
+//            Class<?> type, 
+//            Predicate<Field> filter) {
+//
+//        return stream(type.getDeclaredFields())
+//                .filter(filter)
+//                .findFirst()
+//                .orElse(null);
+//    }
+//
+//    private static void declaredFields_matching(
+//            Class<?> type, 
+//            Predicate<Field> filter, 
+//            Consumer<Field> onField) {
+//
+//        stream(type.getDeclaredFields())
+//        .filter(filter)
+//        .forEach(onField);
+//
+//    }
+//
+//    // -- HELPER - PREDICATES
+//
+//    private static Predicate<Method> isSuperMethodFor(final Method method) {
+//        return m->_Reflect.same(method, m);
+//    }
+//
+//    private static Predicate<Field> isFieldForGetter(final Method getter) {
+//        return field->{
+//            int beginIndex;
+//            final String methodName = getter.getName();
+//            if (methodName.startsWith("get")) {
+//                beginIndex = 3;
+//            } else if (methodName.startsWith("is")) {
+//                beginIndex = 2;
+//            } else {
+//                return false;
+//            }
+//            if(methodName.length()==beginIndex) {
+//                return false;
+//            }
+//            final String suffix = methodName.substring(beginIndex);
+//            final char c = suffix.charAt(0);
+//            final char lower = Character.toLowerCase(c);
+//            final String candidate = "" + lower + suffix.substring(1);
+//            if(field.getName().equals(candidate)) {
+//                return true;
+//            }
+//            if(field.getName().equals("_" + candidate)) {
+//                return true;
+//            }
+//            return false;
+//        };
+//    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodRemoverConstants.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodRemoverConstants.java
index 4495531..d2d3a10 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodRemoverConstants.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodRemoverConstants.java
@@ -27,7 +27,7 @@ import org.apache.isis.core.metamodel.facetapi.MethodRemover;
 
 public class MethodRemoverConstants {
 
-    public static MethodRemover NOOP = new MethodRemover() {
+    public static final MethodRemover NOOP = new MethodRemover() {
 
         @Override
         public void removeMethod(final Method method) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/encodeable/encoder/EncodableFacetUsingEncoderDecoder.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/encodeable/encoder/EncodableFacetUsingEncoderDecoder.java
index 361c4a7..fd83ee3 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/encodeable/encoder/EncodableFacetUsingEncoderDecoder.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/encodeable/encoder/EncodableFacetUsingEncoderDecoder.java
@@ -38,7 +38,7 @@ implements EncodableFacet {
     }
 
     // TODO: is this safe? really?
-    public static String ENCODED_NULL = "NULL";
+    public static final String ENCODED_NULL = "NULL";
 
     @Override
     protected String toStringValues() {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
index a3e1757..e912854 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/navparent/annotation/NavigableParentAnnotationFacetFactory.java
@@ -32,6 +32,8 @@ import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
 import org.apache.isis.core.metamodel.facets.object.navparent.method.NavigableParentFacetMethod;
 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
 
+import lombok.extern.log4j.Log4j2;
+
 /**
  * For detailed behavioral specification see
  * <a href="https://issues.apache.org/jira/browse/ISIS-1816">ISIS-1816</a>.
@@ -39,6 +41,7 @@ import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
  * @since 2.0
  *
  */
+@Log4j2
 public class NavigableParentAnnotationFacetFactory extends FacetFactoryAbstract
 implements MetaModelRefiner {
 
@@ -89,7 +92,8 @@ implements MetaModelRefiner {
         try {
             super.addFacet(new NavigableParentFacetMethod(method, facetHolder));
         } catch (IllegalAccessException e) {
-            e.printStackTrace();
+            log.warn("failed to create NavigableParentFacetMethod method:{} holder:{}", 
+                    method, facetHolder, e);
         }
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java
index b68f724..6e54889 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java
@@ -20,7 +20,6 @@
 package org.apache.isis.core.metamodel.postprocessors.param;
 
 import java.lang.reflect.Method;
-import java.util.List;
 import java.util.stream.Stream;
 
 import org.apache.isis.applib.annotation.Collection;
@@ -28,7 +27,7 @@ import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.events.domain.ActionDomainEvent;
 import org.apache.isis.applib.events.domain.CollectionDomainEvent;
 import org.apache.isis.applib.events.domain.PropertyDomainEvent;
-import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.core.commons.collections.ImmutableEnumSet;
 import org.apache.isis.core.commons.internal.reflection._Annotations;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.context.MetaModelContextAware;
@@ -126,7 +125,7 @@ implements ObjectSpecificationPostProcessor, MetaModelContextAware {
     public void postProcess(final ObjectSpecification objectSpecification) {
 
         // all the actions of this type
-        final List<ActionType> actionTypes = inferActionTypes();
+        val actionTypes = inferActionTypes();
         final Stream<ObjectAction> objectActions = objectSpecification.streamObjectActions(actionTypes, Contributed.INCLUDED);
 
         // and all the collections of this type
@@ -619,13 +618,11 @@ implements ObjectSpecificationPostProcessor, MetaModelContextAware {
         return facet != null && !facet.isFallback();
     }
 
-    private List<ActionType> inferActionTypes() {
-        final List<ActionType> actionTypes = _Lists.newArrayList();
-        actionTypes.add(ActionType.USER);
+    private ImmutableEnumSet<ActionType> inferActionTypes() {
         if (metaModelContext.getSystemEnvironment().isPrototyping()) {
-            actionTypes.add(ActionType.PROTOTYPE);
+            return ActionType.USER_AND_PROTOTYPE;
         }
-        return actionTypes;
+        return ActionType.USER_ONLY;
     }
     
     private void addFacet(Facet facet) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ActionType.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ActionType.java
index 2921833..30bf5b2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ActionType.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ActionType.java
@@ -19,11 +19,11 @@
 
 package org.apache.isis.core.metamodel.spec;
 
-import java.util.Arrays;
-import java.util.List;
+import org.apache.isis.core.commons.collections.ImmutableEnumSet;
 
 public enum ActionType {
-    PROTOTYPE, USER;
+    USER,
+    PROTOTYPE;
 
     public String getName() {
         return name();
@@ -37,5 +37,7 @@ public enum ActionType {
         return this == USER;
     }
 
-    public static final List<ActionType> ALL = Arrays.asList(values());
+    public static final ImmutableEnumSet<ActionType> USER_ONLY = ImmutableEnumSet.of(ActionType.USER);
+    public static final ImmutableEnumSet<ActionType> USER_AND_PROTOTYPE = ImmutableEnumSet.of(ActionType.USER, ActionType.PROTOTYPE);
+    public static final ImmutableEnumSet<ActionType> ALL = ImmutableEnumSet.allOf(ActionType.class);
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionContainer.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionContainer.java
index 6e88ed6..91c8a4a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionContainer.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/feature/ObjectActionContainer.java
@@ -19,12 +19,12 @@
 
 package org.apache.isis.core.metamodel.spec.feature;
 
-import java.util.Collection;
 import java.util.Optional;
 import java.util.stream.Stream;
 
 import org.apache.isis.applib.Identifier;
 import org.apache.isis.core.commons.collections.Can;
+import org.apache.isis.core.commons.collections.ImmutableEnumSet;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.spec.ActionType;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -84,7 +84,7 @@ public interface ObjectActionContainer {
      */
     Stream<ObjectAction> streamObjectActions(ActionType type, Contributed contributee);
 
-    default Stream<ObjectAction> streamObjectActions(Collection<ActionType> types, Contributed contributee) {
+    default Stream<ObjectAction> streamObjectActions(ImmutableEnumSet<ActionType> types, Contributed contributee) {
         return stream(types)
                 .flatMap(type->streamObjectActions(type, contributee));
     }
diff --git a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/internal/Util.java b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/internal/Util.java
index 7dc3fbd..62512e0 100644
--- a/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/internal/Util.java
+++ b/viewers/restfulobjects/rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/swagger/internal/Util.java
@@ -18,7 +18,6 @@
  */
 package org.apache.isis.viewer.restfulobjects.rendering.service.swagger.internal;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.function.Predicate;
@@ -26,7 +25,9 @@ import java.util.stream.Collectors;
 
 import org.apache.isis.applib.services.swagger.SwaggerService;
 import org.apache.isis.core.commons.collections.Can;
+import org.apache.isis.core.commons.collections.ImmutableEnumSet;
 import org.apache.isis.core.commons.internal.base._Casts;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
 import org.apache.isis.core.metamodel.spec.ActionType;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -36,6 +37,8 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 
+import lombok.val;
+
 import io.swagger.models.Response;
 
 public final class Util {
@@ -139,7 +142,7 @@ public final class Util {
             final ObjectSpecification objectSpec,
             final SwaggerService.Visibility visibility,
             final ClassExcluder classExcluder) {
-        final List<ActionType> actionTypes = actionTypesFor(visibility);
+        val actionTypes = actionTypesFor(visibility);
 
         return objectSpec.streamObjectActions(actionTypes, Contributed.INCLUDED)
                 .filter(objectAction->
@@ -159,15 +162,15 @@ public final class Util {
         return response;
     }
 
-    static List<ActionType> actionTypesFor(final SwaggerService.Visibility visibility) {
+    static ImmutableEnumSet<ActionType> actionTypesFor(final SwaggerService.Visibility visibility) {
         switch (visibility) {
         case PUBLIC:
-            return Arrays.asList(ActionType.USER);
+            return ActionType.USER_ONLY;
         case PRIVATE:
-            return Arrays.asList(ActionType.USER);
+            return ActionType.USER_ONLY;
         case PRIVATE_WITH_PROTOTYPING:
-            return Arrays.asList(ActionType.USER, ActionType.PROTOTYPE);
+            return ActionType.USER_AND_PROTOTYPE;
         }
-        throw new IllegalArgumentException("Unrecognized type '" + visibility + "'");
+        throw _Exceptions.unmatchedCase(visibility);
     }
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
index a9520ea..0333247 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/AssociatedWithActionsHelper.java
@@ -24,15 +24,15 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import org.apache.isis.core.commons.internal.collections._Lists;
+import org.apache.isis.core.commons.collections.ImmutableEnumSet;
 import org.apache.isis.core.metamodel.spec.ActionType;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.Contributed;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 import org.apache.isis.core.webapp.context.IsisWebAppCommonContext;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 
 import lombok.val;
 
@@ -58,7 +58,7 @@ public class AssociatedWithActionsHelper implements Serializable {
 
         final ObjectSpecification objectSpec = getObjectSpecification();
 
-        final List<ActionType> actionTypes = inferActionTypes(collectionModel.getCommonContext());
+        val actionTypes = inferActionTypes(collectionModel.getCommonContext());
         final Stream<ObjectAction> objectActions = objectSpec.streamObjectActions(actionTypes, Contributed.INCLUDED);
 
         return objectActions
@@ -72,13 +72,11 @@ public class AssociatedWithActionsHelper implements Serializable {
         return parentAdapter.getSpecification();
     }
 
-    private List<ActionType> inferActionTypes(IsisWebAppCommonContext commonContext) {
-        final List<ActionType> actionTypes = _Lists.newArrayList();
-        actionTypes.add(ActionType.USER);
-        if ( commonContext.getSystemEnvironment().isPrototyping() ) {
-            actionTypes.add(ActionType.PROTOTYPE);
+    private ImmutableEnumSet<ActionType> inferActionTypes(IsisWebAppCommonContext commonContext) {
+        if (commonContext.getSystemEnvironment().isPrototyping()) {
+            return ActionType.USER_AND_PROTOTYPE;
         }
-        return actionTypes;
+        return ActionType.USER_ONLY;
     }
 
 }