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}