All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nakedobjects.metamodel.facets.actions.ActionMethodsFacetFactory Maven / Gradle / Ivy

The newest version!
package org.nakedobjects.metamodel.facets.actions;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.nakedobjects.applib.security.UserMemento;
import org.nakedobjects.metamodel.commons.names.NameConvertorUtils;
import org.nakedobjects.metamodel.commons.names.NameUtils;
import org.nakedobjects.metamodel.exceptions.ReflectionException;
import org.nakedobjects.metamodel.facets.Facet;
import org.nakedobjects.metamodel.facets.FacetHolder;
import org.nakedobjects.metamodel.facets.FacetUtil;
import org.nakedobjects.metamodel.facets.MethodRemover;
import org.nakedobjects.metamodel.facets.actions.choices.ActionChoicesFacetViaMethod;
import org.nakedobjects.metamodel.facets.actions.choices.ActionParameterChoicesFacetViaMethod;
import org.nakedobjects.metamodel.facets.actions.defaults.ActionDefaultsFacetViaMethod;
import org.nakedobjects.metamodel.facets.actions.defaults.ActionParameterDefaultsFacetViaMethod;
import org.nakedobjects.metamodel.facets.actions.executed.ExecutedFacet.Where;
import org.nakedobjects.metamodel.facets.actions.invoke.ActionInvocationFacetViaMethod;
import org.nakedobjects.metamodel.facets.actions.validate.ActionValidationFacetViaMethod;
import org.nakedobjects.metamodel.facets.naming.named.NamedFacetInferred;
import org.nakedobjects.metamodel.java5.MethodPrefixBasedFacetFactoryAbstract;
import org.nakedobjects.metamodel.runtimecontext.RuntimeContext;
import org.nakedobjects.metamodel.runtimecontext.RuntimeContextAware;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.feature.NakedObjectFeatureType;
import org.nakedobjects.metamodel.specloader.internal.peer.JavaNakedObjectActionPeer;
import org.nakedobjects.metamodel.specloader.internal.peer.NakedObjectActionParamPeer;
import org.nakedobjects.metamodel.util.InvokeUtils;


/**
 * Sets up all the {@link Facet}s for an action in a single shot.
 * 
 * 

* TODO: should be more fine-grained? */ public class ActionMethodsFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements RuntimeContextAware { private static final String EXPLORATION_PREFIX = "Exploration"; private static final String DEBUG_PREFIX = "Debug"; private static final String REMOTE_PREFIX = "Remote"; private static final String LOCAL_PREFIX = "Local"; private static final String PARAMETER_NAMES_PREFIX = "names"; private static final String PARAMETER_DESCRIPTIONS_PREFIX = "descriptions"; private static final String PARAMETER_OPTIONAL_PREFIX = "optional"; private static final String PARAMETER_DEFAULTS_PREFIX = "default"; private static final String PARAMETER_CHOICES_PREFIX = "choices"; private static final String[] PREFIXES = { EXPLORATION_PREFIX, DEBUG_PREFIX, REMOTE_PREFIX, LOCAL_PREFIX, PARAMETER_NAMES_PREFIX, PARAMETER_DESCRIPTIONS_PREFIX, PARAMETER_OPTIONAL_PREFIX, PARAMETER_DEFAULTS_PREFIX, PARAMETER_CHOICES_PREFIX, }; private RuntimeContext runtimeContext; /** * Note that the {@link Facet}s registered are the generic ones from noa-architecture (where they exist) */ public ActionMethodsFacetFactory() { super(PREFIXES, NakedObjectFeatureType.ACTIONS_ONLY); } // /////////////////////////////////////////////////////// // Actions // /////////////////////////////////////////////////////// @Override public boolean process(final Method actionMethod, final MethodRemover methodRemover, final FacetHolder action) { final String capitalizedName = NameUtils.capitalizeName(actionMethod.getName()); final Class type = actionMethod.getDeclaringClass(); final Class returnType = actionMethod.getReturnType(); final Class[] paramTypes = actionMethod.getParameterTypes(); final List facets = new ArrayList(); final NakedObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(type); final NakedObjectSpecification returnSpec = getSpecificationLoader().loadSpecification(returnType.getName()); if (returnSpec != null) { facets.add(new ActionInvocationFacetViaMethod(actionMethod, typeSpec, returnSpec, action, getRuntimeContext())); checkForDebugPrefix(facets, capitalizedName, action); checkForExplorationPrefix(facets, capitalizedName, action); checkForExecutionLocationPrefix(facets, capitalizedName, action); } removeMethod(methodRemover, actionMethod); final boolean forClass = (actionMethod.getModifiers() & Modifier.STATIC) > 0; findAndRemoveValidMethod(facets, methodRemover, type, forClass, capitalizedName, returnType, paramTypes, action); boolean oldChoicesOrDefaultsMethodsUsed = findAndRemoveParametersDefaultsMethod(facets, methodRemover, type, forClass, capitalizedName, returnType, paramTypes, action); oldChoicesOrDefaultsMethodsUsed = findAndRemoveParametersChoicesMethod(facets, methodRemover, type, forClass, capitalizedName, returnType, paramTypes, action) || oldChoicesOrDefaultsMethodsUsed; defaultNamedFacet(facets, methodRemover, capitalizedName, action); // must be called after the checkForXxxPrefix methods findAndRemoveNameMethod(facets, methodRemover, type, capitalizedName, new Class[] {}, action); findAndRemoveDescriptionMethod(facets, methodRemover, type, capitalizedName, new Class[] {}, action); findAndRemoveAlwaysHideMethod(facets, methodRemover, type, capitalizedName, paramTypes, action); findAndRemoveProtectMethod(facets, methodRemover, type, capitalizedName, paramTypes, action); findAndRemoveHideForSessionMethod(facets, methodRemover, type, capitalizedName, UserMemento.class, action); findAndRemoveDisableForSessionMethod(facets, methodRemover, type, capitalizedName, UserMemento.class, action); findAndRemoveHideMethod(facets, methodRemover, type, forClass, capitalizedName, paramTypes, action); findAndRemoveDisableMethod(facets, methodRemover, type, forClass, capitalizedName, paramTypes, action); if (action instanceof JavaNakedObjectActionPeer) { final JavaNakedObjectActionPeer javaNakedObjectActionPeer = (JavaNakedObjectActionPeer) action; // process the action's parameters names, descriptions and optional // an alternative design would be to have another facet factory processing just ACTION_PARAMETER, // and have it remove these // supporting methods. However, the FacetFactory API doesn't allow for methods of the class to be // removed while processing // action parameters, only while processing Methods (ie actions) final NakedObjectActionParamPeer[] actionParameters = javaNakedObjectActionPeer.getParameters(); findAndRemoveOptionalForActionParametersMethod(methodRemover, type, capitalizedName, returnType, paramTypes, actionParameters); findAndRemoveNamesForActionParametersMethod(methodRemover, type, capitalizedName, returnType, paramTypes, actionParameters); findAndRemoveDescriptionsforActionParametersMethod(methodRemover, type, capitalizedName, returnType, paramTypes, actionParameters); findAndRemoveChoicesForActionParametersMethod(oldChoicesOrDefaultsMethodsUsed, methodRemover, type, capitalizedName, paramTypes, actionParameters); findAndRemoveDefaultForActionParametersMethod(oldChoicesOrDefaultsMethodsUsed, methodRemover, type, capitalizedName, paramTypes, actionParameters); } return FacetUtil.addFacets(facets); } private void checkForExecutionLocationPrefix(final List actionFacets, final String capitalizedName, final FacetHolder action) { if (capitalizedName.startsWith(LOCAL_PREFIX)) { actionFacets.add(new ExecutedFacetViaNamingConvention(Where.LOCALLY, action)); } else if (capitalizedName.startsWith(REMOTE_PREFIX)) { actionFacets.add(new ExecutedFacetViaNamingConvention(Where.REMOTELY, action)); } } private void checkForDebugPrefix(final List actionFacets, final String capitalizedName, final FacetHolder action) { if (capitalizedName.startsWith(DEBUG_PREFIX)) { actionFacets.add(new DebugFacetViaNamingConvention(action)); } } private void checkForExplorationPrefix(final List facets, final String capitalizedName, final FacetHolder action) { if (capitalizedName.startsWith(EXPLORATION_PREFIX)) { facets.add(new ExplorationFacetViaNamingConvention(action)); } } /** * Must be called after the checkForXxxPrefix methods. */ private void defaultNamedFacet( final List actionFacets, final MethodRemover methodRemover, final String capitalizedName, final FacetHolder action) { String name = removePrefix(capitalizedName, LOCAL_PREFIX); name = removePrefix(name, REMOTE_PREFIX); name = removePrefix(name, DEBUG_PREFIX); name = removePrefix(name, EXPLORATION_PREFIX); name = removePrefix(name, LOCAL_PREFIX); name = removePrefix(name, REMOTE_PREFIX); name = NameConvertorUtils.naturalName(name); actionFacets.add(new NamedFacetInferred(name, action)); } private void findAndRemoveValidMethod( final List actionFacets, final MethodRemover methodRemover, final Class cls, final boolean onClass, final String capitalizedName, final Class returnType, final Class[] params, final FacetHolder action) { final Method method = findMethod(cls, onClass, VALIDATE_PREFIX + capitalizedName, String.class, params); if (method == null) { return; } removeMethod(methodRemover, method); actionFacets.add(new ActionValidationFacetViaMethod(method, action)); } private boolean findAndRemoveParametersDefaultsMethod( final List actionFacets, final MethodRemover methodRemover, final Class cls, final boolean onClass, final String capitalizedName, final Class returnType, final Class[] params, final FacetHolder action) { if (params.length == 0) { return false; } Method method = null; String name = PARAMETER_DEFAULTS_PREFIX + capitalizedName; if (allParametersOfSameType(params)) { final Object array = Array.newInstance(params[0], 0); final Class classes = array.getClass(); method = findMethodWithOrWithoutParameters(cls, onClass, name, classes, params); removeMethod(methodRemover, method); } if (method == null) { method = findMethodWithOrWithoutParameters(cls, onClass, name, Object[].class, params); removeMethod(methodRemover, method); } if (method == null) { method = findMethodWithOrWithoutParameters(cls, onClass, name, List.class, params); removeMethod(methodRemover, method); } if (method == null) { return false; } actionFacets.add(new ActionDefaultsFacetViaMethod(method, action)); return true; } private boolean findAndRemoveParametersChoicesMethod( final List actionFacets, final MethodRemover methodRemover, final Class cls, final boolean onClass, final String capitalizedName, final Class returnType, final Class[] params, final FacetHolder action) { if (params.length <= 0) { return false; } Method method = null; String name = PARAMETER_CHOICES_PREFIX + capitalizedName; if (allParametersOfSameType(params)) { final Object array = Array.newInstance(params[0], new int[] { 0, 0 }); final Class classes = array.getClass(); method = findMethodWithOrWithoutParameters(cls, onClass, name, classes, params); removeMethod(methodRemover, method); } if (method == null) { method = findMethodWithOrWithoutParameters(cls, onClass, name, Object[].class, params); removeMethod(methodRemover, method); } if (method == null) { method = findMethodWithOrWithoutParameters(cls, onClass, name, List.class, params); removeMethod(methodRemover, method); } if (method == null) { return false; } actionFacets.add(new ActionChoicesFacetViaMethod(method, returnType, action, getSpecificationLoader(), getRuntimeContext())); return true; } private void findAndRemoveOptionalForActionParametersMethod( final MethodRemover methodRemover, final Class cls, final String capitalizedName, final Class returnType, final Class[] params, final FacetHolder[] parameters) { if (params.length == 0) { return; } final Method method = findMethodWithOrWithoutParameters(cls, CLASS, PARAMETER_OPTIONAL_PREFIX + capitalizedName, boolean[].class, params); if (method == null) { return; } removeMethod(methodRemover, method); final Object[] parameterObjects = new Object[method.getParameterTypes().length]; final boolean[] names = (boolean[]) InvokeUtils.invokeStatic(method, parameterObjects); for (int i = 0; i < names.length; i++) { if (names[i]) { // add facets directly to parameters, not to actions FacetUtil.addFacet(new MandatoryFacetOverriddenByMethod(parameters[i])); } } } private void findAndRemoveNamesForActionParametersMethod( final MethodRemover methodRemover, final Class cls, final String capitalizedName, final Class returnType, final Class[] params, final FacetHolder[] parameters) { Method method = findMethodWithOrWithoutParameters(cls, CLASS, PARAMETER_NAMES_PREFIX + capitalizedName, String[].class, params); if (method == null) { return; } removeMethod(methodRemover, method); final Object[] parameterObjects = new Object[method.getParameterTypes().length]; final String[] names = (String[]) InvokeUtils.invokeStatic(method, parameterObjects); if (names.length != parameters.length) { throw new ReflectionException("Invalid number of parameter names, expected " + parameters.length + ", but got " + names.length + ", on " + method); } for (int i = 0; i < names.length; i++) { // add facets directly to parameters, not to actions FacetUtil.addFacet(new NamedFacetViaMethod(names[i], method, parameters[i])); } } private void findAndRemoveChoicesForActionParametersMethod( final boolean oldChoicesOrDefaultsMethodsUsed, final MethodRemover methodRemover, final Class cls, final String capitalizedName, final Class[] params, final FacetHolder[] parameters) { for (int i = 0; i < params.length; i++) { final Class returnType = (Array.newInstance(params[i], 0)).getClass(); String name = PARAMETER_CHOICES_PREFIX + i + capitalizedName; Method method = findMethodWithOrWithoutParameters(cls, OBJECT, name, returnType, null); if (method == null) { method = findMethodWithOrWithoutParameters(cls, OBJECT, name, List.class, null); } if (method != null) { if (oldChoicesOrDefaultsMethodsUsed) { throw new ReflectionException(cls + " uses both old and new choices/default syntax - must use one or other"); } removeMethod(methodRemover, method); // add facets directly to parameters, not to actions FacetUtil.addFacet(new ActionParameterChoicesFacetViaMethod(method, returnType, parameters[i], getSpecificationLoader(), getRuntimeContext())); } } } private void findAndRemoveDefaultForActionParametersMethod( final boolean oldChoicesOrDefaultsMethodsUsed, final MethodRemover methodRemover, final Class cls, final String capitalizedName, final Class[] params, final FacetHolder[] parameters) { for (int i = 0; i < params.length; i++) { final Method method = findMethodWithOrWithoutParameters(cls, OBJECT, PARAMETER_DEFAULTS_PREFIX + i + capitalizedName, params[i], null); if (method != null) { if (oldChoicesOrDefaultsMethodsUsed) { throw new ReflectionException(cls + " uses both old and new choices/default syntax - must use one or other"); } removeMethod(methodRemover, method); // add facets directly to parameters, not to actions FacetUtil.addFacet(new ActionParameterDefaultsFacetViaMethod(method, parameters[i])); } } } private void findAndRemoveDescriptionsforActionParametersMethod( final MethodRemover methodRemover, final Class cls, final String capitalizedName, final Class returnType, final Class[] params, final FacetHolder[] parameters) { final Method method = findMethodWithOrWithoutParameters(cls, CLASS, PARAMETER_DESCRIPTIONS_PREFIX + capitalizedName, String[].class, params); if (method == null) { return; } removeMethod(methodRemover, method); final Object[] parameterObjects = new Object[method.getParameterTypes().length]; final String[] names = (String[]) InvokeUtils.invokeStatic(method, parameterObjects); for (int i = 0; i < names.length; i++) { // add facets directly to parameters, not to actions FacetUtil.addFacet(new DescribedAsFacetViaMethod(names[i], method, parameters[i])); } methodRemover.removeMethod(method); } private String removePrefix(final String name, final String prefix) { if (name.startsWith(prefix)) { return name.substring(prefix.length()); } else { return name; } } private boolean allParametersOfSameType(final Class[] params) { final Class firstParam = params[0]; for (int i = 1; i < params.length; i++) { if (params[i] != firstParam) { return false; } } return true; } ///////////////////////////////////////////////////////////////// // Dependencies ///////////////////////////////////////////////////////////////// /** * @see #setRuntimeContext(RuntimeContext) * @return */ public RuntimeContext getRuntimeContext() { return runtimeContext; } /** * Injected because {@link RuntimeContextAware} */ public void setRuntimeContext(final RuntimeContext runtimeContext) { this.runtimeContext = runtimeContext; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy