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