001// Generated by delombok at Thu Mar 19 15:24:58 GMT 2020
002/*
003 *  Licensed to the Apache Software Foundation (ASF) under one
004 *  or more contributor license agreements.  See the NOTICE file
005 *  distributed with this work for additional information
006 *  regarding copyright ownership.  The ASF licenses this file
007 *  to you under the Apache License, Version 2.0 (the
008 *  "License"); you may not use this file except in compliance
009 *  with the License.  You may obtain a copy of the License at
010 *
011 *        http://www.apache.org/licenses/LICENSE-2.0
012 *
013 *  Unless required by applicable law or agreed to in writing,
014 *  software distributed under the License is distributed on an
015 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 *  KIND, either express or implied.  See the License for the
017 *  specific language governing permissions and limitations
018 *  under the License.
019 */
020package org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
021
022import java.util.List;
023import java.util.Map;
024import java.util.stream.Stream;
025import com.fasterxml.jackson.databind.node.NullNode;
026import org.apache.isis.applib.annotation.SemanticsOf;
027import org.apache.isis.applib.annotation.Where;
028import org.apache.isis.core.commons.internal.collections._Lists;
029import org.apache.isis.core.commons.internal.collections._Maps;
030import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
031import org.apache.isis.core.metamodel.spec.ManagedObject;
032import org.apache.isis.core.metamodel.spec.ObjectSpecification;
033import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
034import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
035import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
036import org.apache.isis.viewer.restfulobjects.applib.Rel;
037import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
038import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext;
039import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs;
040import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ActionDescriptionReprRenderer;
041
042public class ObjectActionReprRenderer extends AbstractObjectMemberReprRenderer<ObjectActionReprRenderer, ObjectAction> {
043    public ObjectActionReprRenderer(IResourceContext resourceContext) {
044        this(resourceContext, null, null, JsonRepresentation.newMap());
045    }
046
047    public ObjectActionReprRenderer(final IResourceContext resourceContext, final LinkFollowSpecs linkFollowSpecs, String actionId, final JsonRepresentation representation) {
048        super(resourceContext, linkFollowSpecs, actionId, RepresentationType.OBJECT_ACTION, representation, Where.OBJECT_FORMS);
049    }
050
051    @Override
052    public JsonRepresentation render() {
053        renderMemberContent();
054        putDisabledReasonIfDisabled();
055        if (mode.isStandalone() || mode.isMutated()) {
056            addParameterDetails();
057        }
058        return representation;
059    }
060
061    // ///////////////////////////////////////////////////
062    // details link
063    // ///////////////////////////////////////////////////
064    /**
065     * Mandatory hook method to support x-ro-follow-links
066     */
067    @Override
068    protected void followDetailsLink(final JsonRepresentation detailsLink) {
069        final ObjectActionReprRenderer renderer = new ObjectActionReprRenderer(getResourceContext(), getLinkFollowSpecs(), null, JsonRepresentation.newMap());
070        renderer.with(new ObjectAndAction(objectAdapter, objectMember)).usingLinkTo(linkTo).asFollowed();
071        detailsLink.mapPut("value", renderer.render());
072    }
073
074    // ///////////////////////////////////////////////////
075    // mutators
076    // ///////////////////////////////////////////////////
077    @Override
078    protected void addMutatorLinksIfEnabled() {
079        if (usability().isVetoed()) {
080            return;
081        }
082        final Map<String, MutatorSpec> mutators = objectMemberType.getMutators();
083        final SemanticsOf actionSemantics = objectMember.getSemantics();
084        final String mutator = InvokeKeys.getKeyFor(actionSemantics);
085        final MutatorSpec mutatorSpec = mutators.get(mutator);
086        addLinkFor(mutatorSpec);
087    }
088
089    @Override
090    protected ObjectAdapterLinkTo linkToForMutatorInvoke() {
091        if (true /*!objectMember.isContributed()*/) {
092            return super.linkToForMutatorInvoke();
093        }
094        final DomainServiceLinkTo linkTo = new DomainServiceLinkTo();
095        return linkTo.usingUrlBase(getResourceContext()).with(contributingServiceAdapter());
096    }
097
098    private ManagedObject contributingServiceAdapter() {
099        final ObjectSpecification serviceType = objectMember.getOnType();
100        final Stream<ManagedObject> serviceAdapters = streamServiceAdapters();
101        return serviceAdapters.filter(serviceAdapter -> serviceAdapter.getSpecification() == serviceType).findFirst().orElseThrow(() -> new IllegalStateException("Unable to locate contributing service")); // fail fast
102    }
103
104    @Override
105    protected JsonRepresentation mutatorArgs(final MutatorSpec mutatorSpec) {
106        final JsonRepresentation argMap = JsonRepresentation.newMap();
107        final org.apache.isis.core.commons.collections.Can<org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter> parameters = objectMember.getParameters();
108        for (int i = 0; i < objectMember.getParameterCount(); i++) {
109            argMap.mapPut(parameters.getElseFail(i).getId() + ".value", argValueFor(i));
110        }
111        return argMap;
112    }
113
114    private Object argValueFor(final int i) {
115        // force a null into the map
116        return NullNode.getInstance();
117    }
118
119    // ///////////////////////////////////////////////////
120    // parameter details
121    // ///////////////////////////////////////////////////
122    private ObjectActionReprRenderer addParameterDetails() {
123        final Map<String, Object> parameters = _Maps.newLinkedHashMap();
124        for (int i = 0; i < objectMember.getParameterCount(); i++) {
125            final ObjectActionParameter param = objectMember.getParameters().getElseFail(i);
126            final Object paramDetails = paramDetails(param, getInteractionInitiatedBy());
127            parameters.put(param.getId(), paramDetails);
128        }
129        representation.mapPut("parameters", parameters);
130        return this;
131    }
132
133    private Object paramDetails(final ObjectActionParameter param, final InteractionInitiatedBy interactionInitiatedBy) {
134        final JsonRepresentation paramRep = JsonRepresentation.newMap();
135        paramRep.mapPut("num", param.getNumber());
136        paramRep.mapPut("id", param.getId());
137        paramRep.mapPut("name", param.getName());
138        paramRep.mapPut("description", param.getDescription());
139        final Object paramChoices = choicesFor(param, interactionInitiatedBy);
140        if (paramChoices != null) {
141            paramRep.mapPut("choices", paramChoices);
142        }
143        final Object paramDefault = defaultFor(param);
144        if (paramDefault != null) {
145            paramRep.mapPut("default", paramDefault);
146        }
147        return paramRep;
148    }
149
150    private Object choicesFor(final ObjectActionParameter param, final InteractionInitiatedBy interactionInitiatedBy) {
151        final org.apache.isis.core.commons.collections.Can<org.apache.isis.core.metamodel.spec.ManagedObject> choiceAdapters = param.getChoices(objectAdapter, null, interactionInitiatedBy);
152        if (choiceAdapters == null || choiceAdapters.isEmpty()) {
153            return null;
154        }
155        final List<Object> list = _Lists.newArrayList();
156        for (final org.apache.isis.core.metamodel.spec.ManagedObject choiceAdapter : choiceAdapters) {
157            // REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself
158            // final ObjectSpecification choiceSpec = param.getSpecification();
159            list.add(DomainObjectReprRenderer.valueOrRef(resourceContext, super.getJsonValueEncoder(), choiceAdapter));
160        }
161        return list;
162    }
163
164    private Object defaultFor(final ObjectActionParameter param) {
165        final ManagedObject defaultAdapter = param.getDefault(objectAdapter, null, null);
166        if (defaultAdapter == null) {
167            return null;
168        }
169        // REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself
170        // final ObjectSpecification defaultSpec = param.getSpecification();
171        return DomainObjectReprRenderer.valueOrRef(resourceContext, super.getJsonValueEncoder(), defaultAdapter);
172    }
173
174    // ///////////////////////////////////////////////////
175    // extensions and links
176    // ///////////////////////////////////////////////////
177    @Override
178    protected void addLinksToFormalDomainModel() {
179        if (resourceContext.suppressDescribedByLinks()) {
180            return;
181        }
182        final JsonRepresentation link = ActionDescriptionReprRenderer.newLinkToBuilder(resourceContext, Rel.DESCRIBEDBY, objectAdapter.getSpecification(), objectMember).build();
183        getLinks().arrayAdd(link);
184    }
185
186    @Override
187    protected void addLinksIsisProprietary() {
188        // umm...
189        if (false /*objectMember.isContributed() */) {
190            final ManagedObject serviceAdapter = contributingServiceAdapter();
191            final JsonRepresentation contributedByLink = DomainObjectReprRenderer.newLinkToBuilder(resourceContext, Rel.CONTRIBUTED_BY, serviceAdapter).build();
192            getLinks().arrayAdd(contributedByLink);
193        }
194    }
195
196    @Override
197    protected void putExtensionsIsisProprietary() {
198        getExtensions().mapPut("actionType", objectMember.getType().name().toLowerCase());
199        final SemanticsOf semantics = objectMember.getSemantics();
200        getExtensions().mapPut("actionSemantics", semantics.getCamelCaseName());
201    }
202}