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.defaults.methodnum;
021
022import java.lang.reflect.Method;
023import java.util.List;
024
025import org.apache.isis.core.commons.lang.ListExtensions;
026import org.apache.isis.core.commons.lang.StringExtensions;
027import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
028import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware;
029import org.apache.isis.core.metamodel.exceptions.MetaModelException;
030import org.apache.isis.core.metamodel.facetapi.Facet;
031import org.apache.isis.core.metamodel.facetapi.FacetUtil;
032import org.apache.isis.core.metamodel.facetapi.FeatureType;
033import org.apache.isis.core.metamodel.facets.FacetedMethod;
034import org.apache.isis.core.metamodel.facets.FacetedMethodParameter;
035import org.apache.isis.core.metamodel.facets.actions.defaults.ActionDefaultsFacet;
036import org.apache.isis.core.metamodel.methodutils.MethodScope;
037import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
038import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
039import 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 */
044public class ActionParameterDefaultsFacetFactory 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 ActionParameterDefaultsFacetFactory() {
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        attachDefaultFacetForParametersIfDefaultsNumMethodIsFound(processMethodContext, holderList);
069    }
070
071    private void attachDefaultFacetForParametersIfDefaultsNumMethodIsFound(final ProcessMethodContext processMethodContext, final List<FacetedMethodParameter> parameters) {
072
073        if (parameters.isEmpty()) {
074            return;
075        }
076
077        final Method actionMethod = processMethodContext.getMethod();
078        final Class<?>[] paramTypes = actionMethod.getParameterTypes();
079
080        for (int i = 0; i < paramTypes.length; i++) {
081
082            // attempt to match method...
083            Method defaultMethod = findDefaultNumMethod(processMethodContext, i);
084            if (defaultMethod == null) {
085                continue;
086            }
087            
088            processMethodContext.removeMethod(defaultMethod);
089
090            final FacetedMethod facetedMethod = processMethodContext.getFacetHolder();
091            if (facetedMethod.containsDoOpFacet(ActionDefaultsFacet.class)) {
092                final Class<?> cls2 = processMethodContext.getCls();
093                throw new MetaModelException(cls2 + " uses both old and new default syntax for " + actionMethod.getName() + "(...) - must use one or other");
094            }
095
096            // add facets directly to parameters, not to actions
097            final FacetedMethodParameter paramAsHolder = parameters.get(i);
098            FacetUtil.addFacet(new ActionParameterDefaultsFacetViaMethod(defaultMethod, paramAsHolder, getAdapterManager()));
099        }
100    }
101
102    /**
103     * search successively for the default method, trimming number of param types each loop
104     */
105    private static Method findDefaultNumMethod(ProcessMethodContext processMethodContext, int n) {
106        
107        final Method actionMethod = processMethodContext.getMethod();
108        final List<Class<?>> paramTypes = ListExtensions.mutableCopy(actionMethod.getParameterTypes());
109        
110        final int numParamTypes = paramTypes.size();
111        
112        for(int i=0; i< numParamTypes+1; i++) {
113            final Method method = findDefaultNumMethod(processMethodContext, n, paramTypes.toArray(new Class<?>[]{}));
114            if(method != null) {
115                return method;
116            }
117            // remove last, and search again
118            if(!paramTypes.isEmpty()) {
119                paramTypes.remove(paramTypes.size()-1);
120            }
121        }
122
123        return null;
124    }
125
126    private static Method findDefaultNumMethod(final ProcessMethodContext processMethodContext, int n, Class<?>[] paramTypes) {
127        final Class<?> cls = processMethodContext.getCls();
128        final Method actionMethod = processMethodContext.getMethod();
129        final Class<?> returnType = actionMethod.getParameterTypes()[n];
130        final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName());
131        return MethodFinderUtils.findMethod(cls, MethodScope.OBJECT, MethodPrefixConstants.DEFAULT_PREFIX + n + capitalizedName, returnType, paramTypes);
132    }
133
134    // ///////////////////////////////////////////////////////////////
135    // Dependencies
136    // ///////////////////////////////////////////////////////////////
137
138    @Override
139    public void setAdapterManager(final AdapterManager adapterManager) {
140        this.adapterManager = adapterManager;
141    }
142
143    private AdapterManager getAdapterManager() {
144        return adapterManager;
145    }
146
147
148}