001/**
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.isis.viewer.restfulobjects.rendering.domainobjects;
018
019import java.util.List;
020import java.util.Map;
021
022import org.apache.isis.applib.annotation.Where;
023import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
024import org.apache.isis.core.metamodel.spec.ObjectSpecification;
025import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
026import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
027import org.apache.isis.viewer.restfulobjects.applib.Rel;
028import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
029import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs;
030import org.apache.isis.viewer.restfulobjects.rendering.RendererContext;
031import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.PropertyDescriptionReprRenderer;
032import org.codehaus.jackson.node.NullNode;
033
034import com.google.common.collect.Lists;
035
036public class ObjectPropertyReprRenderer extends AbstractObjectMemberReprRenderer<ObjectPropertyReprRenderer, OneToOneAssociation> {
037
038    public ObjectPropertyReprRenderer(final RendererContext resourceContext, final LinkFollowSpecs linkFollower, final String propertyId, final JsonRepresentation representation) {
039        super(resourceContext, linkFollower, propertyId, RepresentationType.OBJECT_PROPERTY, representation, Where.OBJECT_FORMS);
040    }
041
042    @Override
043    public JsonRepresentation render() {
044        // id and memberType are rendered eagerly
045
046        renderMemberContent();
047        addValue();
048
049        putDisabledReasonIfDisabled();
050
051        if (mode.isStandalone() || mode.isMutated()) {
052            addChoices();
053            addExtensionsIsisProprietaryChangedObjects();
054        }
055
056        return representation;
057    }
058
059    // ///////////////////////////////////////////////////
060    // value
061    // ///////////////////////////////////////////////////
062
063    private void addValue() {
064        final ObjectAdapter valueAdapter = objectMember.get(objectAdapter);
065        
066        // use the runtime type if we have a value, else the compile time type of the member otherwise
067        final ObjectSpecification specification = valueAdapter != null? valueAdapter.getSpecification(): objectMember.getSpecification();
068        
069        DomainObjectReprRenderer.appendValueAndFormatOrRef(rendererContext, valueAdapter, specification, representation);
070    }
071
072    // ///////////////////////////////////////////////////
073    // details link
074    // ///////////////////////////////////////////////////
075
076    /**
077     * Mandatory hook method to support x-ro-follow-links
078     */
079    @Override
080    protected void followDetailsLink(final JsonRepresentation detailsLink) {
081        final ObjectPropertyReprRenderer renderer = new ObjectPropertyReprRenderer(getRendererContext(), getLinkFollowSpecs(), null, JsonRepresentation.newMap());
082        renderer.with(new ObjectAndProperty(objectAdapter, objectMember)).asFollowed();
083        detailsLink.mapPut("value", renderer.render());
084    }
085
086    // ///////////////////////////////////////////////////
087    // mutators
088    // ///////////////////////////////////////////////////
089
090    @Override
091    protected void addMutatorsIfEnabled() {
092        if (usability().isVetoed()) {
093            return;
094        }
095        final Map<String, MutatorSpec> mutators = memberType.getMutators();
096        for (final String mutator : mutators.keySet()) {
097            final MutatorSpec mutatorSpec = mutators.get(mutator);
098            addLinkFor(mutatorSpec);
099        }
100        return;
101    }
102
103    // ///////////////////////////////////////////////////
104    // choices
105    // ///////////////////////////////////////////////////
106
107    private ObjectPropertyReprRenderer addChoices() {
108        final Object propertyChoices = propertyChoices();
109        if (propertyChoices != null) {
110            representation.mapPut("choices", propertyChoices);
111        }
112        return this;
113    }
114
115    private Object propertyChoices() {
116        final ObjectAdapter[] choiceAdapters = objectMember.getChoices(objectAdapter);
117        if (choiceAdapters == null || choiceAdapters.length == 0) {
118            return null;
119        }
120        final List<Object> list = Lists.newArrayList();
121        for (final ObjectAdapter choiceAdapter : choiceAdapters) {
122            // REVIEW: previously was using the spec of the member, but think instead it should be the spec of the adapter itself
123            // final ObjectSpecification choiceSpec = objectMember.getSpecification();
124            
125            // REVIEW: check that it works for ToDoItem$Category, though...
126            final ObjectSpecification choiceSpec = objectAdapter.getSpecification();
127            list.add(DomainObjectReprRenderer.valueOrRef(rendererContext, choiceAdapter, choiceSpec));
128        }
129        return list;
130    }
131
132    // ///////////////////////////////////////////////////
133    // extensions and links
134    // ///////////////////////////////////////////////////
135
136    @Override
137    protected void addLinksToFormalDomainModel() {
138        getLinks().arrayAdd(PropertyDescriptionReprRenderer.newLinkToBuilder(getRendererContext(), Rel.DESCRIBEDBY, objectAdapter.getSpecification(), objectMember).build());
139    }
140
141    @Override
142    protected void addLinksIsisProprietary() {
143        // none
144    }
145
146    @Override
147    protected void putExtensionsIsisProprietary() {
148        // none
149    }
150
151}