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

org.nakedobjects.metamodel.specloader.internal.NakedObjectActionImpl Maven / Gradle / Ivy

The newest version!
package org.nakedobjects.metamodel.specloader.internal;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.nakedobjects.metamodel.adapter.Instance;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.authentication.AuthenticationSession;
import org.nakedobjects.metamodel.commons.debug.DebugString;
import org.nakedobjects.metamodel.commons.exceptions.NakedObjectException;
import org.nakedobjects.metamodel.commons.exceptions.UnknownTypeException;
import org.nakedobjects.metamodel.commons.filters.Filter;
import org.nakedobjects.metamodel.consent.Consent;
import org.nakedobjects.metamodel.consent.InteractionInvocationMethod;
import org.nakedobjects.metamodel.consent.InteractionResultSet;
import org.nakedobjects.metamodel.exceptions.ModelException;
import org.nakedobjects.metamodel.facets.Facet;
import org.nakedobjects.metamodel.facets.actions.choices.ActionChoicesFacet;
import org.nakedobjects.metamodel.facets.actions.choices.ActionParameterChoicesFacet;
import org.nakedobjects.metamodel.facets.actions.debug.DebugFacet;
import org.nakedobjects.metamodel.facets.actions.defaults.ActionDefaultsFacet;
import org.nakedobjects.metamodel.facets.actions.defaults.ActionParameterDefaultsFacet;
import org.nakedobjects.metamodel.facets.actions.executed.ExecutedFacet;
import org.nakedobjects.metamodel.facets.actions.executed.ExecutedFacet.Where;
import org.nakedobjects.metamodel.facets.actions.exploration.ExplorationFacet;
import org.nakedobjects.metamodel.facets.actions.invoke.ActionInvocationFacet;
import org.nakedobjects.metamodel.interactions.ActionInvocationContext;
import org.nakedobjects.metamodel.interactions.ActionUsabilityContext;
import org.nakedobjects.metamodel.interactions.ActionVisibilityContext;
import org.nakedobjects.metamodel.interactions.InteractionUtils;
import org.nakedobjects.metamodel.interactions.UsabilityContext;
import org.nakedobjects.metamodel.interactions.ValidityContext;
import org.nakedobjects.metamodel.interactions.VisibilityContext;
import org.nakedobjects.metamodel.runtimecontext.RuntimeContext;
import org.nakedobjects.metamodel.runtimecontext.spec.feature.NakedObjectMemberAbstract;
import org.nakedobjects.metamodel.services.container.query.QueryFindAllInstances;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.SpecificationFacets;
import org.nakedobjects.metamodel.spec.Target;
import org.nakedobjects.metamodel.spec.feature.NakedObjectAction;
import org.nakedobjects.metamodel.spec.feature.NakedObjectActionConstants;
import org.nakedobjects.metamodel.spec.feature.NakedObjectActionParameter;
import org.nakedobjects.metamodel.spec.feature.NakedObjectActionType;
import org.nakedobjects.metamodel.specloader.internal.peer.NakedObjectActionParamPeer;
import org.nakedobjects.metamodel.specloader.internal.peer.NakedObjectActionPeer;


public class NakedObjectActionImpl extends NakedObjectMemberAbstract implements NakedObjectAction {
    private final static Logger LOG = Logger.getLogger(NakedObjectActionImpl.class);

    public static NakedObjectActionType getType(final String typeStr) {
    	NakedObjectActionType type = NakedObjectActionType.valueOf(typeStr);
    	if (type == null) {
    		throw new IllegalArgumentException();
    	} 
    	return type;
    }

    private final NakedObjectActionPeer nakedObjectActionPeer;
    /**
     * Lazily initialized by {@link #getParameters()} (so don't use directly!)
     */
    private NakedObjectActionParameter[] parameters;

    /**
     * Controls the initialization of {@link #contributed}.
     */
    private boolean knownWhetherContributed = false;
    /**
     * Lazily initialized, controlled by {@link #knownWhetherContributed}
     * 
     * @see #isContributed()
     */
    private boolean contributed;

    // //////////////////////////////////////////////////////////////////
    // Constructors
    // //////////////////////////////////////////////////////////////////

    public NakedObjectActionImpl(
    		final String methodId, 
    		final NakedObjectActionPeer nakedObjectActionPeer, 
    		final RuntimeContext runtimeContext) {
        super(methodId, nakedObjectActionPeer, MemberType.ACTION, runtimeContext);
        this.nakedObjectActionPeer = nakedObjectActionPeer;
    }

    // //////////////////////////////////////////////////////////////////
    // ReturnType, OnType, Actions (set)
    // //////////////////////////////////////////////////////////////////

    /**
     * Always returns null.
     */
    public NakedObjectSpecification getSpecification() {
        return null;
    }

    public NakedObjectSpecification getReturnType() {
        final ActionInvocationFacet facet = getActionInvocationFacet();
        return facet.getReturnType();
    }

    /**
     * Returns true if the represented action returns something, else returns false.
     */
    public boolean hasReturn() {
        return getReturnType() != null;
    }

    public NakedObjectSpecification getOnType() {
        final ActionInvocationFacet facet = getActionInvocationFacet();
        return facet.getOnType();
    }

    public NakedObjectAction[] getActions() {
        return new NakedObjectAction[0];
    }

    // /////////////////////////////////////////////////////////////
    // getInstance
    // /////////////////////////////////////////////////////////////
    
    public Instance getInstance(NakedObject nakedObject) {
        NakedObjectAction specification = this;
        return nakedObject.getInstance(specification);
    }

    // /////////////////////////////////////////////////////////////
    // Target, Type, IsContributed
    // /////////////////////////////////////////////////////////////

    public Target getTarget() {
        final ExecutedFacet facet = getFacet(ExecutedFacet.class);
        final Where executeWhere = facet.value();
        if (executeWhere == Where.LOCALLY) {
            return NakedObjectActionConstants.LOCAL;
        } else if (executeWhere == Where.REMOTELY) {
            return NakedObjectActionConstants.REMOTE;
        } else if (executeWhere == Where.DEFAULT) {
            return NakedObjectActionConstants.DEFAULT;
        } else {
            throw new UnknownTypeException(executeWhere);
        }
    }

    public NakedObjectActionType getType() {
        Facet facet = getFacet(DebugFacet.class);
        if (facet != null) {
            return NakedObjectActionConstants.DEBUG;
        }
        facet = getFacet(ExplorationFacet.class);
        if (facet != null) {
            return NakedObjectActionConstants.EXPLORATION;
        }
        return NakedObjectActionConstants.USER;
    }

    public boolean isContributed() {
    	if (!knownWhetherContributed) {
    		contributed = determineWhetherContributed();
    		knownWhetherContributed = true;
    	}
        return contributed;
    }

	private boolean determineWhetherContributed() {
		if (getOnType().isService() && getParameterCount() > 0) {
            final NakedObjectActionParameter[] params = getParameters();
            for (int i = 0; i < params.length; i++) {
                if (params[i].isObject()) {
                    return true;
                }
            }
        }
        return false;
	}

    // /////////////////////////////////////////////////////////////
    // Parameters
    // /////////////////////////////////////////////////////////////

    public int getParameterCount() {
        return nakedObjectActionPeer.getParameters().length;
    }

    public boolean promptForParameters(final NakedObject target) {
    	NakedObjectActionParameter[] parameters = getParameters();
        if (isContributed() && !target.getSpecification().isService()) {
            return getParameterCount() > 1 || !target.getSpecification().isOfType(parameters[0].getSpecification());
        } else {
            return getParameterCount() > 0;
        }
    }

    /**
     * Build lazily by {@link #getParameters()}.
     * 
     * 

* Although this is lazily loaded, the method is also synchronized so there shouldn't be any * thread race conditions. */ public synchronized NakedObjectActionParameter[] getParameters() { if (this.parameters == null) { final int parameterCount = getParameterCount(); final NakedObjectActionParameter[] parameters = new NakedObjectActionParameter[parameterCount]; final NakedObjectActionParamPeer[] paramPeers = nakedObjectActionPeer.getParameters(); for (int i = 0; i < parameterCount; i++) { final NakedObjectSpecification specification = paramPeers[i].getSpecification(); if (specification.isParseable()) { parameters[i] = new NakedObjectActionParameterParseable(i, this, paramPeers[i]); } else if (specification.isObject()) { parameters[i] = new OneToOneActionParameterImpl(i, this, paramPeers[i]); } else if (specification.isCollection()) { throw new UnknownTypeException("collections not supported as parameters: " + getIdentifier()); } else { throw new UnknownTypeException(specification); } } this.parameters = parameters; } return parameters; } public synchronized NakedObjectSpecification[] getParameterTypes() { List parameterTypes = new ArrayList(); NakedObjectActionParameter[] parameters = getParameters(); for(NakedObjectActionParameter parameter: parameters) { parameterTypes.add(parameter.getSpecification()); } return parameterTypes.toArray(new NakedObjectSpecification[]{}); } public NakedObjectActionParameter[] getParameters(final Filter filter) { final NakedObjectActionParameter[] allParameters = getParameters(); final NakedObjectActionParameter[] selectedParameters = new NakedObjectActionParameter[allParameters.length]; int v = 0; for (int i = 0; i < allParameters.length; i++) { if (filter.accept(allParameters[i])) { selectedParameters[v++] = allParameters[i]; } } final NakedObjectActionParameter[] parameters = new NakedObjectActionParameter[v]; System.arraycopy(selectedParameters, 0, parameters, 0, v); return parameters; } private NakedObjectActionParameter getParameter(final int position) { final NakedObjectActionParameter[] parameters = getParameters(); if (position >= parameters.length) { throw new IllegalArgumentException("getParameter(int): only " + parameters.length + " parameters, position=" + position); } return parameters[position]; } // ///////////////////////////////////////////////////////////// // Visible (or hidden) // ///////////////////////////////////////////////////////////// public VisibilityContext createVisibleInteractionContext( final AuthenticationSession session, final InteractionInvocationMethod invocationMethod, final NakedObject targetNakedObject) { return new ActionVisibilityContext(session, invocationMethod, targetNakedObject, getIdentifier()); } @Override public Consent isVisible(final AuthenticationSession session, final NakedObject target) { return super.isVisible(session, realTarget(target)); } // ///////////////////////////////////////////////////////////// // Usable (or disabled) // ///////////////////////////////////////////////////////////// public UsabilityContext createUsableInteractionContext( final AuthenticationSession session, final InteractionInvocationMethod invocationMethod, final NakedObject targetNakedObject) { return new ActionUsabilityContext(session, invocationMethod, targetNakedObject, getIdentifier()); } @Override public Consent isUsable(final AuthenticationSession session, final NakedObject target) { return super.isUsable(session, realTarget(target)); } // ////////////////////////////////////////////////////////////////// // validate // ////////////////////////////////////////////////////////////////// /** * TODO: currently this method is hard-coded to assume all interactions are initiated * {@link InteractionInvocationMethod#BY_USER by user}. */ public Consent isProposedArgumentSetValid(final NakedObject object, final NakedObject[] proposedArguments) { return isProposedArgumentSetValidResultSet(object, proposedArguments).createConsent(); } private InteractionResultSet isProposedArgumentSetValidResultSet( final NakedObject object, final NakedObject[] proposedArguments) { final InteractionInvocationMethod invocationMethod = InteractionInvocationMethod.BY_USER; final InteractionResultSet resultSet = new InteractionResultSet(); final NakedObjectActionParameter[] actionParameters = getParameters(); if (proposedArguments != null) { // TODO: doesn't seem to be used... // NakedObject[] params = realParameters(object, proposedArguments); for (int i = 0; i < proposedArguments.length; i++) { final ValidityContext ic = actionParameters[i].createProposedArgumentInteractionContext(getAuthenticationSession(), invocationMethod, object, proposedArguments, i); InteractionUtils.isValidResultSet(getParameter(i), ic, resultSet); } } // only check the action's own validity if all the arguments are OK. if (resultSet.isAllowed()) { final ValidityContext ic = createActionInvocationInteractionContext(getAuthenticationSession(), invocationMethod, object, proposedArguments); InteractionUtils.isValidResultSet(this, ic, resultSet); } return resultSet; } public ActionInvocationContext createActionInvocationInteractionContext( final AuthenticationSession session, final InteractionInvocationMethod invocationMethod, final NakedObject targetObject, final NakedObject[] proposedArguments) { return new ActionInvocationContext(getAuthenticationSession(), invocationMethod, targetObject, getIdentifier(), proposedArguments); } // ////////////////////////////////////////////////////////////////// // execute // ////////////////////////////////////////////////////////////////// public NakedObject execute(final NakedObject object, final NakedObject[] parameters) { LOG.debug("execute action " + object + "." + getId()); final NakedObject[] params = realParameters(object, parameters); final NakedObject target = realTarget(object); final ActionInvocationFacet facet = getFacet(ActionInvocationFacet.class); return facet.invoke(target, params); } private ActionInvocationFacet getActionInvocationFacet() { return nakedObjectActionPeer.getFacet(ActionInvocationFacet.class); } /** * Previously (prior to 3.0.x) this method had a check to see if the action was on an instance. With the * reflector redesign this has been removed, because NOF 3.x only supports instance methods, not * class-level methods. */ public NakedObject realTarget(final NakedObject target) { if (target == null) { return findService(); } else if (target.getSpecification().isService()) { return target; } else if (isContributed()) { return findService(); } else { return target; } } private NakedObject findService() { final List services = getRuntimeContext().getServices(); for (NakedObject serviceAdapter : services) { if (serviceAdapter.getSpecification() == getOnType()) { return serviceAdapter; } } throw new NakedObjectException("failed to find service for action " + this.getName()); } private NakedObject[] realParameters(final NakedObject target, final NakedObject[] parameters) { if (parameters != null) { return parameters; } return isContributed() ? new NakedObject[] { target } : new NakedObject[0]; } // ////////////////////////////////////////////////////////////////// // defaults // ////////////////////////////////////////////////////////////////// public NakedObject[] getDefaults(final NakedObject target) { final NakedObject realTarget = realTarget(target); final int parameterCount = getParameterCount(); final NakedObjectActionParameter[] parameters = getParameters(); final Object[] parameterDefaultPojos; // TODO here and elsewhere: the target needs to be // replaced by the service where the action is for a service! // set a flag on entry if for a service - or get from spec using isService final ActionDefaultsFacet facet = getFacet(ActionDefaultsFacet.class); if (!facet.isNoop()) { // use the old defaultXxx approach parameterDefaultPojos = facet.getDefaults(realTarget); if (parameterDefaultPojos.length != parameterCount) { throw new ModelException("Defaults array of incompatible size; expected " + parameterCount + " elements, but was " + parameterDefaultPojos.length + " for " + facet); } for (int i = 0; i < parameterCount; i++) { if (parameterDefaultPojos[i] != null) { NakedObjectSpecification componentSpec = getRuntimeContext().getSpecificationLoader().loadSpecification( parameterDefaultPojos[i].getClass()); NakedObjectSpecification parameterSpec = parameters[i].getSpecification(); if (!componentSpec.isOfType(parameterSpec)) { throw new ModelException("Defaults type incompatible with parameter " + (i + 1) + " type; expected " + parameterSpec.getFullName() + ", but was " + componentSpec.getFullName()); } } } } else { // use the new defaultNXxx approach for each param in turn // (the reflector will have made sure both aren't installed). parameterDefaultPojos = new Object[parameterCount]; for (int i = 0; i < parameterCount; i++) { ActionParameterDefaultsFacet paramFacet = parameters[i].getFacet(ActionParameterDefaultsFacet.class); if (paramFacet != null && !paramFacet.isNoop()) { parameterDefaultPojos[i] = paramFacet.getDefault(realTarget); } else { parameterDefaultPojos[i] = null; } } } final NakedObject[] parameterDefaultAdapters = new NakedObject[parameterCount]; if (parameterDefaultPojos != null) { for (int i = 0; i < parameterCount; i++) { parameterDefaultAdapters[i] = adapterFor(parameterDefaultPojos[i]); } } // set the target if contributed. if (isContributed() && target != null) { for (int i = 0; i < parameterCount; i++) { if (target.getSpecification().isOfType(parameters[i].getSpecification())) { parameterDefaultAdapters[i] = target; } } } return parameterDefaultAdapters; } private NakedObject adapterFor(final Object pojo) { return pojo == null ? null : getRuntimeContext().adapterFor(pojo); } // ///////////////////////////////////////////////////////////// // options (choices) // ///////////////////////////////////////////////////////////// public NakedObject[][] getChoices(final NakedObject target) { final NakedObject realTarget = realTarget(target); final int parameterCount = getParameterCount(); Object[][] parameterChoicesPojos; final ActionChoicesFacet facet = getFacet(ActionChoicesFacet.class); NakedObjectActionParameter[] parameters = getParameters(); if (!facet.isNoop()) { // using the old choicesXxx() approach parameterChoicesPojos = facet.getChoices(realTarget); // if no options, or not the right number of pojos, then default if (parameterChoicesPojos == null) { parameterChoicesPojos = new Object[parameterCount][]; } else if (parameterChoicesPojos.length != parameterCount) { throw new ModelException("Choices array of incompatible size; expected " + parameterCount + " elements, but was " + parameterChoicesPojos.length + " for " + facet); } } else { // use the new choicesNXxx approach for each param in turn // (the reflector will have made sure both aren't installed). parameterChoicesPojos = new Object[parameterCount][]; for (int i = 0; i < parameterCount; i++) { ActionParameterChoicesFacet paramFacet = parameters[i].getFacet(ActionParameterChoicesFacet.class); if (paramFacet != null && !paramFacet.isNoop()) { parameterChoicesPojos[i] = paramFacet.getChoices(realTarget); } else { parameterChoicesPojos[i] = new Object[0]; } } } final NakedObject[][] parameterChoicesAdapters = new NakedObject[parameterCount][]; for (int i = 0; i < parameterCount; i++) { final NakedObjectSpecification paramSpec = parameters[i].getSpecification(); if (parameterChoicesPojos[i] != null && parameterChoicesPojos[i].length > 0) { NakedObjectActionParameterAbstract.checkChoicesType(getRuntimeContext(), parameterChoicesPojos[i], paramSpec); parameterChoicesAdapters[i] = new NakedObject[parameterChoicesPojos[i].length]; for (int j = 0; j < parameterChoicesPojos[i].length; j++) { parameterChoicesAdapters[i][j] = adapterFor(parameterChoicesPojos[i][j]); } } else if (SpecificationFacets.isBoundedSet(paramSpec)) { QueryFindAllInstances query = new QueryFindAllInstances(paramSpec); final List allInstancesAdapter = getRuntimeContext().allMatchingQuery(query); parameterChoicesAdapters[i] = new NakedObject[allInstancesAdapter.size()]; int j = 0; for(NakedObject adapter: allInstancesAdapter) { parameterChoicesAdapters[i][j++] = adapter; } } else if (paramSpec.isObject()) { parameterChoicesAdapters[i] = new NakedObject[0]; } else { throw new UnknownTypeException(paramSpec); } if (parameterChoicesAdapters[i].length == 0) { parameterChoicesAdapters[i] = null; } } return parameterChoicesAdapters; } // ////////////////////////////////////////////////////////////////// // debug, toString // ////////////////////////////////////////////////////////////////// public String debugData() { final DebugString debugString = new DebugString(); nakedObjectActionPeer.debugData(debugString); return debugString.toString(); } @Override public String toString() { final StringBuffer sb = new StringBuffer(); sb.append("Action ["); sb.append(super.toString()); sb.append(",type="); sb.append(getType()); sb.append(",returns="); sb.append(getReturnType()); sb.append(",parameters={"); for (int i = 0; i < getParameterCount(); i++) { if (i > 0) { sb.append(","); } sb.append(getParameters()[i].getSpecification().getShortName()); } sb.append("}]"); return sb.toString(); } } // Copyright (c) Naked Objects Group Ltd.





© 2015 - 2024 Weber Informatics LLC | Privacy Policy