001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.isis.core.progmodel.facets.param.mandatory.staticmethod;
021
022 import java.lang.reflect.Method;
023 import java.util.List;
024
025 import org.apache.isis.core.commons.lang.NameUtils;
026 import org.apache.isis.core.metamodel.adapter.util.InvokeUtils;
027 import org.apache.isis.core.metamodel.exceptions.MetaModelException;
028 import org.apache.isis.core.metamodel.facetapi.Facet;
029 import org.apache.isis.core.metamodel.facetapi.FacetUtil;
030 import org.apache.isis.core.metamodel.facetapi.FeatureType;
031 import org.apache.isis.core.metamodel.facets.FacetedMethod;
032 import org.apache.isis.core.metamodel.facets.FacetedMethodParameter;
033 import org.apache.isis.core.metamodel.methodutils.MethodScope;
034 import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
035 import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
036 import org.apache.isis.core.progmodel.facets.MethodPrefixConstants;
037
038 /**
039 * Sets up all the {@link Facet}s for an action in a single shot.
040 */
041 public class ActionParameterOptionalViaMethodFacetFactory extends MethodPrefixBasedFacetFactoryAbstract {
042
043 private static final String[] PREFIXES = { MethodPrefixConstants.OPTIONAL_PREFIX };
044
045 /**
046 * Note that the {@link Facet}s registered are the generic ones from noa-architecture (where they exist)
047 */
048 public ActionParameterOptionalViaMethodFacetFactory() {
049 super(FeatureType.ACTIONS_ONLY, PREFIXES);
050 }
051
052 @Override
053 public void process(final ProcessMethodContext processMethodContext) {
054
055 final FacetedMethod facetedMethod = processMethodContext.getFacetHolder();
056 final List<FacetedMethodParameter> holderList = facetedMethod.getParameters();
057
058 attachMandatoryFacetForParametersIfOptionalMethodIsFound(processMethodContext, holderList);
059
060 }
061
062 private static void attachMandatoryFacetForParametersIfOptionalMethodIsFound(
063 final ProcessMethodContext processMethodContext, final List<FacetedMethodParameter> parameters) {
064
065 if (parameters.isEmpty()) {
066 return;
067 }
068
069 final Method actionMethod = processMethodContext.getMethod();
070 final String capitalizedName = NameUtils.capitalizeName(actionMethod.getName());
071
072 final Class<?> cls = processMethodContext.getCls();
073 final Method optionalMethod =
074 MethodFinderUtils.findMethod(cls, MethodScope.CLASS, MethodPrefixConstants.OPTIONAL_PREFIX
075 + capitalizedName, boolean[].class, new Class[0]);
076 if (optionalMethod == null) {
077 return;
078 }
079 try {
080 final boolean[] optionals = invokeOptionalsMethod(optionalMethod, parameters.size());
081
082 for (int i = 0; i < optionals.length; i++) {
083 if (optionals[i]) {
084 // add facets directly to parameters, not to actions
085 FacetUtil.addFacet(new MandatoryFacetOptionalViaMethodForParameter(parameters.get(i)));
086 }
087 }
088 } finally {
089 processMethodContext.removeMethod(optionalMethod);
090 }
091 }
092
093 private static boolean[] invokeOptionalsMethod(final Method optionalMethod, final int numElementsRequired) {
094 boolean[] optionals = null;
095 try {
096 optionals = (boolean[]) InvokeUtils.invokeStatic(optionalMethod, new Object[0]);
097 } catch (final ClassCastException ex) {
098 // ignore, test below
099 }
100 if (optionals == null || optionals.length != numElementsRequired) {
101 throw new MetaModelException(optionalMethod
102 + " must return an boolean[] array of same size as number of parameters of action");
103 }
104 return optionals;
105 }
106
107 }