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.applib.annotation.Render.Type; 024import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 025import org.apache.isis.core.metamodel.facets.members.resolve.RenderFacet; 026import org.apache.isis.core.metamodel.facets.object.title.TitleFacet; 027import org.apache.isis.core.metamodel.facets.object.value.ValueFacet; 028import org.apache.isis.core.metamodel.spec.ObjectSpecification; 029import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; 030import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 031import org.apache.isis.viewer.restfulobjects.applib.Rel; 032import org.apache.isis.viewer.restfulobjects.applib.RepresentationType; 033import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder; 034import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs; 035import org.apache.isis.viewer.restfulobjects.rendering.RendererContext; 036import org.apache.isis.viewer.restfulobjects.rendering.domaintypes.PropertyDescriptionReprRenderer; 037import org.codehaus.jackson.node.NullNode; 038 039import com.google.common.collect.Lists; 040 041public class ObjectPropertyReprRenderer extends AbstractObjectMemberReprRenderer<ObjectPropertyReprRenderer, OneToOneAssociation> { 042 043 public ObjectPropertyReprRenderer(final RendererContext resourceContext, final LinkFollowSpecs linkFollower, final String propertyId, final JsonRepresentation representation) { 044 super(resourceContext, linkFollower, propertyId, RepresentationType.OBJECT_PROPERTY, representation, Where.OBJECT_FORMS); 045 } 046 047 @Override 048 public JsonRepresentation render() { 049 050 renderMemberContent(); 051 addValue(); 052 053 putDisabledReasonIfDisabled(); 054 055 if (mode.isStandalone() || mode.isMutated()) { 056 addChoices(); 057 addExtensionsIsisProprietaryChangedObjects(); 058 } 059 060 return representation; 061 } 062 063 // /////////////////////////////////////////////////// 064 // value 065 // /////////////////////////////////////////////////// 066 067 private void addValue() { 068 final ObjectAdapter valueAdapter = objectMember.get(objectAdapter); 069 070 // use the runtime type if we have a value, else the compile time type of the member otherwise 071 final ObjectSpecification spec = valueAdapter != null? valueAdapter.getSpecification(): objectMember.getSpecification(); 072 073 final ValueFacet valueFacet = spec.getFacet(ValueFacet.class); 074 if (valueFacet != null) { 075 JsonValueEncoder.appendValueAndFormat(spec, valueAdapter, representation); 076 return; 077 } 078 079 final RenderFacet renderFacet = objectMember.getFacet(RenderFacet.class); 080 boolean eagerlyRender = renderFacet != null && renderFacet.value() == Type.EAGERLY && rendererContext.canEagerlyRender(valueAdapter); 081 082 if(valueAdapter == null) { 083 representation.mapPut("value", NullNode.getInstance()); 084 } else { 085 final TitleFacet titleFacet = spec.getFacet(TitleFacet.class); 086 final String title = titleFacet.title(valueAdapter, rendererContext.getLocalization()); 087 088 final LinkBuilder valueLinkBuilder = DomainObjectReprRenderer.newLinkToBuilder(rendererContext, Rel.VALUE, valueAdapter).withTitle(title); 089 if(eagerlyRender) { 090 final DomainObjectReprRenderer renderer = new DomainObjectReprRenderer(rendererContext, getLinkFollowSpecs(), JsonRepresentation.newMap()); 091 renderer.with(valueAdapter); 092 if(mode.isEventSerialization()) { 093 renderer.asEventSerialization(); 094 } 095 096 valueLinkBuilder.withValue(renderer.render()); 097 } 098 099 representation.mapPut("value", valueLinkBuilder.build()); 100 } 101 } 102 103 104 105 // /////////////////////////////////////////////////// 106 // details link 107 // /////////////////////////////////////////////////// 108 109 /** 110 * Mandatory hook method to support x-ro-follow-links 111 */ 112 @Override 113 protected void followDetailsLink(final JsonRepresentation detailsLink) { 114 final ObjectPropertyReprRenderer renderer = new ObjectPropertyReprRenderer(getRendererContext(), getLinkFollowSpecs(), null, JsonRepresentation.newMap()); 115 renderer.with(new ObjectAndProperty(objectAdapter, objectMember)).asFollowed(); 116 detailsLink.mapPut("value", renderer.render()); 117 } 118 119 // /////////////////////////////////////////////////// 120 // mutators 121 // /////////////////////////////////////////////////// 122 123 @Override 124 protected void addMutatorsIfEnabled() { 125 if (usability().isVetoed()) { 126 return; 127 } 128 final Map<String, MutatorSpec> mutators = memberType.getMutators(); 129 for (final String mutator : mutators.keySet()) { 130 final MutatorSpec mutatorSpec = mutators.get(mutator); 131 addLinkFor(mutatorSpec); 132 } 133 return; 134 } 135 136 // /////////////////////////////////////////////////// 137 // choices 138 // /////////////////////////////////////////////////// 139 140 private ObjectPropertyReprRenderer addChoices() { 141 final Object propertyChoices = propertyChoices(); 142 if (propertyChoices != null) { 143 representation.mapPut("choices", propertyChoices); 144 } 145 return this; 146 } 147 148 private Object propertyChoices() { 149 final ObjectAdapter[] choiceAdapters = objectMember.getChoices(objectAdapter); 150 if (choiceAdapters == null || choiceAdapters.length == 0) { 151 return null; 152 } 153 final List<Object> list = Lists.newArrayList(); 154 for (final ObjectAdapter choiceAdapter : choiceAdapters) { 155 // REVIEW: previously was using the spec of the member, but think instead it should be the spec of the adapter itself 156 // final ObjectSpecification choiceSpec = objectMember.getSpecification(); 157 158 // REVIEW: check that it works for ToDoItem$Category, though... 159 final ObjectSpecification choiceSpec = objectAdapter.getSpecification(); 160 list.add(DomainObjectReprRenderer.valueOrRef(rendererContext, choiceAdapter, choiceSpec)); 161 } 162 return list; 163 } 164 165 // /////////////////////////////////////////////////// 166 // extensions and links 167 // /////////////////////////////////////////////////// 168 169 @Override 170 protected void addLinksToFormalDomainModel() { 171 getLinks().arrayAdd(PropertyDescriptionReprRenderer.newLinkToBuilder(getRendererContext(), Rel.DESCRIBEDBY, objectAdapter.getSpecification(), objectMember).build()); 172 } 173 174 @Override 175 protected void addLinksIsisProprietary() { 176 // none 177 } 178 179 @Override 180 protected void putExtensionsIsisProprietary() { 181 // none 182 } 183 184}