/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.node.NullNode;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.isis.applib.annotation.ActionSemantics;
import org.apache.isis.applib.annotation.Where;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
import org.apache.isis.viewer.restfulobjects.applib.Rel;
import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs;
import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.ActionDescriptionReprRenderer;
public class ObjectActionReprRenderer extends AbstractObjectMemberReprRenderer<ObjectActionReprRenderer, ObjectAction> {
public ObjectActionReprRenderer(RendererContext rendererContext) {
this(rendererContext, null, null, JsonRepresentation.newMap());
}
public ObjectActionReprRenderer(
final RendererContext rendererContext,
final LinkFollowSpecs linkFollowSpecs,
String actionId,
final JsonRepresentation representation) {
super(rendererContext, linkFollowSpecs, actionId, RepresentationType.OBJECT_ACTION, representation,
Where.OBJECT_FORMS);
}
@Override
public JsonRepresentation render() {
renderMemberContent();
putDisabledReasonIfDisabled();
if (mode.isStandalone() || mode.isMutated()) {
addParameterDetails();
}
return representation;
}
// ///////////////////////////////////////////////////
// details link
// ///////////////////////////////////////////////////
/**
* Mandatory hook method to support x-ro-follow-links
*/
@Override
protected void followDetailsLink(final JsonRepresentation detailsLink) {
final ObjectActionReprRenderer renderer = new ObjectActionReprRenderer(getRendererContext(), getLinkFollowSpecs(), null, JsonRepresentation.newMap());
renderer.with(new ObjectAndAction(objectAdapter, objectMember)).usingLinkTo(linkTo).asFollowed();
detailsLink.mapPut("value", renderer.render());
}
// ///////////////////////////////////////////////////
// mutators
// ///////////////////////////////////////////////////
@Override
protected void addMutatorLinksIfEnabled() {
if (usability().isVetoed()) {
return;
}
final Map<String, MutatorSpec> mutators = objectMemberType.getMutators();
final ActionSemantics.Of actionSemantics = objectMember.getSemantics();
final String mutator = InvokeKeys.getKeyFor(actionSemantics);
final MutatorSpec mutatorSpec = mutators.get(mutator);
addLinkFor(mutatorSpec);
}
@Override
protected ObjectAdapterLinkTo linkToForMutatorInvoke() {
if (true /*!objectMember.isContributed()*/) {
return super.linkToForMutatorInvoke();
}
final DomainServiceLinkTo linkTo = new DomainServiceLinkTo();
return linkTo.usingUrlBase(getRendererContext()).with(contributingServiceAdapter());
}
private ObjectAdapter contributingServiceAdapter() {
final ObjectSpecification serviceType = objectMember.getOnType();
final List<ObjectAdapter> serviceAdapters = getServiceAdapters();
for (final ObjectAdapter serviceAdapter : serviceAdapters) {
if (serviceAdapter.getSpecification() == serviceType) {
return serviceAdapter;
}
}
// fail fast
throw new IllegalStateException("Unable to locate contributing service");
}
@Override
protected JsonRepresentation mutatorArgs(final MutatorSpec mutatorSpec) {
final JsonRepresentation argMap = JsonRepresentation.newMap();
final List<ObjectActionParameter> parameters = objectMember.getParameters();
for (int i = 0; i < objectMember.getParameterCount(); i++) {
argMap.mapPut(parameters.get(i).getId() + ".value", argValueFor(i));
}
return argMap;
}
private Object argValueFor(final int i) {
// force a null into the map
return NullNode.getInstance();
}
// ///////////////////////////////////////////////////
// parameter details
// ///////////////////////////////////////////////////
private ObjectActionReprRenderer addParameterDetails() {
boolean gsoc2013 = getRendererContext().getConfiguration().getBoolean("isis.viewer.restfulobjects.gsoc2013.legacyParamDetails", false);
if(gsoc2013) {
final List<Object> parameters = Lists.newArrayList();
for (int i = 0; i < objectMember.getParameterCount(); i++) {
final ObjectActionParameter param = objectMember.getParameters().get(i);
final Object paramDetails = paramDetails(param, getInteractionInitiatedBy());
parameters.add(paramDetails);
}
representation.mapPut("parameters", parameters);
} else {
final Map<String,Object> parameters = Maps.newLinkedHashMap();
for (int i = 0; i < objectMember.getParameterCount(); i++) {
final ObjectActionParameter param = objectMember.getParameters().get(i);
final Object paramDetails = paramDetails(param, getInteractionInitiatedBy());
parameters.put(param.getId(), paramDetails);
}
representation.mapPut("parameters", parameters);
}
return this;
}
private Object paramDetails(final ObjectActionParameter param, final InteractionInitiatedBy interactionInitiatedBy) {
final JsonRepresentation paramRep = JsonRepresentation.newMap();
paramRep.mapPut("num", param.getNumber());
paramRep.mapPut("id", param.getId());
paramRep.mapPut("name", param.getName());
paramRep.mapPut("description", param.getDescription());
final Object paramChoices = choicesFor(param, interactionInitiatedBy);
if (paramChoices != null) {
paramRep.mapPut("choices", paramChoices);
}
final Object paramDefault = defaultFor(param);
if (paramDefault != null) {
paramRep.mapPut("default", paramDefault);
}
return paramRep;
}
private Object choicesFor(
final ObjectActionParameter param,
final InteractionInitiatedBy interactionInitiatedBy) {
final ObjectAdapter[] choiceAdapters = param.getChoices(objectAdapter, null, interactionInitiatedBy);
if (choiceAdapters == null || choiceAdapters.length == 0) {
return null;
}
final List<Object> list = Lists.newArrayList();
for (final ObjectAdapter choiceAdapter : choiceAdapters) {
// REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself
// final ObjectSpecification choiceSpec = param.getSpecification();
final ObjectSpecification choiceSpec = choiceAdapter.getSpecification();
list.add(DomainObjectReprRenderer.valueOrRef(rendererContext, choiceAdapter, choiceSpec));
}
return list;
}
private Object defaultFor(final ObjectActionParameter param) {
final ObjectAdapter defaultAdapter = param.getDefault(objectAdapter);
if (defaultAdapter == null) {
return null;
}
// REVIEW: previously was using the spec of the parameter, but think instead it should be the spec of the adapter itself
// final ObjectSpecification defaultSpec = param.getSpecification();
final ObjectSpecification defaultSpec = defaultAdapter.getSpecification();
return DomainObjectReprRenderer.valueOrRef(rendererContext, defaultAdapter, defaultSpec);
}
// ///////////////////////////////////////////////////
// extensions and links
// ///////////////////////////////////////////////////
@Override
protected void addLinksToFormalDomainModel() {
if(rendererContext.suppressDescribedByLinks()) {
return;
}
final JsonRepresentation link = ActionDescriptionReprRenderer.newLinkToBuilder(rendererContext, Rel.DESCRIBEDBY, objectAdapter.getSpecification(), objectMember).build();
getLinks().arrayAdd(link);
}
@Override
protected void addLinksIsisProprietary() {
// umm...
if (false /*objectMember.isContributed() */) {
final ObjectAdapter serviceAdapter = contributingServiceAdapter();
final JsonRepresentation contributedByLink = DomainObjectReprRenderer.newLinkToBuilder(rendererContext, Rel.CONTRIBUTED_BY, serviceAdapter).build();
getLinks().arrayAdd(contributedByLink);
}
}
@Override
protected void putExtensionsIsisProprietary() {
getExtensions().mapPut("actionType", objectMember.getType().name().toLowerCase());
final ActionSemantics.Of semantics = objectMember.getSemantics();
getExtensions().mapPut("actionSemantics", semantics.getCamelCaseName());
}
}