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 2021/03/05 14:22:09 UTC

[isis] branch master updated: ISIS-2553: ApplicationFeatureRepository: polishing API

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 545cc2f  ISIS-2553: ApplicationFeatureRepository: polishing API
545cc2f is described below

commit 545cc2f52c2db0897b4ce20dbe33da77b790ebf6
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Mar 5 15:21:53 2021 +0100

    ISIS-2553: ApplicationFeatureRepository: polishing API
    
    made possible by further refactoring secman mixins
---
 .../appfeat/ApplicationFeatureRepository.java      |   8 --
 .../ApplicationFeatureRepositoryDefault.java       |  29 -----
 .../metamodel/services/appfeat/_Predicates.java    |  55 ---------
 .../services/appfeat/ApplicationFeatureIdTest.java |  94 --------------
 .../ApplicationFeatureChoices.java}                | 114 ++++-------------
 ...OrphanedPermissionManager_relocateSelected.java |   7 +-
 .../dom/role/ApplicationRole_addPermission.java    | 136 ++-------------------
 .../user/ApplicationUser_filterPermissions.java    |  47 ++++---
 8 files changed, 60 insertions(+), 430 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/appfeat/ApplicationFeatureRepository.java b/api/applib/src/main/java/org/apache/isis/applib/services/appfeat/ApplicationFeatureRepository.java
index da0b828..b5ec09f 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/appfeat/ApplicationFeatureRepository.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/appfeat/ApplicationFeatureRepository.java
@@ -20,7 +20,6 @@ package org.apache.isis.applib.services.appfeat;
 
 import java.util.Collection;
 import java.util.Map;
-import java.util.SortedSet;
 
 /**
  * Provides the access to string representations of the packages, classes and
@@ -31,13 +30,6 @@ import java.util.SortedSet;
  */
 public interface ApplicationFeatureRepository  {
 
-    //TODO[2553] why return String, when AppFeatId is now API
-    SortedSet<String> namespaceNames();
-
-    //TODO[2553] remove or rename->logicalTypeSimpleNamesRecursivelyContainedInNamespace
-    SortedSet<String> classNamesRecursivelyContainedIn(
-            String packageFqn);
-
     Map<String, ApplicationFeatureId> getFeatureIdentifiersByName();
 
     ApplicationFeature findFeature(ApplicationFeatureId featureId);
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
index dd9bdb6..c9d38a0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefault.java
@@ -25,9 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.OptionalInt;
-import java.util.Set;
 import java.util.SortedMap;
-import java.util.SortedSet;
 import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 
@@ -46,7 +44,6 @@ import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureSort;
 import org.apache.isis.applib.services.appfeat.ApplicationMemberSort;
 import org.apache.isis.commons.internal.collections._Maps;
-import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.config.metamodel.services.ApplicationFeaturesInitConfiguration;
@@ -64,8 +61,6 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
-import static org.apache.isis.commons.internal.base._NullSafe.stream;
-
 import lombok.NonNull;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
@@ -467,29 +462,5 @@ implements ApplicationFeatureRepository {
         return featureIdentifiersByName;
     }
     
-    // -- namespaceNames, packageNamesContainingClasses, classNamesContainedIn, memberNamesOf
-    
-    @Override
-    public SortedSet<String> namespaceNames() {
-        initializeIfRequired();
-        return stream(allFeatures(ApplicationFeatureSort.NAMESPACE))
-                .map(ApplicationFeature::getFullyQualifiedName)
-                .collect(_Sets.toUnmodifiableSorted());
-    }
-
-    @Override
-    public SortedSet<String> classNamesRecursivelyContainedIn(final String packageFqn) {
-        initializeIfRequired();
-        final ApplicationFeatureId packageId = ApplicationFeatureId.newNamespace(packageFqn);
-        final ApplicationFeature pkg = findNamespace(packageId);
-        if (pkg == null) {
-            return Collections.emptySortedSet();
-        }
-        final Set<ApplicationFeatureId> classIds = this.typeFeatures.keySet();
-        return classIds.stream()
-                .filter(_Predicates.isLogicalTypeRecursivelyWithin(packageId))
-                .map(ApplicationFeatureId::getTypeSimpleName)
-                .collect(_Sets.toUnmodifiableSorted());
-    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/_Predicates.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/_Predicates.java
deleted file mode 100644
index 674085e..0000000
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/_Predicates.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  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.core.metamodel.services.appfeat;
-
-import java.util.Objects;
-import java.util.function.Predicate;
-
-import org.apache.isis.applib.services.appfeat.ApplicationFeature;
-import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
-import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
-import org.apache.isis.applib.services.appfeat.ApplicationFeatureSort;
-import org.apache.isis.applib.services.appfeat.ApplicationMemberSort;
-
-final class _Predicates {
-
-    public static Predicate<ApplicationFeatureId> isLogicalTypeContaining(
-            final ApplicationMemberSort memberSort, 
-            final ApplicationFeatureRepository applicationFeatures) {
-        
-        return (final ApplicationFeatureId featureId) -> {
-                if(featureId.getSort() != ApplicationFeatureSort.TYPE) {
-                    return false;
-                }
-                final ApplicationFeature feature = applicationFeatures.findFeature(featureId);
-                if(feature == null) {
-                    return false;
-                }
-                return memberSort == null 
-                        || !feature.getMembersOfSort(memberSort).isEmpty();
-        };
-    }
-
-    public static Predicate<ApplicationFeatureId> isLogicalTypeRecursivelyWithin(
-            final ApplicationFeatureId namespaceId) {
-        return (final ApplicationFeatureId input) -> 
-            input.getPathIds().stream().skip(1L).anyMatch(id->Objects.equals(id, namespaceId));
-    }
-
-}
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureIdTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureIdTest.java
index bb8ccb4..f73fd05 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureIdTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureIdTest.java
@@ -20,11 +20,8 @@ package org.apache.isis.core.metamodel.services.appfeat;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.TreeSet;
 import java.util.function.Function;
 
-import org.jmock.Expectations;
-import org.jmock.auto.Mock;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -38,10 +35,8 @@ import static org.hamcrest.Matchers.emptyCollectionOf;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.lessThan;
 
-import org.apache.isis.applib.services.appfeat.ApplicationFeature;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureSort;
-import org.apache.isis.applib.services.appfeat.ApplicationMemberSort;
 import org.apache.isis.core.internaltestsupport.contract.ValueTypeContractTestAbstract;
 import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
 
@@ -586,93 +581,4 @@ public class ApplicationFeatureIdTest {
 
     }
 
-    public static class PredicatesTest extends ApplicationFeatureIdTest {
-
-        public static class IsClassContaining extends PredicatesTest {
-
-            @SuppressWarnings("unused")
-            private ApplicationMemberSort memberSort;
-
-            @Mock
-            private ApplicationFeatureRepositoryDefault mockApplicationFeatureRepository;
-            @Mock
-            private ApplicationFeature mockApplicationFeature;
-
-            @Test
-            public void whenNull() throws Exception {
-                expectedException.expect(NullPointerException.class);
-
-                _Predicates.
-                isLogicalTypeContaining(ApplicationMemberSort.ACTION, mockApplicationFeatureRepository).
-                test(null);
-            }
-
-            @Test
-            public void whenNotClass() throws Exception {
-                assertThat(
-                        _Predicates.
-                        isLogicalTypeContaining(ApplicationMemberSort.ACTION, mockApplicationFeatureRepository).
-                        test(ApplicationFeatureId.newNamespace("com.mycompany")),
-                        is(false));
-                assertThat(
-                        _Predicates.
-                        isLogicalTypeContaining(ApplicationMemberSort.ACTION, mockApplicationFeatureRepository).
-                        test(ApplicationFeatureId.newMember("com.mycompany.Bar#foo")),
-                        is(false));
-            }
-
-            @Test
-            public void whenClassButFeatureNotFound() throws Exception {
-                final ApplicationFeatureId classFeature = ApplicationFeatureId.newType("com.mycompany.Bar");
-                context.checking(new Expectations() {{
-                    allowing(mockApplicationFeatureRepository).findFeature(classFeature);
-                    will(returnValue(null));
-                }});
-
-                assertThat(
-                        _Predicates.
-                        isLogicalTypeContaining(ApplicationMemberSort.ACTION, mockApplicationFeatureRepository).
-                        test(classFeature),
-                        is(false));
-            }
-            @Test
-            public void whenClassAndFeatureNotFoundButHasNoMembersOfType() throws Exception {
-                final ApplicationFeatureId classFeature = ApplicationFeatureId.newType("com.mycompany.Bar");
-                context.checking(new Expectations() {{
-                    oneOf(mockApplicationFeatureRepository).findFeature(classFeature);
-                    will(returnValue(mockApplicationFeature));
-
-                    allowing(mockApplicationFeature).getMembersOfSort(ApplicationMemberSort.ACTION);
-                    will(returnValue(new TreeSet<>()));
-                }});
-
-                assertThat(
-                        _Predicates.
-                        isLogicalTypeContaining(ApplicationMemberSort.ACTION, mockApplicationFeatureRepository).
-                        test(classFeature),
-                        is(false));
-            }
-            @Test
-            public void whenClassAndFeatureNotFoundAndHasMembersOfType() throws Exception {
-                final ApplicationFeatureId classFeature = ApplicationFeatureId.newType("com.mycompany.Bar");
-                context.checking(new Expectations() {{
-                    oneOf(mockApplicationFeatureRepository).findFeature(classFeature);
-                    will(returnValue(mockApplicationFeature));
-
-                    allowing(mockApplicationFeature).getMembersOfSort(ApplicationMemberSort.ACTION);
-                    will(returnValue(new TreeSet<ApplicationFeatureId>() {{
-                        add(ApplicationFeatureId.newMember("com.mycompany.Bar#foo"));
-                    }}));
-                }});
-
-                assertThat(
-                        _Predicates.
-                        isLogicalTypeContaining(ApplicationMemberSort.ACTION, mockApplicationFeatureRepository).
-                        test(classFeature),
-                        is(true));
-            }
-        }
-    }
-
-
 }
\ No newline at end of file
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addPermission.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/feature/ApplicationFeatureChoices.java
similarity index 54%
copy from extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addPermission.java
copy to extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/feature/ApplicationFeatureChoices.java
index 90a0d53..27098a5 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addPermission.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/feature/ApplicationFeatureChoices.java
@@ -16,153 +16,90 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.secman.model.dom.role;
+package org.apache.isis.extensions.secman.model.dom.feature;
 
+import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
 import java.util.TreeSet;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import javax.enterprise.inject.Model;
-import javax.inject.Inject;
-
 import org.apache.isis.applib.ViewModel;
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.DomainObject;
-import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.MinLength;
 import org.apache.isis.applib.annotation.Nature;
-import org.apache.isis.applib.annotation.Optionality;
-import org.apache.isis.applib.annotation.Parameter;
-import org.apache.isis.applib.annotation.ParameterLayout;
 import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.services.appfeat.ApplicationFeature;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.commons.internal.functions._Predicates;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRepository;
-import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRule;
-import org.apache.isis.extensions.secman.api.role.ApplicationRole;
-import org.apache.isis.extensions.secman.api.role.ApplicationRole.AddPermissionDomainEvent;
+import org.apache.isis.extensions.secman.model.dom.role.ApplicationRole_addPermission;
+import org.apache.isis.extensions.secman.model.dom.user.ApplicationUser_filterPermissions;
 
 import lombok.AllArgsConstructor;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
-import lombok.RequiredArgsConstructor;
-import lombok.Value;
 import lombok.val;
-import lombok.experimental.Accessors;
-
-@Action(
-        domainEvent = AddPermissionDomainEvent.class, 
-        associateWith = "permissions")
-@ActionLayout(named="Add")
-@RequiredArgsConstructor
-public class ApplicationRole_addPermission {
-    
-    @Inject private ApplicationFeatureRepository applicationFeatureRepository;
-    @Inject private ApplicationPermissionRepository<? extends ApplicationPermission> applicationPermissionRepository;
-    
-    private final ApplicationRole target;
-    
-    @Value @Accessors(fluent = true)           
-    public static class Parameters {
-        ApplicationPermissionRule rule; // ALLOW/VETO
-        ApplicationPermissionMode mode; // r/w
-        AppFeat feature;
-    }
 
-    /**
-     * Adds a {@link ApplicationPermission permission} for this role to a
-     * {@link ApplicationFeature feature}.
-     */
-    @MemberOrder(sequence = "0")
-    public ApplicationRole act(
-            
-            @Parameter(optionality = Optionality.MANDATORY)
-            @ParameterLayout(named="Rule")
-            final ApplicationPermissionRule rule,
-            
-            @Parameter(optionality = Optionality.MANDATORY)
-            @ParameterLayout(named="Mode")
-            final ApplicationPermissionMode mode,
-            
-            @Parameter(optionality = Optionality.MANDATORY)
-            @ParameterLayout(
-                    named = "Feature",
-                    describedAs = "To refine the search by sort (namespace, type, member), "
-                            + "use one of "
-                            + "sort:n sort:t sort:m.")
-            final AppFeat feature) {
-        
-        applicationPermissionRepository.newPermission(target, rule, mode, feature.getFeatureId());
-        return target;
-    }
-
-    @Model
-    public ApplicationPermissionRule defaultRule(Parameters params) {
-        return ApplicationPermissionRule.ALLOW;
-    }
-
-    @Model
-    public ApplicationPermissionMode defaultMode(Parameters params) {
-        return ApplicationPermissionMode.CHANGING;
-    }
+/**
+ * Supports mixins {@link ApplicationRole_addPermission} 
+ * and {@link ApplicationUser_filterPermissions}.
+ *
+ */
+public final class ApplicationFeatureChoices {
 
-    @Model
-    public java.util.Collection<AppFeat> autoCompleteFeature(
-            final Parameters params,
+    public static final String DESCRIBED_AS = "To refine the search by feature-sort (namespace, type, member), "
+            + "use one of "
+            + "sort:n sort:t sort:m.";
+    
+    public static Collection<ApplicationFeatureChoices.AppFeat> autoCompleteFeature(
+            final ApplicationFeatureRepository featureRepository,
             final @MinLength(3) String search) {
         
         final Predicate<ApplicationFeatureId> searchRefine; 
         final String searchTerm;
         
         if(search.startsWith("sort:n")) {
-            searchRefine = this::isNamespace;
+            searchRefine = ApplicationFeatureChoices::isNamespace;
             searchTerm = search.substring(6).trim();
         } else if(search.startsWith("sort:t")) {
-            searchRefine = this::isType;
+            searchRefine = ApplicationFeatureChoices::isType;
             searchTerm = search.substring(6).trim();
         } else if(search.startsWith("sort:m")) {
-            searchRefine = this::isMember;
+            searchRefine = ApplicationFeatureChoices::isMember;
             searchTerm = search.substring(6).trim();
         } else {
             searchRefine = _Predicates.alwaysTrue();
             searchTerm = search.trim();
         }
         
-        val idsByName = applicationFeatureRepository.getFeatureIdentifiersByName();
+        val idsByName = featureRepository.getFeatureIdentifiersByName();
         
         return idsByName.entrySet().stream()
         .filter(entry->searchRefine.test(entry.getValue()))
         .filter(entry->matches(entry.getKey(), entry.getValue(), searchTerm))
         .map(Map.Entry::getValue)
-        .map(AppFeat::new)
+        .map(ApplicationFeatureChoices.AppFeat::new)
         .collect(Collectors.toCollection(TreeSet::new));
     }
 
-    private boolean matches(String featureName, ApplicationFeatureId featureId, String search) {
+    private static boolean matches(String featureName, ApplicationFeatureId featureId, String search) {
         return featureName.contains(search);
     }
     
-    private boolean isNamespace(ApplicationFeatureId featureId) {
+    private static boolean isNamespace(ApplicationFeatureId featureId) {
         return featureId.getSort().isNamespace();
     }
     
-    private boolean isType(ApplicationFeatureId featureId) {
+    private static boolean isType(ApplicationFeatureId featureId) {
         return featureId.getSort().isType();
     }
     
-    private boolean isMember(ApplicationFeatureId featureId) {
+    private static boolean isMember(ApplicationFeatureId featureId) {
         return featureId.getSort().isMember();
     }
     
-    
     // -- FEATURE VIEW MODEL WRAPPING A VALUE TYPE 
 
     /**
@@ -224,5 +161,4 @@ public class ApplicationRole_addPermission {
         
     }
     
-    
 }
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationOrphanedPermissionManager_relocateSelected.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationOrphanedPermissionManager_relocateSelected.java
index e45289c..bf86949 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationOrphanedPermissionManager_relocateSelected.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/permission/ApplicationOrphanedPermissionManager_relocateSelected.java
@@ -19,6 +19,8 @@
 package org.apache.isis.extensions.secman.model.dom.permission;
 
 import java.util.Collection;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
@@ -27,6 +29,7 @@ import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Parameter;
 import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.appfeat.ApplicationFeature;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
@@ -58,7 +61,9 @@ public class ApplicationOrphanedPermissionManager_relocateSelected {
     }
 
     public Collection<String> choices1Act() {
-        return featureRepository.namespaceNames();
+        return featureRepository.allNamespaces().stream()
+                    .map(ApplicationFeature::getFullyQualifiedName)
+                    .collect(Collectors.toCollection(TreeSet::new));
     }
     
     private void relocate(
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addPermission.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addPermission.java
index 90a0d53..8170ace 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addPermission.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/role/ApplicationRole_addPermission.java
@@ -18,44 +18,28 @@
  */
 package org.apache.isis.extensions.secman.model.dom.role;
 
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeSet;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
 import javax.enterprise.inject.Model;
 import javax.inject.Inject;
 
-import org.apache.isis.applib.ViewModel;
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.MinLength;
-import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Parameter;
 import org.apache.isis.applib.annotation.ParameterLayout;
-import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.services.appfeat.ApplicationFeature;
-import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
-import org.apache.isis.commons.internal.functions._Predicates;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermission;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionMode;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRepository;
 import org.apache.isis.extensions.secman.api.permission.ApplicationPermissionRule;
 import org.apache.isis.extensions.secman.api.role.ApplicationRole;
 import org.apache.isis.extensions.secman.api.role.ApplicationRole.AddPermissionDomainEvent;
+import org.apache.isis.extensions.secman.model.dom.feature.ApplicationFeatureChoices;
 
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.Value;
-import lombok.val;
 import lombok.experimental.Accessors;
 
 @Action(
@@ -65,7 +49,7 @@ import lombok.experimental.Accessors;
 @RequiredArgsConstructor
 public class ApplicationRole_addPermission {
     
-    @Inject private ApplicationFeatureRepository applicationFeatureRepository;
+    @Inject private ApplicationFeatureRepository featureRepository;
     @Inject private ApplicationPermissionRepository<? extends ApplicationPermission> applicationPermissionRepository;
     
     private final ApplicationRole target;
@@ -74,7 +58,7 @@ public class ApplicationRole_addPermission {
     public static class Parameters {
         ApplicationPermissionRule rule; // ALLOW/VETO
         ApplicationPermissionMode mode; // r/w
-        AppFeat feature;
+        ApplicationFeatureChoices.AppFeat feature;
     }
 
     /**
@@ -95,10 +79,8 @@ public class ApplicationRole_addPermission {
             @Parameter(optionality = Optionality.MANDATORY)
             @ParameterLayout(
                     named = "Feature",
-                    describedAs = "To refine the search by sort (namespace, type, member), "
-                            + "use one of "
-                            + "sort:n sort:t sort:m.")
-            final AppFeat feature) {
+                    describedAs = ApplicationFeatureChoices.DESCRIBED_AS)
+            final ApplicationFeatureChoices.AppFeat feature) {
         
         applicationPermissionRepository.newPermission(target, rule, mode, feature.getFeatureId());
         return target;
@@ -115,114 +97,10 @@ public class ApplicationRole_addPermission {
     }
 
     @Model
-    public java.util.Collection<AppFeat> autoCompleteFeature(
+    public java.util.Collection<ApplicationFeatureChoices.AppFeat> autoCompleteFeature(
             final Parameters params,
             final @MinLength(3) String search) {
-        
-        final Predicate<ApplicationFeatureId> searchRefine; 
-        final String searchTerm;
-        
-        if(search.startsWith("sort:n")) {
-            searchRefine = this::isNamespace;
-            searchTerm = search.substring(6).trim();
-        } else if(search.startsWith("sort:t")) {
-            searchRefine = this::isType;
-            searchTerm = search.substring(6).trim();
-        } else if(search.startsWith("sort:m")) {
-            searchRefine = this::isMember;
-            searchTerm = search.substring(6).trim();
-        } else {
-            searchRefine = _Predicates.alwaysTrue();
-            searchTerm = search.trim();
-        }
-        
-        val idsByName = applicationFeatureRepository.getFeatureIdentifiersByName();
-        
-        return idsByName.entrySet().stream()
-        .filter(entry->searchRefine.test(entry.getValue()))
-        .filter(entry->matches(entry.getKey(), entry.getValue(), searchTerm))
-        .map(Map.Entry::getValue)
-        .map(AppFeat::new)
-        .collect(Collectors.toCollection(TreeSet::new));
-    }
-
-    private boolean matches(String featureName, ApplicationFeatureId featureId, String search) {
-        return featureName.contains(search);
+        return ApplicationFeatureChoices.autoCompleteFeature(featureRepository, search);
     }
     
-    private boolean isNamespace(ApplicationFeatureId featureId) {
-        return featureId.getSort().isNamespace();
-    }
-    
-    private boolean isType(ApplicationFeatureId featureId) {
-        return featureId.getSort().isType();
-    }
-    
-    private boolean isMember(ApplicationFeatureId featureId) {
-        return featureId.getSort().isMember();
-    }
-    
-    
-    // -- FEATURE VIEW MODEL WRAPPING A VALUE TYPE 
-
-    /**
-     * Viewmodel wrapper around value type {@link ApplicationFeatureId}. Introduced,
-     * because at the time of writing, 
-     * autoComplete/choices do not support value types.
-     */
-    @DomainObject(
-            nature = Nature.VIEW_MODEL, 
-            objectType = "isis.ext.secman.AppFeat")
-    @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode
-    public static class AppFeat 
-    implements 
-        Comparable<AppFeat>,
-        ViewModel {
-        
-        @Property
-        @Getter  
-        private ApplicationFeatureId featureId;
-        
-        public String title() {
-            return toString();
-        }
-        
-        @Override
-        public int compareTo(AppFeat o) {
-            val thisId = this.getFeatureId();
-            val otherId = o!=null ? o.getFeatureId() : null;
-            if(Objects.equals(thisId, otherId)) {
-                return 0;
-            }
-            if(thisId==null) {
-                return -1;
-            }
-            if(otherId==null) {
-                return 1;
-            }
-            return this.getFeatureId().compareTo(o.getFeatureId());
-        }
-        
-        @Override
-        public String toString() {
-            return featureId!=null 
-                    ? featureId.getSort().name() + ": " + featureId.getFullyQualifiedName()
-                    : "<no id>";
-        }
-
-        @Override
-        public String viewModelMemento() {
-            return featureId!=null 
-                    ? featureId.asEncodedString() 
-                    : "<no id>";
-        }
-
-        @Override
-        public void viewModelInit(String memento) {
-            featureId = ApplicationFeatureId.parseEncoded(memento); // fail by intention if memento is '<no id>'
-        }
-        
-    }
-    
-    
 }
diff --git a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUser_filterPermissions.java b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUser_filterPermissions.java
index 1f3e7e0..31bb905 100644
--- a/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUser_filterPermissions.java
+++ b/extensions/security/secman/model/src/main/java/org/apache/isis/extensions/secman/model/dom/user/ApplicationUser_filterPermissions.java
@@ -27,6 +27,7 @@ import javax.enterprise.inject.Model;
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.MinLength;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Parameter;
 import org.apache.isis.applib.annotation.ParameterLayout;
@@ -37,7 +38,8 @@ import org.apache.isis.applib.services.appfeat.ApplicationFeatureRepository;
 import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.extensions.secman.api.user.ApplicationUser;
-import org.apache.isis.extensions.secman.model.dom.feature.ApplicationFeatureConstants;
+import org.apache.isis.extensions.secman.model.dom.feature.ApplicationFeatureChoices;
+import org.apache.isis.extensions.secman.model.dom.role.ApplicationRole_addPermission.Parameters;
 
 import lombok.RequiredArgsConstructor;
 import lombok.val;
@@ -58,37 +60,32 @@ public class ApplicationUser_filterPermissions {
     @Model
     public List<UserPermissionViewModel> act(
             
-            @ParameterLayout(named="Namespace", typicalLength=ApplicationFeatureConstants.TYPICAL_LENGTH_NAMESPACE)
-            final String namespace,
-            
-            @Parameter(optionality = Optionality.OPTIONAL)
-            @ParameterLayout(named="Type", typicalLength=ApplicationFeatureConstants.TYPICAL_LENGTH_TYPE_SIMPLE_NAME)
-            final String logicalTypeSimpleName) {
+            @Parameter(optionality = Optionality.MANDATORY)
+            @ParameterLayout(
+                    named = "Feature",
+                    describedAs = ApplicationFeatureChoices.DESCRIBED_AS)
+            final ApplicationFeatureChoices.AppFeat feature) {
+        
+        val featureId = feature.getFeatureId();
+        
+        final String namespace = featureId.getNamespace();
+        final String typeSimpleName = featureId.getTypeSimpleName();
         
         val allMembers = featureRepository.allMembers();
-        val filtered = _Lists.filter(allMembers, within(namespace, logicalTypeSimpleName));
+        val filtered = _Lists.filter(allMembers, within(namespace, typeSimpleName));
         return asViewModels(filtered);
     }
 
-    /**
-     * Package names that have classes in them.
-     */
-    @Model
-    public Collection<String> choices0Act() {
-        return featureRepository.namespaceNames();
-    }
-
-
-    /**
-     * Class names for selected package.
-     */
     @Model
-    public Collection<String> choices1Act(final String namespace) {
-        return featureRepository.classNamesRecursivelyContainedIn(namespace);
+    public java.util.Collection<ApplicationFeatureChoices.AppFeat> autoComplete0Act(
+            final Parameters params,
+            final @MinLength(3) String search) {
+        return ApplicationFeatureChoices.autoCompleteFeature(featureRepository, search);
     }
+    
+    // -- HELPER XXX left over from refactoring, could be simplified ..
 
-
-    static Predicate<ApplicationFeature> within(final String namespace, final String logicalTypeSimpleName) {
+    private static Predicate<ApplicationFeature> within(final String namespace, final String logicalTypeSimpleName) {
         return (ApplicationFeature input) -> {
             final ApplicationFeatureId inputFeatureId = input.getFeatureId();
 
@@ -104,7 +101,7 @@ public class ApplicationUser_filterPermissions {
         };
     }
 
-    List<UserPermissionViewModel> asViewModels(final Collection<ApplicationFeature> features) {
+    private List<UserPermissionViewModel> asViewModels(final Collection<ApplicationFeature> features) {
         return _Lists.map(
                 features,
                 UserPermissionViewModel.Functions.asViewModel(target, factory));