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 */
019package org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
020
021import java.util.Map;
022import java.util.stream.Stream;
023
024import com.fasterxml.jackson.databind.node.NullNode;
025
026import org.apache.isis.core.metamodel.facets.collections.CollectionFacet;
027import org.apache.isis.core.metamodel.spec.ManagedObject;
028import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
029import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
030import org.apache.isis.viewer.restfulobjects.applib.Rel;
031import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
032import org.apache.isis.viewer.restfulobjects.applib.RestfulHttpMethod;
033import org.apache.isis.viewer.restfulobjects.applib.domainobjects.ActionResultRepresentation.ResultType;
034import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext;
035import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder;
036import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs;
037import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererAbstract;
038
039public class ActionResultReprRenderer extends ReprRendererAbstract<ActionResultReprRenderer, ObjectAndActionInvocation> {
040
041    private ObjectAdapterLinkTo adapterLinkTo = new DomainObjectLinkTo();
042
043    private ManagedObject objectAdapter;
044    private ObjectAction action;
045    private JsonRepresentation arguments;
046    private ManagedObject returnedAdapter;
047    private final SelfLink selfLink;
048    private ObjectAndActionInvocation objectAndActionInvocation;
049
050    public enum SelfLink {
051        INCLUDED, EXCLUDED
052    }
053
054    public ActionResultReprRenderer(
055            final IResourceContext resourceContext,
056            final LinkFollowSpecs linkFollower,
057            final SelfLink selfLink,
058            final JsonRepresentation representation) {
059        super(resourceContext, linkFollower, RepresentationType.ACTION_RESULT, representation);
060        this.selfLink = selfLink;
061    }
062
063    @Override
064    public ActionResultReprRenderer with(final ObjectAndActionInvocation objectAndActionInvocation) {
065
066        this.objectAndActionInvocation = objectAndActionInvocation;
067
068        objectAdapter = objectAndActionInvocation.getObjectAdapter();
069        action = objectAndActionInvocation.getAction();
070        arguments = objectAndActionInvocation.getArguments();
071        returnedAdapter = objectAndActionInvocation.getReturnedAdapter();
072
073        adapterLinkTo.with(returnedAdapter);
074
075        return this;
076    }
077
078    public void using(final ObjectAdapterLinkTo adapterLinkTo) {
079        this.adapterLinkTo = adapterLinkTo.with(objectAdapter);
080    }
081
082    @Override
083    public JsonRepresentation render() {
084
085        if(representation == null) {
086            return null;
087        }
088
089        representationWithSelfFor(action, arguments);
090
091        addResult(representation);
092
093        addExtensionsIsisProprietaryChangedObjects();
094
095        return representation;
096    }
097
098    private void addResult(final JsonRepresentation representation) {
099
100        final ResultType resultType = objectAndActionInvocation.determineResultType();
101        final JsonRepresentation result = JsonRepresentation.newMap();
102        addResultTo(resultType, result);
103
104        putResultType(representation, resultType);
105        if (!resultType.isVoid()) {
106            if(returnedAdapter != null) {
107                representation.mapPut("result", result);
108            } else {
109                representation.mapPut("result", NullNode.getInstance());
110            }
111        }
112    }
113
114    private void addResultTo(
115            final ResultType resultType, final JsonRepresentation representation) {
116
117        if(returnedAdapter == null) {
118            return;
119        }
120
121        final ReprRendererAbstract<?, ?> renderer = buildResultRenderer(resultType, representation);
122        if(renderer != null) {
123            renderer.render();
124        }
125    }
126
127    private ReprRendererAbstract<?, ?> buildResultRenderer(
128            final ResultType resultType,
129            final JsonRepresentation representation) {
130
131        //final ObjectSpecification returnType = this.action.getReturnType();
132
133        switch (resultType) {
134        case VOID:
135            return null;
136
137        case LIST:
138
139            final Stream<ManagedObject> collectionAdapters =
140                CollectionFacet.streamAdapters(returnedAdapter);
141
142            final ListReprRenderer listReprRenderer =
143                    new ListReprRenderer(resourceContext, null, representation).withElementRel(Rel.ELEMENT);
144            listReprRenderer.with(collectionAdapters)
145            .withReturnType(action.getReturnType())
146            .withElementType(returnedAdapter.getElementSpecification().orElse(null));
147
148            return listReprRenderer;
149
150        case SCALAR_VALUE:
151
152            final ScalarValueReprRenderer scalarValueReprRenderer =
153            new ScalarValueReprRenderer(resourceContext, null, representation);
154            scalarValueReprRenderer.with(returnedAdapter)
155            .withReturnType(action.getReturnType());
156
157            return scalarValueReprRenderer;
158
159        case DOMAIN_OBJECT:
160
161            final DomainObjectReprRenderer objectReprRenderer =
162            new DomainObjectReprRenderer(resourceContext, null, representation);
163
164            objectReprRenderer.with(returnedAdapter).includesSelf();
165
166            return objectReprRenderer;
167        default:
168            throw new IllegalStateException("All possible states of ResultType enumerated; resultType = " + resultType);
169        }
170    }
171
172    private void putResultType(final JsonRepresentation representation, final ResultType resultType) {
173        representation.mapPut("resulttype", resultType.getValue());
174    }
175
176    private void representationWithSelfFor(final ObjectAction action, final JsonRepresentation bodyArgs) {
177        final JsonRepresentation links = JsonRepresentation.newArray();
178        representation.mapPut("links", links);
179
180        if(selfLink == SelfLink.EXCLUDED) {
181            return;
182        }
183
184        final LinkBuilder selfLinkBuilder = adapterLinkTo.memberBuilder(Rel.SELF, MemberType.ACTION, action, RepresentationType.ACTION_RESULT, "invoke");
185
186        // TODO: remove duplication with AbstractObjectMember#addLinkTo
187        final MemberType memberType = MemberType.of(action);
188        final Map<String, MutatorSpec> mutators = memberType.getMutators();
189
190        final String mutator = InvokeKeys.getKeyFor(action.getSemantics());
191        final MutatorSpec mutatorSpec = mutators.get(mutator);
192        selfLinkBuilder.withHttpMethod(mutatorSpec.httpMethod);
193
194        final JsonRepresentation selfLink = selfLinkBuilder.build();
195
196        links.arrayAdd(selfLink);
197        selfLink.mapPut("args", bodyArgs);
198
199        final LinkBuilder upLinkBuilder = adapterLinkTo.memberBuilder(Rel.UP, MemberType.ACTION, action, RepresentationType.OBJECT_ACTION);
200        upLinkBuilder.withHttpMethod(RestfulHttpMethod.GET);
201
202        links.arrayAdd(upLinkBuilder.build());
203
204    }
205
206}