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.Map; 024import java.util.Objects; 025import com.fasterxml.jackson.databind.node.NullNode; 026import org.apache.isis.applib.annotation.Where; 027import org.apache.isis.commons.internal.collections._Lists; 028import org.apache.isis.core.metamodel.facetapi.Facet; 029import org.apache.isis.core.metamodel.facetapi.FacetHolder; 030import org.apache.isis.core.metamodel.facets.collections.collection.defaultview.DefaultViewFacet; 031import org.apache.isis.core.metamodel.facets.object.title.TitleFacet; 032import org.apache.isis.core.metamodel.facets.object.value.ValueFacet; 033import org.apache.isis.core.metamodel.facets.value.bigdecimal.BigDecimalValueFacet; 034import org.apache.isis.core.metamodel.facets.value.biginteger.BigIntegerValueFacet; 035import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty; 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.rendering.IResourceContext; 041import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder; 042import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs; 043import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.PropertyDescriptionReprRenderer; 044 045public class ObjectPropertyReprRenderer extends AbstractObjectMemberReprRenderer<ObjectPropertyReprRenderer, OneToOneAssociation> { 046 public ObjectPropertyReprRenderer(IResourceContext context) { 047 this(context, null, null, JsonRepresentation.newMap()); 048 } 049 050 public ObjectPropertyReprRenderer(final IResourceContext context, final LinkFollowSpecs linkFollower, final String propertyId, final JsonRepresentation representation) { 051 super(context, linkFollower, propertyId, RepresentationType.OBJECT_PROPERTY, representation, Where.OBJECT_FORMS); 052 } 053 054 @Override 055 public JsonRepresentation render() { 056 renderMemberContent(); 057 final LinkFollowSpecs followValue = getLinkFollowSpecs().follow("value"); 058 addValue(followValue); 059 putDisabledReasonIfDisabled(); 060 if (mode.isStandalone() || mode.isMutated()) { 061 addChoices(); 062 addExtensionsIsisProprietaryChangedObjects(); 063 } 064 return representation; 065 } 066 067 // /////////////////////////////////////////////////// 068 // value 069 // /////////////////////////////////////////////////// 070 private Object addValue(final LinkFollowSpecs linkFollower) { 071 final org.apache.isis.core.metamodel.spec.ManagedObject valueAdapterIfAny = objectMember.get(objectAdapter, getInteractionInitiatedBy()); 072 // use the runtime type if we have a value, else the compile time type of the member otherwise 073 final org.apache.isis.core.metamodel.spec.ObjectSpecification spec = valueAdapterIfAny != null ? valueAdapterIfAny.getSpecification() : objectMember.getSpecification(); 074 final org.apache.isis.core.metamodel.facets.object.value.ValueFacet valueFacet = spec.getFacet(ValueFacet.class); 075 if (valueFacet != null) { 076 String format = null; 077 final Class<?> specClass = spec.getCorrespondingClass(); 078 if (specClass == java.math.BigDecimal.class) { 079 // look for facet on member, else on the value's spec 080 final BigDecimalValueFacet bigDecimalValueFacet = getFacet(BigDecimalValueFacet.class, objectMember, valueAdapterIfAny != null ? valueAdapterIfAny.getSpecification() : null); 081 if (bigDecimalValueFacet != null) { 082 final Integer precision = bigDecimalValueFacet.getPrecision(); 083 final Integer scale = bigDecimalValueFacet.getScale(); 084 format = String.format("big-decimal(%d,%d)", precision, scale); 085 } 086 } else if (specClass == java.math.BigInteger.class) { 087 // look for facet on member, else on the value's spec 088 final BigIntegerValueFacet bigIntegerValueFacet = getFacet(BigIntegerValueFacet.class, objectMember, valueAdapterIfAny != null ? valueAdapterIfAny.getSpecification() : null); 089 if (bigIntegerValueFacet != null) { 090 format = String.format("big-integer"); 091 } 092 } 093 return jsonValueEncoder.appendValueAndFormat(valueAdapterIfAny, spec, representation, format, resourceContext.suppressMemberExtensions()); 094 } 095 boolean eagerlyRender = (renderEagerly() && resourceContext.canEagerlyRender(valueAdapterIfAny)) || (linkFollower != null && !linkFollower.isTerminated()); 096 if (valueAdapterIfAny == null) { 097 final NullNode value = NullNode.getInstance(); 098 representation.mapPut("value", value); 099 return value; 100 } else { 101 final TitleFacet titleFacet = spec.getFacet(TitleFacet.class); 102 final String title = titleFacet.title(valueAdapterIfAny); 103 final LinkBuilder valueLinkBuilder = DomainObjectReprRenderer.newLinkToBuilder(resourceContext, Rel.VALUE, valueAdapterIfAny).withTitle(title); 104 if (eagerlyRender) { 105 final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(resourceContext, linkFollower, JsonRepresentation.newMap()); 106 renderer.with(valueAdapterIfAny); 107 if (mode.isEventSerialization()) { 108 renderer.asEventSerialization(); 109 } 110 valueLinkBuilder.withValue(renderer.render()); 111 } 112 final JsonRepresentation valueJsonRepr = valueLinkBuilder.build(); 113 representation.mapPut("value", valueJsonRepr); 114 return valueJsonRepr; 115 } 116 } 117 118 private boolean renderEagerly() { 119 final DefaultViewFacet defaultViewFacet = objectMember.getFacet(DefaultViewFacet.class); 120 return defaultViewFacet != null && Objects.equals(defaultViewFacet.value(), "table"); 121 } 122 123 private static <T extends Facet> T getFacet(Class<T> facetType, FacetHolder... holders) { 124 for (FacetHolder holder : holders) { 125 if (holder == null) { 126 continue; 127 } 128 final T facet = holder.getFacet(facetType); 129 if (facet != null) { 130 return facet; 131 } 132 } 133 return null; 134 } 135 136 // /////////////////////////////////////////////////// 137 // details link 138 // /////////////////////////////////////////////////// 139 /** 140 * Mandatory hook method to support x-ro-follow-links 141 */ 142 @Override 143 protected void followDetailsLink(final JsonRepresentation detailsLink) { 144 final JsonRepresentation representation = JsonRepresentation.newMap(); 145 final ObjectPropertyReprRenderer renderer = new ObjectPropertyReprRenderer(getResourceContext(), getLinkFollowSpecs(), null, representation); 146 renderer.with(ManagedProperty.of(objectAdapter, objectMember, super.where)).asFollowed(); 147 detailsLink.mapPut("value", renderer.render()); 148 } 149 150 // /////////////////////////////////////////////////// 151 // mutators 152 // /////////////////////////////////////////////////// 153 @Override 154 protected void addMutatorLinksIfEnabled() { 155 if (usability().isVetoed()) { 156 return; 157 } 158 final Map<String, MutatorSpec> mutators = objectMemberType.getMutators(); 159 for (final String mutator : mutators.keySet()) { 160 final MutatorSpec mutatorSpec = mutators.get(mutator); 161 addLinkFor(mutatorSpec); 162 } 163 return; 164 } 165 166 // /////////////////////////////////////////////////// 167 // choices 168 // /////////////////////////////////////////////////// 169 private ObjectPropertyReprRenderer addChoices() { 170 final Object propertyChoices = propertyChoices(); 171 if (propertyChoices != null) { 172 representation.mapPut("choices", propertyChoices); 173 } 174 return this; 175 } 176 177 private Object propertyChoices() { 178 final org.apache.isis.commons.collections.Can<org.apache.isis.core.metamodel.spec.ManagedObject> choiceAdapters = objectMember.getChoices(objectAdapter, getInteractionInitiatedBy()); 179 if (choiceAdapters == null || choiceAdapters.isEmpty()) { 180 return null; 181 } 182 final List<Object> list = _Lists.newArrayList(); 183 for (final org.apache.isis.core.metamodel.spec.ManagedObject choiceAdapter : choiceAdapters) { 184 // REVIEW: previously was using the spec of the member, but think instead it should be the spec of the adapter itself 185 // final ObjectSpecification choiceSpec = objectMember.getSpecification(); 186 // REVIEW: check that it works for ToDoItem$Category, though... 187 list.add(DomainObjectReprRenderer.valueOrRef(resourceContext, super.getJsonValueEncoder(), choiceAdapter)); 188 } 189 return list; 190 } 191 192 // /////////////////////////////////////////////////// 193 // extensions and links 194 // /////////////////////////////////////////////////// 195 @Override 196 protected void addLinksToFormalDomainModel() { 197 if (resourceContext.suppressDescribedByLinks()) { 198 return; 199 } 200 final JsonRepresentation link = PropertyDescriptionReprRenderer.newLinkToBuilder(getResourceContext(), Rel.DESCRIBEDBY, objectAdapter.getSpecification(), objectMember).build(); 201 getLinks().arrayAdd(link); 202 } 203 204 @Override 205 protected void putExtensionsIsisProprietary() { 206 // none 207 } 208}