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