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}