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.List; 020import java.util.Map; 021 022import org.apache.isis.applib.annotation.ActionSemantics; 023import org.apache.isis.applib.annotation.Where; 024import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 025import org.apache.isis.core.metamodel.spec.ObjectSpecification; 026import org.apache.isis.core.metamodel.spec.feature.ObjectAction; 027import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter; 028import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 029import org.apache.isis.viewer.restfulobjects.applib.Rel; 030import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; 031import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs; 032import org.apache.isis.viewer.restfulobjects.rendering.RendererContext; 033import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ActionDescriptionReprRenderer; 034import org.codehaus.jackson.node.NullNode; 035 036import com.google.common.collect.Lists; 037 038public class ObjectActionReprRenderer extends AbstractObjectMemberReprRenderer<ObjectActionReprRenderer, ObjectAction> { 039 040 public ObjectActionReprRenderer(final RendererContext resourceContext, final LinkFollowSpecs linkFollowSpecs, String actionId, final JsonRepresentation representation) { 041 super(resourceContext, linkFollowSpecs, actionId, RepresentationType.OBJECT_ACTION, representation, Where.OBJECT_FORMS); 042 } 043 044 @Override 045 public JsonRepresentation render() { 046 // memberType is rendered eagerly 047 048 renderMemberContent(); 049 putDisabledReasonIfDisabled(); 050 051 if (mode.isStandalone() || mode.isMutated()) { 052 addParameterDetails(); 053 } 054 055 return representation; 056 } 057 058 // /////////////////////////////////////////////////// 059 // details link 060 // /////////////////////////////////////////////////// 061 062 /** 063 * Mandatory hook method to support x-ro-follow-links 064 */ 065 @Override 066 protected void followDetailsLink(final JsonRepresentation detailsLink) { 067 final ObjectActionReprRenderer renderer = new ObjectActionReprRenderer(getRendererContext(), getLinkFollowSpecs(), null, JsonRepresentation.newMap()); 068 renderer.with(new ObjectAndAction(objectAdapter, objectMember)).usingLinkTo(linkTo).asFollowed(); 069 detailsLink.mapPut("value", renderer.render()); 070 } 071 072 // /////////////////////////////////////////////////// 073 // mutators 074 // /////////////////////////////////////////////////// 075 076 @Override 077 protected void addMutatorsIfEnabled() { 078 if (usability().isVetoed()) { 079 return; 080 } 081 final Map<String, MutatorSpec> mutators = memberType.getMutators(); 082 083 final ActionSemantics.Of actionSemantics = objectMember.getSemantics(); 084 final String mutator = InvokeKeys.getKeyFor(actionSemantics); 085 final MutatorSpec mutatorSpec = mutators.get(mutator); 086 087 addLinkFor(mutatorSpec); 088 } 089 090 @Override 091 protected ObjectAdapterLinkTo linkToForMutatorInvoke() { 092 if (!objectMember.isContributed()) { 093 return super.linkToForMutatorInvoke(); 094 } 095 final DomainServiceLinkTo linkTo = new DomainServiceLinkTo(); 096 return linkTo.usingUrlBase(getRendererContext()).with(contributingServiceAdapter()); 097 } 098 099 private ObjectAdapter contributingServiceAdapter() { 100 final ObjectSpecification serviceType = objectMember.getOnType(); 101 final List<ObjectAdapter> serviceAdapters = getServiceAdapters(); 102 for (final ObjectAdapter serviceAdapter : serviceAdapters) { 103 if (serviceAdapter.getSpecification() == serviceType) { 104 return serviceAdapter; 105 } 106 } 107 // fail fast 108 throw new IllegalStateException("Unable to locate contributing service"); 109 } 110 111 @Override 112 protected JsonRepresentation mutatorArgs(final MutatorSpec mutatorSpec) { 113 final JsonRepresentation argMap = JsonRepresentation.newMap(); 114 final List<ObjectActionParameter> parameters = objectMember.getParameters(); 115 for (int i = 0; i < objectMember.getParameterCount(); i++) { 116 argMap.mapPut(parameters.get(i).getId() + ".value", argValueFor(i)); 117 } 118 return argMap; 119 } 120 121 private Object argValueFor(final int i) { 122 if (objectMember.isContributed()) { 123 final ObjectActionParameter actionParameter = objectMember.getParameters().get(i); 124 if (actionParameter.getSpecification().isOfType(objectAdapter.getSpecification())) { 125 return DomainObjectReprRenderer.newLinkToBuilder(rendererContext, Rel.VALUE, objectAdapter).build(); 126 } 127 } 128 // force a null into the map 129 return NullNode.getInstance(); 130 } 131 132 // /////////////////////////////////////////////////// 133 // parameter details 134 // /////////////////////////////////////////////////// 135 136 private ObjectActionReprRenderer addParameterDetails() { 137 final List<Object> parameters = Lists.newArrayList(); 138 for (int i = 0; i < objectMember.getParameterCount(); i++) { 139 final ObjectActionParameter param = objectMember.getParameters().get(i); 140 parameters.add(paramDetails(param)); 141 } 142 representation.mapPut("parameters", parameters); 143 return this; 144 } 145 146 private Object paramDetails(final ObjectActionParameter param) { 147 final JsonRepresentation paramRep = JsonRepresentation.newMap(); 148 paramRep.mapPut("num", param.getNumber()); 149 paramRep.mapPut("id", param.getId()); 150 paramRep.mapPut("name", param.getName()); 151 paramRep.mapPut("description", param.getDescription()); 152 final Object paramChoices = choicesFor(param); 153 if (paramChoices != null) { 154 paramRep.mapPut("choices", paramChoices); 155 } 156 final Object paramDefault = defaultFor(param); 157 if (paramDefault != null) { 158 paramRep.mapPut("default", paramDefault); 159 } 160 return paramRep; 161 } 162 163 private Object choicesFor(final ObjectActionParameter param) { 164 final ObjectAdapter[] choiceAdapters = param.getChoices(objectAdapter); 165 if (choiceAdapters == null || choiceAdapters.length == 0) { 166 return null; 167 } 168 final List<Object> list = Lists.newArrayList(); 169 for (final ObjectAdapter choiceAdapter : choiceAdapters) { 170 // REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself 171 // final ObjectSpecification choiceSpec = param.getSpecification(); 172 final ObjectSpecification choiceSpec = choiceAdapter.getSpecification(); 173 list.add(DomainObjectReprRenderer.valueOrRef(rendererContext, choiceAdapter, choiceSpec)); 174 } 175 return list; 176 } 177 178 private Object defaultFor(final ObjectActionParameter param) { 179 final ObjectAdapter defaultAdapter = param.getDefault(objectAdapter); 180 if (defaultAdapter == null) { 181 return null; 182 } 183 // REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself 184 // final ObjectSpecification defaultSpec = param.getSpecification(); 185 final ObjectSpecification defaultSpec = defaultAdapter.getSpecification(); 186 return DomainObjectReprRenderer.valueOrRef(rendererContext, defaultAdapter, defaultSpec); 187 } 188 189 // /////////////////////////////////////////////////// 190 // extensions and links 191 // /////////////////////////////////////////////////// 192 193 @Override 194 protected void addLinksToFormalDomainModel() { 195 getLinks().arrayAdd(ActionDescriptionReprRenderer.newLinkToBuilder(rendererContext, Rel.DESCRIBEDBY, objectAdapter.getSpecification(), objectMember).build()); 196 } 197 198 @Override 199 protected void addLinksIsisProprietary() { 200 if (objectMember.isContributed()) { 201 final ObjectAdapter serviceAdapter = contributingServiceAdapter(); 202 final JsonRepresentation contributedByLink = DomainObjectReprRenderer.newLinkToBuilder(rendererContext, Rel.CONTRIBUTED_BY, serviceAdapter).build(); 203 getLinks().arrayAdd(contributedByLink); 204 } 205 } 206 207 @Override 208 protected void putExtensionsIsisProprietary() { 209 getExtensions().mapPut("actionType", objectMember.getType().name().toLowerCase()); 210 211 final ActionSemantics.Of semantics = objectMember.getSemantics(); 212 getExtensions().mapPut("actionSemantics", semantics.getCamelCaseName()); 213 } 214 215}