/* * 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; import java.util.List; import java.util.Map; import javax.ws.rs.core.MediaType; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; import org.apache.isis.core.metamodel.deployment.DeploymentCategory; import org.apache.isis.core.metamodel.spec.ObjectSpecification; 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.domainobjects.DomainObjectReprRenderer; import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.DomainTypeReprRenderer; import org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationService; public abstract class ReprRendererAbstract<R extends ReprRendererAbstract<R, T>, T> implements ReprRenderer<R, T> { protected final RendererContext rendererContext; private final LinkFollowSpecs linkFollower; private final RepresentationType representationType; protected final JsonRepresentation representation; private final Map<String,String> mediaTypeParams = Maps.newLinkedHashMap(); private final DeploymentCategory deploymentCategory; private final InteractionInitiatedBy interactionInitiatedBy; protected boolean includesSelf; public ReprRendererAbstract( final RendererContext rendererContext, final LinkFollowSpecs linkFollower, final RepresentationType representationType, final JsonRepresentation representation) { this.rendererContext = rendererContext; this.linkFollower = asProvidedElseCreate(linkFollower); this.representationType = representationType; this.representation = representation; this.deploymentCategory = determineDeploymentCategoryFrom(this.rendererContext); this.interactionInitiatedBy = determineInteractionInitiatedByFrom(this.rendererContext); } private static DeploymentCategory determineDeploymentCategoryFrom(final RendererContext rendererContext) { if(rendererContext instanceof RendererContext3) { return ((RendererContext3) rendererContext).getDeploymentCategory(); } else { return DeploymentCategory.PRODUCTION; // fallback } } private static InteractionInitiatedBy determineInteractionInitiatedByFrom( final RendererContext rendererContext) { if (rendererContext instanceof RepresentationService.Context4) { return ((RepresentationService.Context4) rendererContext).getInteractionInitiatedBy(); } else { // fallback return InteractionInitiatedBy.USER; } } protected DeploymentCategory getDeploymentCategory() { return deploymentCategory; } protected InteractionInitiatedBy getInteractionInitiatedBy() { return interactionInitiatedBy; } public RendererContext getRendererContext() { return rendererContext; } public LinkFollowSpecs getLinkFollowSpecs() { return linkFollower; } private LinkFollowSpecs asProvidedElseCreate(final LinkFollowSpecs linkFollower) { if (linkFollower != null) { return linkFollower; } return LinkFollowSpecs.create(rendererContext.getFollowLinks()); } @Override public MediaType getMediaType() { return representationType.getMediaType(mediaTypeParams); } protected void addMediaTypeParams(String param, String paramValue) { mediaTypeParams.put(param, paramValue); } @SuppressWarnings("unchecked") public R includesSelf() { this.includesSelf = true; return (R) this; } public R withLink(final Rel rel, final String href) { if (href != null) { getLinks().arrayAdd(LinkBuilder.newBuilder(rendererContext, rel.getName(), representationType, href).build()); } return cast(this); } public R withLink(final Rel rel, final JsonRepresentation link) { final String relStr = link.getString("rel"); if (relStr == null || !relStr.equals(rel.getName())) { throw new IllegalArgumentException("Provided link does not have a 'rel' of '" + rel.getName() + "'; was: " + link); } if (link != null) { getLinks().arrayAdd(link); } return cast(this); } /** * Will lazily create links array as required */ protected JsonRepresentation getLinks() { JsonRepresentation links = representation.getArray("links"); if (links == null) { links = JsonRepresentation.newArray(); representation.mapPut("links", links); } return links; } protected void addLink(final Rel rel, final ObjectSpecification objectSpec) { if (objectSpec == null) { return; } final LinkBuilder linkBuilder = DomainTypeReprRenderer.newLinkToBuilder(getRendererContext(), rel, objectSpec); JsonRepresentation link = linkBuilder.build(); getLinks().arrayAdd(link); final LinkFollowSpecs linkFollower = getLinkFollowSpecs().follow("links"); if (linkFollower.matches(link)) { final DomainTypeReprRenderer renderer = new DomainTypeReprRenderer(getRendererContext(), linkFollower, JsonRepresentation.newMap()) .with(objectSpec); link.mapPut("value", renderer.render()); } } /** * Will lazily create extensions map as required */ protected JsonRepresentation getExtensions() { JsonRepresentation extensions = representation.getMap("extensions"); if (extensions == null) { extensions = JsonRepresentation.newMap(); representation.mapPut("extensions", extensions); } return extensions; } public R withExtensions(final JsonRepresentation extensions) { if (!extensions.isMap()) { throw new IllegalArgumentException("extensions must be a map"); } representation.mapPut("extensions", extensions); return cast(this); } @SuppressWarnings("unchecked") protected static <R extends ReprRendererAbstract<R, T>, T> R cast(final ReprRendererAbstract<R, T> builder) { return (R) builder; } @Override public abstract JsonRepresentation render(); /** * Convenience for representations that are returned from objects that * mutate state. */ protected final void addExtensionsIsisProprietaryChangedObjects() { // TODO: have removed UpdateNotifier, plan is to re-introduce using the IsisTransaction enlisted objects // (which would also allow newly-created objects to be shown) final List<ObjectAdapter> changedObjects = Lists.newArrayList(); // updateNotifier.getChangedObjects(); final List<ObjectAdapter> disposedObjects = Lists.newArrayList(); // updateNotifier.getDisposedObjects(); addToExtensions("changed", changedObjects); addToExtensions("disposed", disposedObjects); } private void addToExtensions(final String key, final List<ObjectAdapter> adapters) { if(adapters == null || adapters.isEmpty()) { return; } final JsonRepresentation adapterList = JsonRepresentation.newArray(); getExtensions().mapPut(key, adapterList); for (final ObjectAdapter adapter : adapters) { adapterList.arrayAdd(DomainObjectReprRenderer.newLinkToBuilder(getRendererContext(), Rel.VALUE, adapter).build()); } } protected List<ObjectAdapter> getServiceAdapters() { return rendererContext.getPersistenceSession().getServices(); } }