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