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.metamodel.adapter.util;
021
022import java.lang.reflect.Method;
023import java.util.List;
024import java.util.Map;
025
026import com.google.common.collect.Lists;
027
028import org.apache.isis.core.commons.lang.ClassExtensions;
029import org.apache.isis.core.commons.lang.ListExtensions;
030import org.apache.isis.core.commons.lang.MethodExtensions;
031import org.apache.isis.core.commons.lang.MethodUtil;
032import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
033import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
034
035public final class AdapterInvokeUtils {
036
037    private AdapterInvokeUtils() {
038    }
039
040    public static void invokeAll(final List<Method> methods, final ObjectAdapter adapter) {
041        MethodUtil.invoke(methods, AdapterUtils.unwrap(adapter));
042    }
043
044    public static Object invoke(final Method method, final ObjectAdapter adapter) {
045        return MethodExtensions.invoke(method, AdapterUtils.unwrap(adapter));
046    }
047
048    public static Object invoke(final Method method, final ObjectAdapter adapter, final Object arg0) {
049        return MethodExtensions.invoke(method, AdapterUtils.unwrap(adapter), new Object[] {arg0});
050    }
051
052    public static Object invoke(final Method method, final ObjectAdapter adapter, final ObjectAdapter arg0Adapter) {
053        return invoke(method, adapter, AdapterUtils.unwrap(arg0Adapter));
054    }
055
056    public static Object invoke(final Method method, final ObjectAdapter adapter, final ObjectAdapter[] argumentAdapters) {
057        return MethodExtensions.invoke(method, AdapterUtils.unwrap(adapter), AdapterUtils.unwrap(argumentAdapters));
058    }
059    
060    public static Object invoke(final Method method, final ObjectAdapter adapter, final Map<Integer, ObjectAdapter> argumentAdapters) {
061        return invoke(method, adapter, asArray(argumentAdapters, method.getParameterTypes().length));
062    }
063
064    private static ObjectAdapter[] asArray(Map<Integer, ObjectAdapter> argumentAdapters, int length) {
065        ObjectAdapter[] args = new ObjectAdapter[length];
066        for (final Map.Entry<Integer, ObjectAdapter> entry : argumentAdapters.entrySet()) {
067            final Integer paramNum = entry.getKey();
068            if(paramNum < length) {
069                args[paramNum] = entry.getValue();
070            }
071        }
072        return args;
073    }
074
075    /**
076     * Invokes the method, adjusting arguments as required to make them fit the method's parameters.
077     * 
078     * <p>
079     * That is:
080     * <ul>
081     * <li>if the method declares parameters but arguments are missing, then will provide 'null' defaults for these.
082     * <li>if the method does not declare all parameters for arguments, then truncates arguments.
083     * </ul>
084     */
085    public static Object invokeAutofit(final Method method, final ObjectAdapter target, List<ObjectAdapter> argumentsIfAvailable, final AdapterManager adapterManager) {
086        final List<ObjectAdapter> args = Lists.newArrayList();
087        if(argumentsIfAvailable != null) {
088            args.addAll(argumentsIfAvailable);
089        }
090        
091        adjust(method, args, adapterManager);
092
093        final ObjectAdapter[] argArray = args.toArray(new ObjectAdapter[]{});
094        return AdapterInvokeUtils.invoke(method, target, argArray);
095    }
096
097    private static void adjust(final Method method, final List<ObjectAdapter> args, final AdapterManager adapterManager) {
098        final Class<?>[] parameterTypes = method.getParameterTypes();
099        ListExtensions.adjust(args, parameterTypes.length);
100        
101        for(int i=0; i<parameterTypes.length; i++) {
102            final Class<?> cls = parameterTypes[i];
103            if(args.get(i) == null && cls.isPrimitive()) {
104                final Object object = ClassExtensions.toDefault(cls);
105                final ObjectAdapter adapter = adapterManager.adapterFor(object);
106                args.set(i, adapter);
107            }
108        }
109    }
110
111    /**
112     * Invokes the method, adjusting arguments as required.
113     * 
114     * <p>
115     * That is:
116     * <ul>
117     * <li>if the method declares parameters but no arguments are provided, then will provide 'null' defaults for these.
118     * <li>if the method does not declare parameters but arguments were provided, then will ignore those argumens.
119     * </ul>
120     */
121    @SuppressWarnings("unused")
122    private static Object invokeWithDefaults(final Method method, final ObjectAdapter adapter, final ObjectAdapter[] argumentAdapters) {
123        final int numParams = method.getParameterTypes().length;
124        ObjectAdapter[] adapters;
125        
126        if(argumentAdapters == null || argumentAdapters.length == 0) {
127            adapters = new ObjectAdapter[numParams];
128        } else if(numParams == 0) {
129            // ignore any arguments, even if they were supplied.
130            // eg used by contributee actions, but 
131            // underlying service 'default' action declares no params 
132            adapters = new ObjectAdapter[0];
133        } else if(argumentAdapters.length == numParams){
134            adapters = argumentAdapters;
135        } else {
136            throw new IllegalArgumentException("Method has " + numParams + " params but " + argumentAdapters.length + " arguments provided");
137        }
138
139        return AdapterInvokeUtils.invoke(method, adapter, adapters);
140    }
141
142}