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.choices.methodnum;
021
022 import java.lang.reflect.Array;
023 import java.lang.reflect.Method;
024 import java.util.List;
025
026 import org.apache.isis.core.commons.lang.NameUtils;
027 import org.apache.isis.core.metamodel.adapter.map.AdapterMap;
028 import org.apache.isis.core.metamodel.adapter.map.AdapterMapAware;
029 import org.apache.isis.core.metamodel.exceptions.MetaModelException;
030 import org.apache.isis.core.metamodel.facetapi.Facet;
031 import org.apache.isis.core.metamodel.facetapi.FacetUtil;
032 import org.apache.isis.core.metamodel.facetapi.FeatureType;
033 import org.apache.isis.core.metamodel.facets.FacetedMethod;
034 import org.apache.isis.core.metamodel.facets.FacetedMethodParameter;
035 import org.apache.isis.core.metamodel.facets.actions.choices.ActionChoicesFacet;
036 import org.apache.isis.core.metamodel.methodutils.MethodScope;
037 import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
038 import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
039 import org.apache.isis.core.progmodel.facets.MethodPrefixConstants;
040
041 /**
042 * Sets up all the {@link Facet}s for an action in a single shot.
043 */
044 public class ActionParameterChoicesFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements
045 AdapterMapAware {
046
047 private static final String[] PREFIXES = {};
048
049 private AdapterMap adapterMap;
050
051 /**
052 * Note that the {@link Facet}s registered are the generic ones from noa-architecture (where they exist)
053 */
054 public ActionParameterChoicesFacetFactory() {
055 super(FeatureType.ACTIONS_ONLY, PREFIXES);
056 }
057
058 // ///////////////////////////////////////////////////////
059 // Actions
060 // ///////////////////////////////////////////////////////
061
062 @Override
063 public void process(final ProcessMethodContext processMethodContext) {
064
065 final FacetedMethod facetedMethod = processMethodContext.getFacetHolder();
066 final List<FacetedMethodParameter> holderList = facetedMethod.getParameters();
067
068 attachChoicesFacetForParametersIfChoicesNumMethodIsFound(processMethodContext, holderList);
069
070 }
071
072 private void attachChoicesFacetForParametersIfChoicesNumMethodIsFound(
073 final ProcessMethodContext processMethodContext, final List<FacetedMethodParameter> parameters) {
074
075 if (parameters.isEmpty()) {
076 return;
077 }
078
079 final Method actionMethod = processMethodContext.getMethod();
080 final Class<?>[] params = actionMethod.getParameterTypes();
081
082 for (int i = 0; i < params.length; i++) {
083
084 final Class<?> arrayOfParamType = (Array.newInstance(params[i], 0)).getClass();
085
086 Method choicesMethod = findChoicesNumMethodReturning(processMethodContext, i, arrayOfParamType);
087 if (choicesMethod == null) {
088 choicesMethod = findChoicesNumMethodReturning(processMethodContext, i, List.class);
089 }
090 if (choicesMethod == null) {
091 continue;
092 }
093 processMethodContext.removeMethod(choicesMethod);
094
095 final FacetedMethod facetedMethod = processMethodContext.getFacetHolder();
096 if (facetedMethod.containsDoOpFacet(ActionChoicesFacet.class)) {
097 final Class<?> cls = processMethodContext.getCls();
098 throw new MetaModelException(cls + " uses both old and new choices syntax - must use one or other");
099 }
100
101 // add facets directly to parameters, not to actions
102 final FacetedMethodParameter paramAsHolder = parameters.get(i);
103 FacetUtil.addFacet(new ActionParameterChoicesFacetViaMethod(choicesMethod, arrayOfParamType, paramAsHolder,
104 getSpecificationLookup(), getAdapterMap()));
105 }
106 }
107
108 private Method findChoicesNumMethodReturning(final ProcessMethodContext processMethodContext, final int i,
109 final Class<?> arrayOfParamType) {
110
111 final Class<?> cls = processMethodContext.getCls();
112 final Method actionMethod = processMethodContext.getMethod();
113 final String capitalizedName = NameUtils.capitalizeName(actionMethod.getName());
114 final String name = MethodPrefixConstants.CHOICES_PREFIX + i + capitalizedName;
115 return MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, name, arrayOfParamType, new Class[0]);
116 }
117
118 // ///////////////////////////////////////////////////////////////
119 // Dependencies
120 // ///////////////////////////////////////////////////////////////
121
122 @Override
123 public void setAdapterMap(final AdapterMap adapterMap) {
124 this.adapterMap = adapterMap;
125 }
126
127 private AdapterMap getAdapterMap() {
128 return adapterMap;
129 }
130
131 }