001/**
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
018
019import java.util.Collection;
020import java.util.Map;
021
022import org.codehaus.jackson.node.NullNode;
023
024import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
025import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
026import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
027import org.apache.isis.core.metamodel.spec.ObjectSpecification;
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.domainobjects.ActionResultRepresentation.ResultType;
033import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder;
034import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs;
035import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
036import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererAbstract;
037
038public class ActionResultReprRenderer extends ReprRendererAbstract<ActionResultReprRenderer, ObjectAndActionInvocation> {
039
040    private ObjectAdapterLinkTo adapterLinkTo = new DomainObjectLinkTo();
041
042    private ObjectAdapter objectAdapter;
043    private ObjectAction action;
044    private JsonRepresentation arguments;
045    private ObjectAdapter returnedAdapter;
046    private final SelfLink selfLink;
047
048
049    public enum SelfLink {
050        INCLUDED, EXCLUDED
051    }
052
053    public ActionResultReprRenderer(final RendererContext rendererContext, final LinkFollowSpecs linkFollower, final SelfLink selfLink, final JsonRepresentation representation) {
054        super(rendererContext, linkFollower, RepresentationType.ACTION_RESULT, representation);
055        this.selfLink = selfLink;
056    }
057
058    @Override
059    public ActionResultReprRenderer with(final ObjectAndActionInvocation objectAndActionInvocation) {
060
061        objectAdapter = objectAndActionInvocation.getObjectAdapter();
062        action = objectAndActionInvocation.getAction();
063        arguments = objectAndActionInvocation.getArguments();
064        returnedAdapter = objectAndActionInvocation.getReturnedAdapter();
065
066        adapterLinkTo.with(returnedAdapter);
067
068        return this;
069    }
070
071    public void using(final ObjectAdapterLinkTo adapterLinkTo) {
072        this.adapterLinkTo = adapterLinkTo.with(objectAdapter);
073    }
074
075    @Override
076    public JsonRepresentation render() {
077
078        representationWithSelfFor(action, arguments);
079
080        addResult(representation);
081
082        addExtensionsIsisProprietaryChangedObjects();
083
084        return representation;
085    }
086
087    private void addResult(final JsonRepresentation representation) {
088        final JsonRepresentation result = JsonRepresentation.newMap();
089        final ResultType resultType = addResultTo(result);
090
091        if (!resultType.isVoid()) {
092            putResultType(representation, resultType);
093            if(returnedAdapter != null) {
094                representation.mapPut("result", result);
095            } else {
096                representation.mapPut("result", NullNode.getInstance());
097            }
098        }
099    }
100
101    private ResultType addResultTo(final JsonRepresentation result) {
102
103        final ObjectSpecification returnType = this.action.getReturnType();
104
105        if (returnType.getCorrespondingClass() == void.class) {
106            // void
107            return ResultType.VOID;
108        }
109
110        final CollectionFacet collectionFacet = returnType.getFacet(CollectionFacet.class);
111        if (collectionFacet != null) {
112            // collection
113
114            if(returnedAdapter != null) {
115                final Collection<ObjectAdapter> collectionAdapters = collectionFacet.collection(returnedAdapter);
116    
117                final ListReprRenderer renderer = new ListReprRenderer(rendererContext, null, result).withElementRel(Rel.ELEMENT);
118                renderer.with(collectionAdapters).withReturnType(action.getReturnType()).withElementType(returnedAdapter.getElementSpecification());
119    
120                renderer.render();
121            }
122            return ResultType.LIST;
123        }
124
125        final EncodableFacet encodableFacet = returnType.getFacet(EncodableFacet.class);
126        if (encodableFacet != null) {
127            // scalar
128
129            if(returnedAdapter != null) {
130                final ScalarValueReprRenderer renderer = new ScalarValueReprRenderer(rendererContext, null, result);
131                renderer.with(returnedAdapter).withReturnType(action.getReturnType());
132    
133                renderer.render();
134            }
135            return ResultType.SCALAR_VALUE;
136
137        }
138
139        {
140            // object
141            if(returnedAdapter != null) {
142                final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(rendererContext, null, result);
143                
144                renderer.with(returnedAdapter).includesSelf();
145                
146                renderer.render();
147            }
148            return ResultType.DOMAIN_OBJECT;
149        }
150    }
151
152    private void putResultType(final JsonRepresentation representation, final ResultType resultType) {
153        representation.mapPut("resulttype", resultType.getValue());
154    }
155
156    private void representationWithSelfFor(final ObjectAction action, final JsonRepresentation bodyArgs) {
157        final JsonRepresentation links = JsonRepresentation.newArray();
158        representation.mapPut("links", links);
159
160        if(selfLink == SelfLink.EXCLUDED) {
161            return;
162        }
163        
164        final LinkBuilder selfLinkBuilder = adapterLinkTo.memberBuilder(Rel.SELF, MemberType.ACTION, action, RepresentationType.ACTION_RESULT, "invoke");
165
166        // TODO: remove duplication with AbstractObjectMember#addLinkTo
167        final MemberType memberType = MemberType.of(action);
168        final Map<String, MutatorSpec> mutators = memberType.getMutators();
169
170        final String mutator = InvokeKeys.getKeyFor(action.getSemantics());
171        final MutatorSpec mutatorSpec = mutators.get(mutator);
172        selfLinkBuilder.withHttpMethod(mutatorSpec.httpMethod);
173
174        final JsonRepresentation selfLink = selfLinkBuilder.build();
175
176        links.arrayAdd(selfLink);
177        selfLink.mapPut("args", bodyArgs);
178    }
179
180}