001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.isis.viewer.restfulobjects.rendering; 020 021import java.util.List; 022import java.util.Map; 023 024import javax.ws.rs.core.MediaType; 025 026import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 027import org.apache.isis.core.metamodel.spec.ObjectSpecification; 028import org.apache.isis.core.runtime.system.context.IsisContext; 029import org.apache.isis.core.runtime.system.transaction.UpdateNotifier; 030import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 031import org.apache.isis.viewer.restfulobjects.applib.Rel; 032import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; 033import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.DomainObjectReprRenderer; 034import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.DomainTypeReprRenderer; 035 036import com.google.common.collect.Maps; 037 038public abstract class ReprRendererAbstract<R extends ReprRendererAbstract<R, T>, T> implements ReprRenderer<R, T> { 039 040 protected final RendererContext rendererContext; 041 private final LinkFollowSpecs linkFollower; 042 private final RepresentationType representationType; 043 protected final JsonRepresentation representation; 044 private final Map<String,String> mediaTypeParams = Maps.newLinkedHashMap(); 045 046 protected boolean includesSelf; 047 048 public ReprRendererAbstract(final RendererContext rendererContext, final LinkFollowSpecs linkFollower, final RepresentationType representationType, final JsonRepresentation representation) { 049 this.rendererContext = rendererContext; 050 this.linkFollower = asProvidedElseCreate(linkFollower); 051 this.representationType = representationType; 052 this.representation = representation; 053 } 054 055 public RendererContext getRendererContext() { 056 return rendererContext; 057 } 058 059 public LinkFollowSpecs getLinkFollowSpecs() { 060 return linkFollower; 061 } 062 063 private LinkFollowSpecs asProvidedElseCreate(final LinkFollowSpecs linkFollower) { 064 if (linkFollower != null) { 065 return linkFollower; 066 } 067 return LinkFollowSpecs.create(rendererContext.getFollowLinks()); 068 } 069 070 @Override 071 public MediaType getMediaType() { 072 return representationType.getMediaType(mediaTypeParams); 073 } 074 075 protected void addMediaTypeParams(String param, String paramValue) { 076 mediaTypeParams.put(param, paramValue); 077 } 078 079 @SuppressWarnings("unchecked") 080 public R includesSelf() { 081 this.includesSelf = true; 082 return (R) this; 083 } 084 085 public R withLink(final Rel rel, final String href) { 086 if (href != null) { 087 getLinks().arrayAdd(LinkBuilder.newBuilder(rendererContext, rel.getName(), representationType, href).build()); 088 } 089 return cast(this); 090 } 091 092 public R withLink(final Rel rel, final JsonRepresentation link) { 093 final String relStr = link.getString("rel"); 094 if (relStr == null || !relStr.equals(rel.getName())) { 095 throw new IllegalArgumentException("Provided link does not have a 'rel' of '" + rel.getName() + "'; was: " + link); 096 } 097 if (link != null) { 098 getLinks().arrayAdd(link); 099 } 100 return cast(this); 101 } 102 103 104 /** 105 * Will lazily create links array as required 106 */ 107 protected JsonRepresentation getLinks() { 108 JsonRepresentation links = representation.getArray("links"); 109 if (links == null) { 110 links = JsonRepresentation.newArray(); 111 representation.mapPut("links", links); 112 } 113 return links; 114 } 115 116 protected void addLink(final Rel rel, final ObjectSpecification objectSpec) { 117 if (objectSpec == null) { 118 return; 119 } 120 final LinkBuilder linkBuilder = DomainTypeReprRenderer.newLinkToBuilder(getRendererContext(), rel, objectSpec); 121 JsonRepresentation link = linkBuilder.build(); 122 getLinks().arrayAdd(link); 123 124 final LinkFollowSpecs linkFollower = getLinkFollowSpecs().follow("links"); 125 if (linkFollower.matches(link)) { 126 final DomainTypeReprRenderer renderer = new DomainTypeReprRenderer(getRendererContext(), linkFollower, JsonRepresentation.newMap()) 127 .with(objectSpec); 128 link.mapPut("value", renderer.render()); 129 } 130 131 } 132 133 /** 134 * Will lazily create extensions map as required 135 */ 136 protected JsonRepresentation getExtensions() { 137 JsonRepresentation extensions = representation.getMap("extensions"); 138 if (extensions == null) { 139 extensions = JsonRepresentation.newMap(); 140 representation.mapPut("extensions", extensions); 141 } 142 return extensions; 143 } 144 145 public R withExtensions(final JsonRepresentation extensions) { 146 if (!extensions.isMap()) { 147 throw new IllegalArgumentException("extensions must be a map"); 148 } 149 representation.mapPut("extensions", extensions); 150 return cast(this); 151 } 152 153 @SuppressWarnings("unchecked") 154 protected static <R extends ReprRendererAbstract<R, T>, T> R cast(final ReprRendererAbstract<R, T> builder) { 155 return (R) builder; 156 } 157 158 @Override 159 public abstract JsonRepresentation render(); 160 161 /** 162 * Convenience for representations that are returned from objects that 163 * mutate state. 164 */ 165 protected final void addExtensionsIsisProprietaryChangedObjects() { 166 final UpdateNotifier updateNotifier = getUpdateNotifier(); 167 168 addToExtensions("changed", updateNotifier.getChangedObjects()); 169 addToExtensions("disposed", updateNotifier.getDisposedObjects()); 170 } 171 172 private void addToExtensions(final String key, final List<ObjectAdapter> adapters) { 173 final JsonRepresentation adapterList = JsonRepresentation.newArray(); 174 getExtensions().mapPut(key, adapterList); 175 for (final ObjectAdapter adapter : adapters) { 176 adapterList.arrayAdd(DomainObjectReprRenderer.newLinkToBuilder(getRendererContext(), Rel.VALUE, adapter).build()); 177 } 178 } 179 180 protected List<ObjectAdapter> getServiceAdapters() { 181 return IsisContext.getPersistenceSession().getServices(); 182 } 183 184 protected UpdateNotifier getUpdateNotifier() { 185 return IsisContext.getCurrentTransaction().getUpdateNotifier(); 186 } 187 188}