001// Generated by delombok at Mon Oct 12 22:51:05 BST 2020 002/* 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020package org.apache.isis.viewer.restfulobjects.rendering.domainobjects; 021 022import java.util.List; 023import java.util.stream.Collectors; 024import java.util.stream.Stream; 025import org.apache.isis.applib.annotation.DomainServiceLayout; 026import org.apache.isis.core.metamodel.consent.Consent; 027import org.apache.isis.core.metamodel.facets.object.domainservicelayout.DomainServiceLayoutFacet; 028import org.apache.isis.core.metamodel.facets.object.title.TitleFacet; 029import org.apache.isis.core.metamodel.interactions.managed.ManagedAction; 030import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection; 031import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty; 032import org.apache.isis.core.metamodel.services.ServiceUtil; 033import org.apache.isis.core.metamodel.spec.ManagedObject; 034import org.apache.isis.core.metamodel.spec.ManagedObjects; 035import org.apache.isis.core.metamodel.spec.ObjectSpecification; 036import org.apache.isis.core.metamodel.spec.feature.Contributed; 037import org.apache.isis.core.metamodel.spec.feature.ObjectAction; 038import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; 039import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; 040import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; 041import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 042import org.apache.isis.viewer.restfulobjects.applib.Rel; 043import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; 044import org.apache.isis.viewer.restfulobjects.applib.RestfulHttpMethod; 045import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext; 046import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder; 047import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs; 048import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererAbstract; 049import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.DomainTypeReprRenderer; 050 051public class DomainObjectReprRenderer extends ReprRendererAbstract<DomainObjectReprRenderer, ManagedObject> { 052 private static final String X_RO_DOMAIN_TYPE = "x-ro-domain-type"; 053 054 public static LinkBuilder newLinkToBuilder(final IResourceContext resourceContext, final Rel rel, final ManagedObject objectAdapter) { 055 final String objectRef = ManagedObjects.stringifyElseFail(objectAdapter, "/"); 056 final String url = "objects/" + objectRef; 057 return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.DOMAIN_OBJECT, url).withTitle(objectAdapter.titleString(null)); 058 } 059 060 public static LinkBuilder newLinkToObjectLayoutBuilder(final IResourceContext resourceContext, final ManagedObject objectAdapter) { 061 final Rel rel = Rel.OBJECT_LAYOUT; 062 final String objectRef = ManagedObjects.stringifyElseFail(objectAdapter, "/"); 063 final String url = "objects/" + objectRef + "/object-layout"; 064 return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.OBJECT_LAYOUT, url); 065 } 066 067 public static LinkBuilder newLinkToObjectIconBuilder(final IResourceContext resourceContext, final ManagedObject objectAdapter) { 068 final Rel rel = Rel.OBJECT_ICON; 069 final String objectRef = ManagedObjects.stringifyElseFail(objectAdapter, "/"); 070 final String url = "objects/" + objectRef + "/image"; 071 return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.OBJECT_IMAGE, url); 072 } 073 074 075 private static enum Mode { 076 REGULAR, PERSIST_LINK_ARGUMENTS, UPDATE_PROPERTIES_LINK_ARGUMENTS, EVENT_SERIALIZATION; 077 078 public boolean isRegular() { 079 return this == REGULAR; 080 } 081 082 public boolean isPersistLinkArgs() { 083 return this == PERSIST_LINK_ARGUMENTS; 084 } 085 086 public boolean isUpdatePropertiesLinkArgs() { 087 return this == UPDATE_PROPERTIES_LINK_ARGUMENTS; 088 } 089 090 public boolean isEventSerialization() { 091 return this == EVENT_SERIALIZATION; 092 } 093 094 public boolean includeDescribedBy() { 095 return isRegular() || isPersistLinkArgs(); 096 } 097 098 public boolean includeUp() { 099 return isRegular(); 100 } 101 102 public boolean checkVisibility() { 103 return isRegular() || isUpdatePropertiesLinkArgs(); 104 } 105 106 public boolean isArgs() { 107 return isPersistLinkArgs() || isUpdatePropertiesLinkArgs(); 108 } 109 } 110 111 private ObjectAdapterLinkTo linkToBuilder; 112 private ManagedObject objectAdapter; 113 private Mode mode = Mode.REGULAR; 114 115 public DomainObjectReprRenderer(final IResourceContext resourceContext, final LinkFollowSpecs linkFollower, final JsonRepresentation representation) { 116 super(resourceContext, linkFollower, RepresentationType.DOMAIN_OBJECT, representation); 117 usingLinkToBuilder(new DomainObjectLinkTo()); 118 } 119 120 /** 121 * Override the default {@link ObjectAdapterLinkTo} (that is used for 122 123 * generating links. 124 */ 125 public DomainObjectReprRenderer usingLinkToBuilder(final ObjectAdapterLinkTo objectAdapterLinkToBuilder) { 126 this.linkToBuilder = objectAdapterLinkToBuilder.usingUrlBase(resourceContext); 127 return this; 128 } 129 130 @Override 131 public DomainObjectReprRenderer with(final ManagedObject objectAdapter) { 132 this.objectAdapter = objectAdapter; 133 String domainTypeHref = DomainTypeReprRenderer.newLinkToBuilder(getResourceContext(), Rel.DOMAIN_TYPE, objectAdapter.getSpecification()).build().getString("href"); 134 addMediaTypeParams(X_RO_DOMAIN_TYPE, domainTypeHref); 135 return this; 136 } 137 138 @Override 139 public JsonRepresentation render() { 140 if (representation == null) { 141 return null; 142 } 143 final boolean isService = objectAdapter.getSpecification().isManagedBean(); 144 if (!(mode.isArgs())) { 145 final java.util.Optional<org.apache.isis.core.metamodel.adapter.oid.RootOid> rootOidIfAny = objectAdapter.getRootOid(); 146 // self, extensions.oid 147 if (ManagedObjects.isIdentifiable(objectAdapter)) { 148 if (includesSelf) { 149 addLinkToSelf(); 150 } 151 rootOidIfAny.ifPresent(rootOid -> { 152 final java.lang.String oidStr = rootOid.enString(); 153 getExtensions().mapPut("oid", oidStr); 154 }); 155 } 156 // title 157 final String title = objectAdapter.titleString(null); 158 representation.mapPut("title", title); 159 // serviceId or instance Id 160 if (isService) { 161 representation.mapPut("serviceId", ServiceUtil.idOfAdapter(objectAdapter)); 162 } else { 163 rootOidIfAny.ifPresent(rootOid -> { 164 representation.mapPut("domainType", rootOid.getObjectSpecId().asString()); 165 representation.mapPut("instanceId", rootOid.getIdentifier()); 166 }); 167 } 168 } 169 // members 170 if (!mode.isUpdatePropertiesLinkArgs()) { 171 withMembers(objectAdapter); 172 } 173 // described by 174 if (mode.includeDescribedBy() && !resourceContext.suppressDescribedByLinks()) { 175 addLinkToDescribedBy(); 176 addLinkToObjectLayout(); 177 addLinkToObjectIcon(); 178 } 179 if (isService && mode.includeUp()) { 180 addLinkToUp(); 181 } 182 if (!mode.isArgs() && !resourceContext.objectPropertyValuesOnly()) { 183 // update/persist 184 addPersistLinkIfTransientAndPersistable(); 185 addUpdatePropertiesLinkIfRequired(); 186 // extensions 187 getExtensions().mapPut("isService", isService); 188 getExtensions().mapPut("isPersistent", ManagedObjects.isIdentifiable(objectAdapter)); 189 if (isService) { 190 final ObjectSpecification objectSpec = objectAdapter.getSpecification(); 191 final DomainServiceLayoutFacet layoutFacet = objectSpec.getFacet(DomainServiceLayoutFacet.class); 192 if (layoutFacet != null) { 193 final DomainServiceLayout.MenuBar menuBar = layoutFacet.getMenuBar(); 194 if (menuBar != null) { 195 getExtensions().mapPut("menuBar", menuBar); 196 } 197 } 198 } 199 } 200 return representation; 201 } 202 203 private void addLinkToSelf() { 204 final JsonRepresentation link = linkToBuilder.with(objectAdapter).builder(Rel.SELF).build(); 205 final LinkFollowSpecs linkFollower = getLinkFollowSpecs().follow("links"); 206 if (linkFollower.matches(link)) { 207 final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(getResourceContext(), linkFollower, JsonRepresentation.newMap()); 208 renderer.with(objectAdapter); 209 link.mapPut("value", renderer.render()); 210 } 211 getLinks().arrayAdd(link); 212 } 213 214 private void addLinkToDescribedBy() { 215 final JsonRepresentation link = DomainTypeReprRenderer.newLinkToBuilder(getResourceContext(), Rel.DESCRIBEDBY, objectAdapter.getSpecification()).build(); 216 final LinkFollowSpecs linkFollower = getLinkFollowSpecs().follow("links"); 217 if (linkFollower.matches(link)) { 218 final DomainTypeReprRenderer renderer = new DomainTypeReprRenderer(getResourceContext(), linkFollower, JsonRepresentation.newMap()); 219 renderer.with(objectAdapter.getSpecification()); 220 link.mapPut("value", renderer.render()); 221 } 222 getLinks().arrayAdd(link); 223 } 224 225 private void addLinkToObjectLayout() { 226 final LinkBuilder linkBuilder = DomainObjectReprRenderer.newLinkToObjectLayoutBuilder(getResourceContext(), objectAdapter); 227 final JsonRepresentation link = linkBuilder.build(); 228 getLinks().arrayAdd(link); 229 } 230 231 private void addLinkToObjectIcon() { 232 final LinkBuilder linkBuilder = DomainObjectReprRenderer.newLinkToObjectIconBuilder(getResourceContext(), objectAdapter); 233 final JsonRepresentation link = linkBuilder.build(); 234 getLinks().arrayAdd(link); 235 } 236 237 private void addLinkToUp() { 238 final JsonRepresentation link = LinkBuilder.newBuilder(resourceContext, Rel.UP.getName(), RepresentationType.LIST, "services").build(); 239 getLinks().arrayAdd(link); 240 } 241 242 private DomainObjectReprRenderer withMembers(final ManagedObject objectAdapter) { 243 final JsonRepresentation appendTo = mode.isUpdatePropertiesLinkArgs() ? representation : JsonRepresentation.newMap(); 244 final List<ObjectAssociation> associations = objectAdapter.getSpecification().streamAssociations(Contributed.INCLUDED).collect(Collectors.toList()); 245 addProperties(objectAdapter, appendTo, associations); 246 if (!resourceContext.objectPropertyValuesOnly()) { 247 if (!mode.isArgs()) { 248 addCollections(objectAdapter, appendTo, associations); 249 } 250 if (mode.isRegular()) { 251 final Stream<ObjectAction> actions = objectAdapter.getSpecification().streamObjectActions(Contributed.INCLUDED); 252 addActions(objectAdapter, actions, appendTo); 253 } 254 } 255 if (!mode.isUpdatePropertiesLinkArgs()) { 256 representation.mapPut("members", appendTo); 257 } 258 return this; 259 } 260 261 private void addProperties(final ManagedObject objectAdapter, final JsonRepresentation members, final List<ObjectAssociation> associations) { 262 for (final ObjectAssociation assoc : associations) { 263 if (mode.checkVisibility()) { 264 final Consent visibility = assoc.isVisible(objectAdapter, getInteractionInitiatedBy(), resourceContext.getWhere()); 265 if (!visibility.isAllowed()) { 266 continue; 267 } 268 } 269 if (!(assoc instanceof OneToOneAssociation)) { 270 continue; 271 } 272 final OneToOneAssociation property = (OneToOneAssociation) assoc; 273 final LinkFollowSpecs linkFollowerForProp = getLinkFollowSpecs().follow("members[" + property.getId() + "]"); 274 final JsonRepresentation propertyRepresentation = JsonRepresentation.newMap(); 275 final ObjectPropertyReprRenderer renderer = new ObjectPropertyReprRenderer(getResourceContext(), linkFollowerForProp, property.getId(), propertyRepresentation); 276 renderer.with(ManagedProperty.of(objectAdapter, property, resourceContext.getWhere())).usingLinkTo(linkToBuilder); 277 if (mode.isArgs()) { 278 renderer.asArguments(); 279 } 280 if (mode.isEventSerialization()) { 281 renderer.asEventSerialization(); 282 } 283 final JsonRepresentation propertyValueRepresentation = renderer.render(); 284 final JsonRepresentation propertyRepr = resourceContext.objectPropertyValuesOnly() ? propertyValueRepresentation.getRepresentation("value") : propertyValueRepresentation; 285 members.mapPut(assoc.getId(), propertyRepr); 286 } 287 } 288 289 private void addCollections(final ManagedObject objectAdapter, final JsonRepresentation members, final List<ObjectAssociation> associations) { 290 for (final ObjectAssociation assoc : associations) { 291 if (mode.checkVisibility()) { 292 final Consent visibility = assoc.isVisible(objectAdapter, getInteractionInitiatedBy(), resourceContext.getWhere()); 293 if (!visibility.isAllowed()) { 294 continue; 295 } 296 } 297 if (!(assoc instanceof OneToManyAssociation)) { 298 continue; 299 } 300 final OneToManyAssociation collection = (OneToManyAssociation) assoc; 301 final LinkFollowSpecs linkFollowerForColl = getLinkFollowSpecs().follow("members[" + collection.getId() + "]"); 302 final JsonRepresentation collectionRepresentation = JsonRepresentation.newMap(); 303 final ObjectCollectionReprRenderer renderer = new ObjectCollectionReprRenderer(getResourceContext(), linkFollowerForColl, collection.getId(), collectionRepresentation); 304 final org.apache.isis.applib.annotation.Where where = resourceContext.getWhere(); 305 renderer.with(ManagedCollection.of(objectAdapter, collection, where)).usingLinkTo(linkToBuilder); 306 if (mode.isEventSerialization()) { 307 renderer.asEventSerialization(); 308 } 309 members.mapPut(assoc.getId(), renderer.render()); 310 } 311 } 312 313 private void addActions(final ManagedObject objectAdapter, final Stream<ObjectAction> actions, final JsonRepresentation members) { 314 actions.filter(action -> { 315 final Consent visibility = action.isVisible(objectAdapter, getInteractionInitiatedBy(), resourceContext.getWhere()); 316 return visibility.isAllowed(); 317 }).forEach(action -> { 318 final LinkFollowSpecs linkFollowSpecs = getLinkFollowSpecs().follow("members[" + action.getId() + "]"); 319 final ObjectActionReprRenderer renderer = new ObjectActionReprRenderer(getResourceContext(), linkFollowSpecs, action.getId(), JsonRepresentation.newMap()); 320 final org.apache.isis.applib.annotation.Where where = resourceContext.getWhere(); 321 renderer.with(ManagedAction.of(objectAdapter, action, where)).usingLinkTo(linkToBuilder); 322 members.mapPut(action.getId(), renderer.render()); 323 }); 324 } 325 326 private void addPersistLinkIfTransientAndPersistable() { 327 if (ManagedObjects.isIdentifiable(objectAdapter)) { 328 return; 329 } 330 final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 331 final JsonRepresentation domainObjectRepr = renderer.with(objectAdapter).asPersistLinkArguments().render(); 332 final String domainType = objectAdapter.getSpecification().getSpecId().asString(); 333 final LinkBuilder persistLinkBuilder = LinkBuilder.newBuilder(getResourceContext(), Rel.PERSIST.getName(), RepresentationType.DOMAIN_OBJECT, "objects/%s", domainType).withHttpMethod(RestfulHttpMethod.POST).withArguments(domainObjectRepr); 334 getLinks().arrayAdd(persistLinkBuilder.build()); 335 } 336 337 private DomainObjectReprRenderer asPersistLinkArguments() { 338 this.mode = Mode.PERSIST_LINK_ARGUMENTS; 339 return this; 340 } 341 342 private DomainObjectReprRenderer asUpdatePropertiesLinkArguments() { 343 this.mode = Mode.UPDATE_PROPERTIES_LINK_ARGUMENTS; 344 return this; 345 } 346 347 // not part of the spec 348 public DomainObjectReprRenderer asEventSerialization() { 349 this.mode = Mode.EVENT_SERIALIZATION; 350 return this; 351 } 352 353 private void addUpdatePropertiesLinkIfRequired() { 354 if (mode.isEventSerialization()) { 355 return; 356 } 357 if (!ManagedObjects.isIdentifiable(objectAdapter)) { 358 return; 359 } 360 final boolean isService = objectAdapter.getSpecification().isManagedBean(); 361 if (isService) { 362 return; 363 } 364 final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(getResourceContext(), null, JsonRepresentation.newMap()); 365 final JsonRepresentation domainObjectRepr = renderer.with(objectAdapter).asUpdatePropertiesLinkArguments().render(); 366 if (!getResourceContext().suppressUpdateLink()) { 367 final java.lang.String objectRef = ManagedObjects.stringifyElseFail(objectAdapter); 368 final org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder updateLinkBuilder = LinkBuilder.newBuilder(getResourceContext(), Rel.UPDATE.getName(), RepresentationType.DOMAIN_OBJECT, "objects/%s", objectRef).withHttpMethod(RestfulHttpMethod.PUT).withArguments(domainObjectRepr); 369 getLinks().arrayAdd(updateLinkBuilder.build()); 370 } 371 } 372 373 public static Object valueOrRef(IResourceContext context, JsonValueEncoder jsonValueEncoder, ManagedObject adapter) { 374 final org.apache.isis.core.metamodel.spec.ObjectSpecification spec = adapter.getSpecification(); 375 if (spec.isValue()) { 376 String format = null; // TODO 377 return jsonValueEncoder.asObject(adapter, format); 378 } 379 final org.apache.isis.core.metamodel.facets.object.title.TitleFacet titleFacet = spec.getFacet(TitleFacet.class); 380 final String title = titleFacet.title(adapter); 381 return DomainObjectReprRenderer.newLinkToBuilder(context, Rel.VALUE, adapter).withTitle(title).build(); 382 } 383}