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}