Package org.apache.isis.viewer.wicket.model.models

Source Code of org.apache.isis.viewer.wicket.model.models.ActionModel

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you under the Apache License, Version 2.0 (the
*  "License"); you may not use this file except in compliance
*  with the License.  You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/

package org.apache.isis.viewer.wicket.model.models;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.collect.Maps;

import org.apache.wicket.request.mapper.parameter.PageParameters;

import org.apache.isis.applib.Identifier;
import org.apache.isis.applib.annotation.ActionSemantics;
import org.apache.isis.applib.annotation.BookmarkPolicy;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking;
import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
import org.apache.isis.core.metamodel.adapter.oid.RootOid;
import org.apache.isis.core.metamodel.adapter.oid.RootOidDefault;
import org.apache.isis.core.metamodel.consent.Consent;
import org.apache.isis.core.metamodel.facets.object.bookmarkable.BookmarkPolicyFacet;
import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
import org.apache.isis.core.metamodel.spec.ActionType;
import org.apache.isis.core.metamodel.spec.ObjectSpecId;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
import org.apache.isis.core.runtime.system.context.IsisContext;
import org.apache.isis.viewer.wicket.model.mementos.ActionMemento;
import org.apache.isis.viewer.wicket.model.mementos.ActionParameterMemento;
import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
import org.apache.isis.viewer.wicket.model.mementos.PageParameterNames;

/**
* Models an action invocation, either the gathering of arguments for the
* action's {@link Mode#PARAMETERS parameters}, or the handling of the
* {@link Mode#RESULTS results} once invoked.
*/
public class ActionModel extends BookmarkableModel<ObjectAdapter> {

    private static final long serialVersionUID = 1L;
   
    private static final String NULL_ARG = "$nullArg$";

    /**
     * Whether we are obtaining arguments (eg in a dialog), or displaying the
     * results
     */
    public enum Mode {
        PARAMETERS, RESULTS
    }

    /**
     * How to handle results when only a single result is returned
     */
    public enum SingleResultsMode {
        /**
         * Render a simple link, using "entityLink"
         */
        LINK,
        /**
         * Render the object directly, using "entity"
         */
        INLINE,
        /**
         * Redirect to <tt>EntityPage</tt>.
         */
        REDIRECT
    }

   
    //////////////////////////////////////////////////
    // Factory methods
    //////////////////////////////////////////////////

    public static ActionModel create(final ObjectAdapterMemento targetAdapter, final ActionMemento action, final Mode mode, final SingleResultsMode singleResultsMode) {
        return new ActionModel(targetAdapter, action, mode, singleResultsMode);
    }

    public static ActionModel createForPersistent(final PageParameters pageParameters) {
        return new ActionModel(pageParameters);
    }

    /**
     * Factory method for creating {@link PageParameters}.
     *
     * see {@link #ActionModel(PageParameters)}
     * @param concurrencyChecking TODO
     */
    public static PageParameters createPageParameters(final ObjectAdapter adapter, final ObjectAction objectAction, final ObjectAdapter contextAdapter, final SingleResultsMode singleResultsMode, ConcurrencyChecking concurrencyChecking) {
       
        final PageParameters pageParameters = createPageParameters(adapter, objectAction, singleResultsMode, concurrencyChecking);

        final String actionTitle = objectAction.getName();
        PageParameterNames.PAGE_TITLE.addStringTo(pageParameters, actionTitle);

        final Mode actionMode = determineActionMode(objectAction, contextAdapter);
        PageParameterNames.ACTION_MODE.addEnumTo(pageParameters, actionMode);

        return pageParameters;
    }

    private static PageParameters createPageParameters(final ObjectAdapter adapter, final ObjectAction objectAction, final SingleResultsMode singleResultsMode, ConcurrencyChecking concurrencyChecking) {
        final PageParameters pageParameters = new PageParameters();

        PageParameterNames.PAGE_TYPE.addEnumTo(pageParameters, PageType.ACTION);
        PageParameterNames.ACTION_SINGLE_RESULTS_MODE.addEnumTo(pageParameters, singleResultsMode);

        final String oidStr = concurrencyChecking == ConcurrencyChecking.CHECK?
                adapter.getOid().enString(getOidMarshaller()):
                adapter.getOid().enStringNoVersion(getOidMarshaller());
        PageParameterNames.OBJECT_OID.addStringTo(pageParameters, oidStr);

        final ActionType actionType = objectAction.getType();
        PageParameterNames.ACTION_TYPE.addEnumTo(pageParameters, actionType);
       
        final ObjectSpecification actionOnTypeSpec = objectAction.getOnType();
        if (actionOnTypeSpec != null) {
            PageParameterNames.ACTION_OWNING_SPEC.addStringTo(pageParameters, actionOnTypeSpec.getFullIdentifier());
        }

        final String actionId = determineActionId(objectAction);
        PageParameterNames.ACTION_ID.addStringTo(pageParameters, actionId);
       
        return pageParameters;
    }


    public static Entry<Integer, String> parse(final String paramContext) {
        final Pattern compile = Pattern.compile("([^=]+)=(.+)");
        final Matcher matcher = compile.matcher(paramContext);
        if (!matcher.matches()) {
            return null;
        }

        final int paramNum;
        try {
            paramNum = Integer.parseInt(matcher.group(1));
        } catch (final Exception e) {
            // ignore
            return null;
        }

        final String oidStr;
        try {
            oidStr = matcher.group(2);
        } catch (final Exception e) {
            return null;
        }

        return new Map.Entry<Integer, String>() {

            @Override
            public Integer getKey() {
                return paramNum;
            }

            @Override
            public String getValue() {
                return oidStr;
            }

            @Override
            public String setValue(final String value) {
                return null;
            }
        };
    }

    //////////////////////////////////////////////////
    // BookmarkableModel
    //////////////////////////////////////////////////


    public PageParameters asPageParameters() {
        final ObjectAdapter adapter = getTargetAdapter();
        final ObjectAction objectAction = getActionMemento().getAction();
        final PageParameters pageParameters = createPageParameters(adapter, objectAction, SingleResultsMode.REDIRECT, ConcurrencyChecking.CHECK);

        // capture argument values and build up a title
        final StringBuilder buf = new StringBuilder();
        final ObjectAdapter[] argumentsAsArray = getArgumentsAsArray();
        for(ObjectAdapter argumentAdapter: argumentsAsArray) {
            final String encodedArg = encodeArg(argumentAdapter);
            PageParameterNames.ACTION_ARGS.addStringTo(pageParameters, encodedArg);
            if(buf.length() > 0) {
                buf.append(",");
            }
            buf.append(abbreviated(titleOf(argumentAdapter), 8));
        }

        final String actionTitle = adapter.titleString(null) + "." + objectAction.getName() + (buf.length()>0?"(" + buf.toString() + ")":"");
        PageParameterNames.PAGE_TITLE.addStringTo(pageParameters, actionTitle);

        return pageParameters;
    }


    @Override
    public boolean hasAsRootPolicy() {
        return true;
    }

    @Override
    public boolean hasAsChildPolicy() {
        return false;
    }
   
    //////////////////////////////////////////////////
    // helpers
    //////////////////////////////////////////////////

   
    private static String titleOf(ObjectAdapter argumentAdapter) {
        return argumentAdapter!=null?argumentAdapter.titleString(null):"";
    }
   
    private static String abbreviated(final String str, final int maxLength) {
        return str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "...";
    }


    private static Mode determineActionMode(final ObjectAction objectAction, final ObjectAdapter contextAdapter) {
        return objectAction.promptForParameters(contextAdapter)?Mode.PARAMETERS:Mode.RESULTS;
    }

    private static String determineActionId(final ObjectAction objectAction) {
        final Identifier identifier = objectAction.getIdentifier();
        if (identifier != null) {
            return identifier.toNameParmsIdentityString();
        }
        // fallback (used for action sets)
        return objectAction.getId();
    }

    public static Mode determineMode(final ObjectAction action) {
        return action.getParameterCount() > 0 ? Mode.PARAMETERS : Mode.RESULTS;
    }

    private final ObjectAdapterMemento targetAdapterMemento;
    private final ActionMemento actionMemento;
    private Mode actionMode;
    private final SingleResultsMode singleResultsMode;

    /**
     * Lazily populated in {@link #getArgumentModel(ActionParameterMemento)}
     */
    private Map<Integer, ScalarModel> arguments = Maps.newHashMap();
    private ActionExecutor executor;

    private ActionModel(final PageParameters pageParameters) {
        this(newObjectAdapterMementoFrom(pageParameters), newActionMementoFrom(pageParameters), PageParameterNames.ACTION_MODE.getEnumFrom(pageParameters, Mode.class), PageParameterNames.ACTION_SINGLE_RESULTS_MODE.getEnumFrom(pageParameters, SingleResultsMode.class));

        setArgumentsIfPossible(pageParameters);
        setContextArgumentIfPossible(pageParameters);
    }

    private static ActionMemento newActionMementoFrom(final PageParameters pageParameters) {
        final ObjectSpecId owningSpec = ObjectSpecId.of(PageParameterNames.ACTION_OWNING_SPEC.getStringFrom(pageParameters));
        final ActionType actionType = PageParameterNames.ACTION_TYPE.getEnumFrom(pageParameters, ActionType.class);
        final String actionNameParms = PageParameterNames.ACTION_ID.getStringFrom(pageParameters);
        return new ActionMemento(owningSpec, actionType, actionNameParms);
    }

    private static ObjectAdapterMemento newObjectAdapterMementoFrom(final PageParameters pageParameters) {
        RootOid oid = oidFor(pageParameters);
        if(oid.isTransient()) {
            return null;
        } else {
            return ObjectAdapterMemento.createPersistent(oid);
        }
    }

    private static RootOid oidFor(final PageParameters pageParameters) {
        String oidStr = PageParameterNames.OBJECT_OID.getStringFrom(pageParameters);
        return getOidMarshaller().unmarshal(oidStr, RootOid.class);
    }

    private ActionModel(final ObjectAdapterMemento adapterMemento, final ActionMemento actionMemento, final Mode actionMode, final SingleResultsMode singleResultsMode) {
        this.targetAdapterMemento = adapterMemento;
        this.actionMemento = actionMemento;
        this.actionMode = actionMode;
        this.singleResultsMode = singleResultsMode;
    }

    private void setArgumentsIfPossible(final PageParameters pageParameters) {
        List<String> args = PageParameterNames.ACTION_ARGS.getListFrom(pageParameters);

        final ObjectAction action = actionMemento.getAction();
        final List<ObjectSpecification> parameterTypes = action.getParameterTypes();

        for (int paramNum = 0; paramNum < args.size(); paramNum++) {
            String encoded = args.get(paramNum);
            setArgument(paramNum, parameterTypes.get(paramNum), encoded);
        }
    }

    private boolean setContextArgumentIfPossible(final PageParameters pageParameters) {
        final String paramContext = PageParameterNames.ACTION_PARAM_CONTEXT.getStringFrom(pageParameters);
        if (paramContext == null) {
            return false;
        }

        final ObjectAction action = actionMemento.getAction();
        final List<ObjectSpecification> parameterTypes = action.getParameterTypes();
        final int parameterCount = parameterTypes.size();

        final Map.Entry<Integer, String> mapEntry = parse(paramContext);

        final int paramNum = mapEntry.getKey();
        if (paramNum >= parameterCount) {
            return false;
        }

        final String encoded = mapEntry.getValue();
        setArgument(paramNum, parameterTypes.get(paramNum), encoded);

        return true;
    }

    private void setArgument(final int paramNum, final ObjectSpecification argSpec, final String encoded) {
        final ObjectAdapter argumentAdapter = decodeArg(argSpec, encoded);
        setArgument(paramNum, argumentAdapter);
    }

    private String encodeArg(ObjectAdapter adapter) {
        if(adapter == null) {
            return NULL_ARG;
        }
       
        ObjectSpecification objSpec = adapter.getSpecification();
        if(objSpec.isEncodeable()) {
            EncodableFacet encodeable = objSpec.getFacet(EncodableFacet.class);
            return encodeable.toEncodedString(adapter);
        }
       
        return adapter.getOid().enString(getOidMarshaller());
    }

    private ObjectAdapter decodeArg(ObjectSpecification objSpec, String encoded) {
        if(NULL_ARG.equals(encoded)) {
            return null;
        }
       
        if(objSpec.isEncodeable()) {
            EncodableFacet encodeable = objSpec.getFacet(EncodableFacet.class);
            return encodeable.fromEncodedString(encoded);
        }
       
        try {
            final RootOid oid = RootOidDefault.deStringEncoded(encoded, getOidMarshaller());
            return getAdapterManager().adapterFor(oid);
        } catch (final Exception e) {
            return null;
        }
    }

    private void setArgument(final int paramNum, final ObjectAdapter argumentAdapter) {
        final ObjectAction action = actionMemento.getAction();
        final ObjectActionParameter actionParam = action.getParameters().get(paramNum);
        final ActionParameterMemento apm = new ActionParameterMemento(actionParam);
        final ScalarModel argumentModel = getArgumentModel(apm);
        argumentModel.setObject(argumentAdapter);
    }


    public ScalarModel getArgumentModel(final ActionParameterMemento apm) {
        ScalarModel scalarModel = arguments.get(apm.getNumber());
        if (scalarModel == null) {
            scalarModel = new ScalarModel(targetAdapterMemento, apm);
            final int number = scalarModel.getParameterMemento().getNumber();
            arguments.put(number, scalarModel);
        }
        return scalarModel;
    }

    public ObjectAdapter getTargetAdapter() {
        return targetAdapterMemento.getObjectAdapter(getConcurrencyChecking());
    }

    protected ConcurrencyChecking getConcurrencyChecking() {
        return actionMemento.getConcurrencyChecking();
    }

    public Mode getActionMode() {
        return actionMode;
    }

    public ActionMemento getActionMemento() {
        return actionMemento;
    }

    @Override
    protected ObjectAdapter load() {
       
        // from getObject()/reExecute
        detach(); // force re-execute
       
        final ObjectAdapter results = executeAction();
        this.actionMode = Mode.RESULTS;
       
        return results;
    }

   
   
    private ObjectAdapter executeAction() {
        final ObjectAdapter targetAdapter = getTargetAdapter();
        final ObjectAdapter[] arguments = getArgumentsAsArray();
        final ObjectAction action = getActionMemento().getAction();

        // let any exceptions propogate, will be caught by UI layer
        // (ActionPanel at time of writing)
        final ObjectAdapter results = action.execute(targetAdapter, arguments);
        return results;
    }

    public String getReasonInvalidIfAny() {
        final ObjectAdapter targetAdapter = getTargetAdapter();
        final ObjectAdapter[] proposedArguments = getArgumentsAsArray();
        final ObjectAction objectAction = getActionMemento().getAction();
        final Consent validity = objectAction.isProposedArgumentSetValid(targetAdapter, proposedArguments);
        return validity.isAllowed() ? null : validity.getReason();
    }

    @Override
    public void setObject(final ObjectAdapter object) {
        throw new UnsupportedOperationException("target adapter for ActionModel cannot be changed");
    }

    public ObjectAdapter[] getArgumentsAsArray() {
        final ObjectAction objectAction = getActionMemento().getAction();
        final ObjectAdapter[] arguments = new ObjectAdapter[objectAction.getParameterCount()];
        for (int i = 0; i < arguments.length; i++) {
            final ScalarModel scalarModel = this.arguments.get(i);
            arguments[i] = scalarModel.getObject();
        }
        return arguments;
    }

    public ActionExecutor getExecutor() {
        return executor;
    }

    public void setExecutor(final ActionExecutor executor) {
        this.executor = executor;
    }

    public SingleResultsMode getSingleResultsMode() {
        return singleResultsMode;
    }

    public void reset() {
        this.actionMode = determineMode(actionMemento.getAction());
    }

    public void clearArguments() {
        for (ScalarModel argumentModel : arguments.values()) {
            argumentModel.setObject((ObjectAdapter)null);
        }
    }

    /**
     * Bookmarkable if the {@link ObjectAction action} has a {@link BookmarkPolicyFacet bookmark} policy
     * of {@link BookmarkPolicy#AS_ROOT root}, and has safe {@link ObjectAction#getSemantics() semantics}.
     */
    public boolean isBookmarkable() {
        final ObjectAction action = getActionMemento().getAction();
        final BookmarkPolicyFacet bookmarkPolicy = action.getFacet(BookmarkPolicyFacet.class);
        final boolean safeSemantics = action.getSemantics() == ActionSemantics.Of.SAFE;
        return bookmarkPolicy.value() == BookmarkPolicy.AS_ROOT && safeSemantics;
    }

    //////////////////////////////////////////////////
    // Dependencies (from context)
    //////////////////////////////////////////////////
   
    protected static OidMarshaller getOidMarshaller() {
        return IsisContext.getOidMarshaller();
    }


}
TOP

Related Classes of org.apache.isis.viewer.wicket.model.models.ActionModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.