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/07/18 17:03:11 UTC

[isis] branch master updated: ISIS-2362: new prg.mod. for action validation ... validate(P params)

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 dfccf5f  ISIS-2362: new prg.mod. for action validation ... validate(P params)
dfccf5f is described below

commit dfccf5f453f4378b165cbdb67ac1deb3fcc6934b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sat Jul 18 19:02:52 2020 +0200

    ISIS-2362: new prg.mod. for action validation ... validate(P params)
    
    - in full support of
    https://cwiki.apache.org/confluence/display/ISIS/ActionParameters?focusedCommentId=158867933#comment-158867933
---
 .../isis/core/metamodel/facets/ActionSupport.java  | 257 +++++++++++++++++++++
 .../metamodel/facets/MethodLiteralConstants.java   |   4 +-
 .../method/ActionValidationFacetViaMethod.java     |  18 +-
 .../ActionValidationFacetViaMethodFactory.java     |  74 ++++--
 ...ActionParameterHiddenFacetViaMethodFactory.java |   2 +-
 .../InteractionNpmDemo_biArgDisabled.java          |  33 ++-
 6 files changed, 357 insertions(+), 31 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ActionSupport.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ActionSupport.java
new file mode 100644
index 0000000..2cd5f3a
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/ActionSupport.java
@@ -0,0 +1,257 @@
+/*
+ *  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.facets;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.core.commons.collections.Can;
+import org.apache.isis.core.commons.internal._Constants;
+import org.apache.isis.core.commons.internal.base._NullSafe;
+import org.apache.isis.core.commons.internal.collections._Arrays;
+import org.apache.isis.core.metamodel.facets.MethodFinderUtils.MethodAndPpmConstructor;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Value;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public final class ActionSupport {
+
+    @Value @Builder
+    public static class ActionSupportingMethodSearchRequest {
+
+        public static enum ReturnType {
+            TEXT,
+            BOOLEAN, 
+        }
+
+        @NonNull FacetFactory.ProcessMethodContext processMethodContext;
+        @NonNull Can<String> methodNames;
+        @NonNull EnumSet<SearchAlgorithm> searchAlgorithms; 
+        @NonNull ReturnType returnType;
+
+        Class<?> additionalParamType;
+
+        @Getter(lazy = true)
+        Class<?>[] paramTypes = getProcessMethodContext().getMethod().getParameterTypes();
+
+        Can<String> getSupporingMethodNameCandidates() {
+            return methodNames;
+        }
+    }
+
+    @FunctionalInterface
+    public static interface SearchFunction {
+        ActionSupportingMethodSearchResult search(
+                ActionSupportingMethodSearchRequest searchRequest);
+    }
+
+    @RequiredArgsConstructor
+    public static enum SearchAlgorithm
+    implements SearchFunction {
+        PPM(ActionSupport::findActionSupportingMethodWithPPMArg),
+        ALL_PARAM_TYPES(ActionSupport::findActionSupportingMethodWithAllParamTypes),
+        ;
+        private final SearchFunction searchFunction;
+        public ActionSupportingMethodSearchResult search(
+                final ActionSupportingMethodSearchRequest searchRequest) {
+            return searchFunction.search(searchRequest);
+        }
+    }
+
+    @Value(staticConstructor = "of")
+    public static class ActionSupportingMethodSearchResult {
+        Method supportingMethod;
+        Class<?> returnType;
+        Optional<Constructor<?>> ppmFactory;
+    }
+
+    public static void findActionSupportingMethods(
+            final ActionSupportingMethodSearchRequest searchRequest,
+            final Consumer<ActionSupportingMethodSearchResult> onMethodFound) {
+        
+        for (val searchAlgorithm : searchRequest.searchAlgorithms) { 
+
+            val searchResult = searchAlgorithm.search(searchRequest); 
+            
+            if(log.isDebugEnabled()) {
+                log.debug("search algorithm={} {}",
+                        searchAlgorithm.name(),
+                        searchResult != null ? "FOUND " : "",
+                        toString(searchRequest));
+            }
+
+            if (searchResult != null) {
+                onMethodFound.accept(searchResult);
+            }
+        }
+        
+    }
+
+    // -- SEARCH ALGORITHMS
+
+    private final static ActionSupportingMethodSearchResult findActionSupportingMethodWithPPMArg(
+            final ActionSupportingMethodSearchRequest searchRequest) {
+        
+        val processMethodContext = searchRequest.getProcessMethodContext();
+        val type = processMethodContext.getCls();
+        val paramTypes = searchRequest.getParamTypes();
+        val methodNames = searchRequest.getMethodNames();
+                
+        val additionalParamTypes = Can.ofNullable(searchRequest.getAdditionalParamType());
+        
+        final MethodAndPpmConstructor supportingMethodAndPpmConstructor;
+        
+        switch(searchRequest.getReturnType()) {
+        case BOOLEAN:
+            supportingMethodAndPpmConstructor = MethodFinder2
+                .findMethodWithPPMArg_returningBoolean(type, methodNames, paramTypes, additionalParamTypes);
+            break;
+        case TEXT:
+            supportingMethodAndPpmConstructor = MethodFinder2
+                .findMethodWithPPMArg_returningText(type, methodNames, paramTypes, additionalParamTypes);
+            break;
+        default:
+            supportingMethodAndPpmConstructor = null;
+        }
+        
+        if(log.isDebugEnabled()) {
+            
+            log.debug(". signature (<any>, {}) {}", 
+                    toString(additionalParamTypes.toArray(_Constants.emptyClasses)),
+                    supportingMethodAndPpmConstructor != null 
+                        ? "found -> " + supportingMethodAndPpmConstructor.getSupportingMethod() 
+                        : "");
+        }
+        
+        if(supportingMethodAndPpmConstructor != null) {
+            val searchResult = ActionSupportingMethodSearchResult
+                    .of( 
+                            supportingMethodAndPpmConstructor.getSupportingMethod(), 
+                            supportingMethodAndPpmConstructor.getSupportingMethod().getReturnType(),
+                            Optional.of(supportingMethodAndPpmConstructor.getPpmFactory()));
+            return searchResult;
+        }
+        
+        return null;
+    }
+
+    private final static ActionSupportingMethodSearchResult findActionSupportingMethodWithAllParamTypes(
+            final ActionSupportingMethodSearchRequest searchRequest) {
+        
+        val processMethodContext = searchRequest.getProcessMethodContext();
+        val type = processMethodContext.getCls();
+        val paramTypes = searchRequest.getParamTypes();
+        val methodNames = searchRequest.getMethodNames();
+        
+        val additionalParamType = searchRequest.getAdditionalParamType();
+        val additionalParamCount = additionalParamType!=null ? 1 : 0;
+        
+        final int paramsConsideredCount = paramTypes.length + additionalParamCount; 
+        if(paramsConsideredCount>=0) {
+        
+            val paramTypesToLookFor = concat(paramTypes, paramsConsideredCount, additionalParamType);
+            
+            final Method supportingMethod;
+            
+            switch(searchRequest.getReturnType()) {
+            case BOOLEAN:
+                supportingMethod = MethodFinder2
+                    .findMethod_returningBoolean(type, methodNames, paramTypesToLookFor);
+                break;
+            case TEXT:
+                supportingMethod = MethodFinder2
+                    .findMethod_returningText(type, methodNames, paramTypesToLookFor);
+                break;
+            default:
+                supportingMethod = null;
+            }
+            
+            if(log.isDebugEnabled()) {
+                log.debug(". signature ({}) {}", 
+                        toString(paramTypesToLookFor),
+                        supportingMethod != null ? "found -> " + supportingMethod : "");
+            }
+            
+            if(supportingMethod != null) {
+                val searchResult = ActionSupportingMethodSearchResult
+                        .of(supportingMethod, supportingMethod.getReturnType(), Optional.empty());
+                return searchResult;
+            }
+        }
+        
+        return null;
+    }
+    
+    // -- PARAM UTIL
+    
+    private static Class<?>[] concat(
+            final Class<?>[] paramTypes,
+            final int paramsConsidered,
+            @Nullable final Class<?> additionalParamType) {
+
+        if(paramsConsidered>paramTypes.length) {
+            val msg = String.format("paramsConsidered %d exceeds size of paramTypes %d", 
+                    paramsConsidered, paramTypes.length);
+            throw new IllegalArgumentException(msg);
+        }
+        
+        val paramTypesConsidered = paramsConsidered<paramTypes.length
+                ? Arrays.copyOf(paramTypes, paramsConsidered)
+                        : paramTypes;
+                
+        val withAdditional = additionalParamType!=null
+                ? _Arrays.combine(paramTypesConsidered, additionalParamType)
+                        : paramTypesConsidered;
+                
+        return withAdditional;
+    }
+    
+    // -- DEBUG LOGGING
+    
+    private static String toString(
+            ActionSupportingMethodSearchRequest searchRequest) {
+        
+        return String.format("%s.%s(%s) : %s",
+                searchRequest.getProcessMethodContext().getCls().getSimpleName(),
+                searchRequest.getSupporingMethodNameCandidates(),
+                toString(searchRequest.getParamTypes()),
+                searchRequest.getReturnType().name()
+                );
+    }
+    
+    private static String toString(Class<?>[] types) {
+        return _NullSafe.stream(types)
+                .map(Class::getSimpleName)
+                .collect(Collectors.joining(","));
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodLiteralConstants.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodLiteralConstants.java
index 3b80695..3b3400c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodLiteralConstants.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/MethodLiteralConstants.java
@@ -126,8 +126,8 @@ public final class MethodLiteralConstants {
             }
             @Override
             protected String getMemberSupportingMethodName(Member member, String prefix) {
-                // same as classic ???
-                return PREFIX_PARAMNUM_ACTION.getMemberSupportingMethodName(member, prefix);
+                // prefix only
+                return prefix;
             }
         }
         
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethod.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethod.java
index e466a9a..92cd5dd 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethod.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethod.java
@@ -19,10 +19,12 @@
 
 package org.apache.isis.core.metamodel.facets.actions.validate.method;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 import org.apache.isis.applib.services.i18n.TranslatableString;
 import org.apache.isis.applib.services.i18n.TranslationService;
@@ -38,12 +40,20 @@ public class ActionValidationFacetViaMethod extends ActionValidationFacetAbstrac
     private final Method method;
     private final TranslationService translationService;
     private final String translationContext;
+    private final Optional<Constructor<?>> ppmFactory;
 
-    public ActionValidationFacetViaMethod(final Method method, final TranslationService translationService, final String translationContext, final FacetHolder holder) {
+    public ActionValidationFacetViaMethod(
+            final Method method, 
+            final TranslationService translationService, 
+            final String translationContext, 
+            Optional<Constructor<?>> ppmFactory, 
+            final FacetHolder holder) {
+        
         super(holder);
         this.method = method;
         this.translationService = translationService;
         this.translationContext = translationContext;
+        this.ppmFactory = ppmFactory;
     }
 
     /**
@@ -62,7 +72,11 @@ public class ActionValidationFacetViaMethod extends ActionValidationFacetAbstrac
 
     @Override
     public String invalidReason(final ManagedObject owningAdapter, final Can<ManagedObject> proposedArgumentAdapters) {
-        final Object returnValue = ManagedObjects.InvokeUtil.invoke(method, owningAdapter, proposedArgumentAdapters);
+        
+        final Object returnValue = ppmFactory.isPresent()
+                ? ManagedObjects.InvokeUtil.invokeWithPPM(ppmFactory.get(), method, owningAdapter, proposedArgumentAdapters)
+                : ManagedObjects.InvokeUtil.invoke(method, owningAdapter, proposedArgumentAdapters);
+
         if(returnValue instanceof String) {
             return (String) returnValue;
         }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethodFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethodFactory.java
index 2483550..0ffef68 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethodFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/validate/method/ActionValidationFacetViaMethodFactory.java
@@ -19,15 +19,17 @@
 
 package org.apache.isis.core.metamodel.facets.actions.validate.method;
 
-import java.lang.reflect.Method;
+import java.util.EnumSet;
 
-import org.apache.isis.applib.services.i18n.TranslationService;
 import org.apache.isis.core.commons.collections.Can;
+import org.apache.isis.core.metamodel.exceptions.MetaModelException;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
-import org.apache.isis.core.metamodel.facets.MethodFinder2;
+import org.apache.isis.core.metamodel.facets.ActionSupport;
+import org.apache.isis.core.metamodel.facets.ActionSupport.SearchAlgorithm;
 import org.apache.isis.core.metamodel.facets.MethodLiteralConstants;
 import org.apache.isis.core.metamodel.facets.MethodPrefixBasedFacetFactoryAbstract;
 import org.apache.isis.core.metamodel.facets.actions.validate.ActionValidationFacet;
+import org.apache.isis.core.metamodel.facets.param.validate.method.ActionParameterValidationFacetViaMethod;
 
 import lombok.val;
 
@@ -50,29 +52,61 @@ extends MethodPrefixBasedFacetFactoryAbstract  {
 
     private void handleValidateAllArgsMethod(final ProcessMethodContext processMethodContext) {
 
-        val cls = processMethodContext.getCls();
+        //val cls = processMethodContext.getCls();
         val actionMethod = processMethodContext.getMethod();
         val facetHolder = processMethodContext.getFacetHolder();
 
-        val paramTypes = actionMethod.getParameterTypes();
+        //val paramTypes = actionMethod.getParameterTypes();
 
         val namingConvention = PREFIX_BASED_NAMING.map(naming->naming.providerForAction(actionMethod, PREFIX));
         
-        final Method validateMethod = MethodFinder2.findMethod_returningText(
-                cls,
-                namingConvention.map(x->x.get()),
-                paramTypes);
-        if (validateMethod == null) {
-            return;
-        }
-        processMethodContext.removeMethod(validateMethod);
-
-        final TranslationService translationService = getTranslationService();
-        // sadness: same as in TranslationFactory
-        final String translationContext = facetHolder.getIdentifier().toClassAndNameIdentityString();
-        final ActionValidationFacetViaMethod facet = 
-                new ActionValidationFacetViaMethod(validateMethod, translationService, translationContext, facetHolder);
-        super.addFacet(facet);
+        val searchRequest = ActionSupport.ActionSupportingMethodSearchRequest.builder()
+                .processMethodContext(processMethodContext)
+                .returnType(ActionSupport.ActionSupportingMethodSearchRequest.ReturnType.TEXT)
+                .methodNames(namingConvention.map(x->x.get()))
+                .searchAlgorithms(EnumSet.of(SearchAlgorithm.PPM, SearchAlgorithm.ALL_PARAM_TYPES))
+                .build();
+        
+        ActionSupport.findActionSupportingMethods(searchRequest, searchResult -> {
+            val validateMethod = searchResult.getSupportingMethod();
+            
+            processMethodContext.removeMethod(validateMethod);
+            
+            if (facetHolder.containsNonFallbackFacet(ActionValidationFacetViaMethod.class)) {
+                throw new MetaModelException( processMethodContext.getCls() + " uses both old and new 'validate' syntax - "
+                        + "must use one or other");
+            }
+
+            val ppmFactory = searchResult.getPpmFactory();
+            val translationService = getTranslationService();
+            val translationContext = facetHolder.getIdentifier().toClassAndNameIdentityString();
+            super.addFacet(
+                    new ActionValidationFacetViaMethod(
+                            validateMethod, translationService, translationContext, ppmFactory, facetHolder));
+        });
+        
+//        final Method validateMethod = MethodFinder2.findMethod_returningText(
+//                cls,
+//                namingConvention.map(x->x.get()),
+//                paramTypes);
+//        
+//        val validateMethodPpm = MethodFinder2.findMethodWithPPMArg_returningText(
+//                cls,
+//                namingConvention.map(x->x.get()),
+//                paramTypes,
+//                Can.empty());
+//        if (validateMethod == null) {
+//            return;
+//        }
+//        processMethodContext.removeMethod(validateMethod);
+//
+//        final TranslationService translationService = getTranslationService();
+//        final String translationContext = facetHolder.getIdentifier().toClassAndNameIdentityString();
+//        final ActionValidationFacetViaMethod facet = 
+//                new ActionValidationFacetViaMethod(validateMethod, translationService, translationContext, facetHolder);
+//        super.addFacet(facet);
+        
+
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/hide/method/ActionParameterHiddenFacetViaMethodFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/hide/method/ActionParameterHiddenFacetViaMethodFactory.java
index 07e6ec8..70b1d5d 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/hide/method/ActionParameterHiddenFacetViaMethodFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/param/hide/method/ActionParameterHiddenFacetViaMethodFactory.java
@@ -64,7 +64,7 @@ public class ActionParameterHiddenFacetViaMethodFactory extends MethodPrefixBase
                 .returnType(ReturnType.BOOLEAN)
                 .paramIndexToMethodNameProviders(namingConvention)
                 .searchAlgorithms(EnumSet.of(SearchAlgorithm.PPM, SearchAlgorithm.SWEEP))
-                .build();
+                .build(); 
         
         ParameterSupport.findParamSupportingMethods(searchRequest, searchResult -> {
             
diff --git a/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/model/interaction/InteractionNpmDemo_biArgDisabled.java b/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/model/interaction/InteractionNpmDemo_biArgDisabled.java
index c88792e..9f21acb 100644
--- a/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/model/interaction/InteractionNpmDemo_biArgDisabled.java
+++ b/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/model/interaction/InteractionNpmDemo_biArgDisabled.java
@@ -43,16 +43,37 @@ public class InteractionNpmDemo_biArgDisabled {
         return a + b;
     }
     
-    //TODO[ISIS-2362] supporting methods (no arg): rename -> disable()
     @Model
-    public String disableAct() {
+    public boolean hide() {
+        return false;
+    }
+    
+    @Model
+    public String disable() {
         return "Disabled for demonstration.";
     }
     
-    //TODO[ISIS-2362] supporting methods (no arg): rename -> validate()
-    //TODO[ISIS-2362] supporting methods (all args): rename -> validate(params)
-    //@Model //FIXME not recognized by method finders (metamodel)
-    public String validateAct(Parameters params) {
+    @Model
+    public String validate(Parameters params) {
         return "Never valid for demonstration.";
     }
+    
+    // -- PARAM SUPPORTING METHODS 
+    
+    // testing whether all of these get picked up by the meta-model
+    
+    @Model public boolean hideA(Parameters params) { return false; }         
+    @Model public String disableA(Parameters params) { return null; }                           
+    @Model public String validateA(Parameters params) { return null; }
+    @Model public int[] choicesA(Parameters params) { return null; }          
+    @Model public int[] autoCompleteA(Parameters params, String search) { return null; }
+    @Model public int defaultA(Parameters params) { return 0; }
+    
+    @Model public boolean hideB(Parameters params) { return false; }         
+    @Model public String disableB(Parameters params) { return null; }                           
+    @Model public String validateB(Parameters params) { return null; }
+    @Model public int[] choicesB(Parameters params) { return null; }          
+    @Model public int[] autoCompleteB(Parameters params, String search) { return null; }
+    @Model public int defaultB(Parameters params) { return 0; }
+    
 }