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 020package org.apache.isis.core.progmodel.facets.param.choices.methodnum; 021 022import java.lang.reflect.Array; 023import java.lang.reflect.Method; 024import java.util.Collection; 025import java.util.List; 026 027import org.apache.isis.core.commons.lang.ListExtensions; 028import org.apache.isis.core.commons.lang.StringExtensions; 029import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 030import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware; 031import org.apache.isis.core.metamodel.exceptions.MetaModelException; 032import org.apache.isis.core.metamodel.facetapi.Facet; 033import org.apache.isis.core.metamodel.facetapi.FacetUtil; 034import org.apache.isis.core.metamodel.facetapi.FeatureType; 035import org.apache.isis.core.metamodel.facets.FacetedMethod; 036import org.apache.isis.core.metamodel.facets.FacetedMethodParameter; 037import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext; 038import org.apache.isis.core.metamodel.facets.actions.choices.ActionChoicesFacet; 039import org.apache.isis.core.metamodel.methodutils.MethodScope; 040import org.apache.isis.core.progmodel.facets.MethodFinderUtils; 041import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract; 042import org.apache.isis.core.progmodel.facets.MethodPrefixConstants; 043 044public class ActionParameterChoicesFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements AdapterManagerAware { 045 046 private static final String[] PREFIXES = {}; 047 048 private AdapterManager adapterManager; 049 050 /** 051 * Note that the {@link Facet}s registered are the generic ones from 052 * noa-architecture (where they exist) 053 */ 054 public ActionParameterChoicesFacetFactory() { 055 super(FeatureType.ACTIONS_ONLY, OrphanValidation.VALIDATE, 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(final ProcessMethodContext processMethodContext, final List<FacetedMethodParameter> parameters) { 073 074 if (parameters.isEmpty()) { 075 return; 076 } 077 078 final Method actionMethod = processMethodContext.getMethod(); 079 final Class<?>[] paramTypes = actionMethod.getParameterTypes(); 080 081 for (int i = 0; i < paramTypes.length; i++) { 082 083 final Class<?> arrayOfParamType = (Array.newInstance(paramTypes[i], 0)).getClass(); 084 085 final Method choicesMethod = findChoicesNumMethodReturning(processMethodContext, i); 086 if (choicesMethod == null) { 087 continue; 088 } 089 090 processMethodContext.removeMethod(choicesMethod); 091 092 final FacetedMethod facetedMethod = processMethodContext.getFacetHolder(); 093 if (facetedMethod.containsDoOpFacet(ActionChoicesFacet.class)) { 094 final Class<?> cls = processMethodContext.getCls(); 095 throw new MetaModelException(cls + " uses both old and new choices syntax - must use one or other"); 096 } 097 098 // add facets directly to parameters, not to actions 099 final FacetedMethodParameter paramAsHolder = parameters.get(i); 100 FacetUtil.addFacet(new ActionParameterChoicesFacetViaMethod(choicesMethod, arrayOfParamType, paramAsHolder, getSpecificationLoader(), getAdapterManager())); 101 } 102 } 103 104 /** 105 * search successively for the default method, trimming number of param types each loop 106 */ 107 private static Method findChoicesNumMethodReturning(final ProcessMethodContext processMethodContext, final int n) { 108 109 final Method actionMethod = processMethodContext.getMethod(); 110 final List<Class<?>> paramTypes = ListExtensions.mutableCopy(actionMethod.getParameterTypes()); 111 112 final Class<?> arrayOfParamType = (Array.newInstance(paramTypes.get(n), 0)).getClass(); 113 114 final int numParamTypes = paramTypes.size(); 115 116 for(int i=0; i< numParamTypes+1; i++) { 117 Method method; 118 119 method = findChoicesNumMethodReturning(processMethodContext, n, paramTypes.toArray(new Class<?>[]{}), arrayOfParamType); 120 if(method != null) { 121 return method; 122 } 123 method = findChoicesNumMethodReturning(processMethodContext, n, paramTypes.toArray(new Class<?>[]{}), Collection.class); 124 if(method != null) { 125 return method; 126 } 127 128 // remove last, and search again 129 if(!paramTypes.isEmpty()) { 130 paramTypes.remove(paramTypes.size()-1); 131 } 132 } 133 134 return null; 135 } 136 137 138 139 private static Method findChoicesNumMethodReturning(final ProcessMethodContext processMethodContext, final int n, Class<?>[] paramTypes, final Class<?> returnType) { 140 final Class<?> cls = processMethodContext.getCls(); 141 final Method actionMethod = processMethodContext.getMethod(); 142 final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName()); 143 final String name = MethodPrefixConstants.CHOICES_PREFIX + n + capitalizedName; 144 return MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, name, returnType, paramTypes); 145 } 146 147 // /////////////////////////////////////////////////////////////// 148 // Dependencies 149 // /////////////////////////////////////////////////////////////// 150 151 @Override 152 public void setAdapterManager(final AdapterManager adapterManager) { 153 this.adapterManager = adapterManager; 154 } 155 156 private AdapterManager getAdapterManager() { 157 return adapterManager; 158 } 159 160}