001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019package org.apache.isis.viewer.restfulobjects.rendering.domaintypes;
020
021import java.util.stream.Stream;
022
023import com.fasterxml.jackson.databind.node.NullNode;
024
025import org.apache.isis.commons.internal.base._Strings;
026import org.apache.isis.core.metamodel.spec.ObjectSpecification;
027import org.apache.isis.core.metamodel.spec.feature.Contributed;
028import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
029import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
030import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
031import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
032import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
033import org.apache.isis.viewer.restfulobjects.applib.Rel;
034import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
035import org.apache.isis.viewer.restfulobjects.rendering.IResourceContext;
036import org.apache.isis.viewer.restfulobjects.rendering.LinkBuilder;
037import org.apache.isis.viewer.restfulobjects.rendering.LinkFollowSpecs;
038import org.apache.isis.viewer.restfulobjects.rendering.ReprRendererAbstract;
039
040public class DomainTypeReprRenderer extends ReprRendererAbstract<DomainTypeReprRenderer, ObjectSpecification> {
041
042    public static LinkBuilder newLinkToBuilder(final IResourceContext resourceContext, final Rel rel, final ObjectSpecification objectSpec) {
043        final String typeFullName = objectSpec.getSpecId().asString();
044        final String url = String.format("domain-types/%s", typeFullName);
045        return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.DOMAIN_TYPE, url);
046    }
047
048    public static LinkBuilder newLinkToLayoutBuilder(
049            final IResourceContext resourceContext,
050            final ObjectSpecification objectSpec) {
051        final Rel rel = Rel.LAYOUT;
052        final String typeFullName = objectSpec.getSpecId().asString();
053        final String url = String.format("domain-types/%s/layout", typeFullName);
054        return LinkBuilder.newBuilder(resourceContext, rel.getName(), RepresentationType.LAYOUT, url);
055    }
056
057    private ObjectSpecification objectSpecification;
058
059    public DomainTypeReprRenderer(final IResourceContext resourceContext, final LinkFollowSpecs linkFollower, final JsonRepresentation representation) {
060        super(resourceContext, linkFollower, RepresentationType.DOMAIN_TYPE, representation);
061    }
062
063    @Override
064    public DomainTypeReprRenderer with(final ObjectSpecification objectSpecification) {
065        this.objectSpecification = objectSpecification;
066        return cast(this);
067    }
068
069    @Override
070    public JsonRepresentation render() {
071
072        if (objectSpecification == null) {
073            throw new IllegalStateException("ObjectSpecification not specified");
074        }
075
076        // self
077        if (includesSelf) {
078            final JsonRepresentation selfLink = newLinkToBuilder(getResourceContext(), Rel.SELF, objectSpecification).build();
079            getLinks().arrayAdd(selfLink);
080
081            final JsonRepresentation layoutLink = newLinkToLayoutBuilder(getResourceContext(), objectSpecification).build();
082            getLinks().arrayAdd(layoutLink);
083        }
084
085        representation.mapPut("canonicalName", objectSpecification.getFullIdentifier());
086        addMembers();
087
088        addTypeActions();
089
090        putExtensionsNames();
091        putExtensionsDescriptionIfAvailable();
092        putExtensionsIfService();
093
094        return representation;
095    }
096
097    private void addMembers() {
098        final JsonRepresentation membersList = JsonRepresentation.newArray();
099        representation.mapPut("members", membersList);
100        final Stream<ObjectAssociation> associations = objectSpecification.streamAssociations(Contributed.EXCLUDED);
101
102        associations.forEach(association->{
103            if (association.isOneToOneAssociation()) {
104                final OneToOneAssociation property = (OneToOneAssociation) association;
105                final LinkBuilder linkBuilder = PropertyDescriptionReprRenderer.newLinkToBuilder(getResourceContext(), Rel.PROPERTY, objectSpecification, property);
106                membersList.arrayAdd(linkBuilder.build());
107            } else if (association.isOneToManyAssociation()) {
108                final OneToManyAssociation collection = (OneToManyAssociation) association;
109                final LinkBuilder linkBuilder = CollectionDescriptionReprRenderer.newLinkToBuilder(getResourceContext(), Rel.PROPERTY, objectSpecification, collection);
110                membersList.arrayAdd(linkBuilder.build());
111            }
112        });
113
114        final Stream<ObjectAction> actions = objectSpecification.streamObjectActions(Contributed.INCLUDED);
115
116        actions.forEach(action->{
117            final LinkBuilder linkBuilder = ActionDescriptionReprRenderer
118                    .newLinkToBuilder(getResourceContext(), Rel.ACTION, objectSpecification, action);
119            membersList.arrayAdd(linkBuilder.build());            
120        });
121
122    }
123
124    private JsonRepresentation getTypeActions() {
125        JsonRepresentation typeActions = representation.getArray("typeActions");
126        if (typeActions == null) {
127            typeActions = JsonRepresentation.newArray();
128            representation.mapPut("typeActions", typeActions);
129        }
130        return typeActions;
131    }
132
133    private void addTypeActions() {
134        getTypeActions().arrayAdd(linkToIsSubtypeOf());
135        getTypeActions().arrayAdd(linkToIsSupertypeOf());
136    }
137
138    private JsonRepresentation linkToIsSubtypeOf() {
139        final String url = "domain-types/" + objectSpecification.getSpecId().asString() + "/type-actions/isSubtypeOf/invoke";
140
141        final LinkBuilder linkBuilder = LinkBuilder.newBuilder(getResourceContext(), Rel.INVOKE.andParam("typeaction", "isSubtypeOf"), RepresentationType.TYPE_ACTION_RESULT, url);
142        final JsonRepresentation arguments = argumentsTo(getResourceContext(), "supertype", null);
143        final JsonRepresentation link = linkBuilder.withArguments(arguments).build();
144        return link;
145    }
146
147    private JsonRepresentation linkToIsSupertypeOf() {
148        final String url = "domain-types/" + objectSpecification.getSpecId().asString() + "/type-actions/isSupertypeOf/invoke";
149
150        final LinkBuilder linkBuilder = LinkBuilder.newBuilder(getResourceContext(), Rel.INVOKE.andParam("typeaction", "isSupertypeOf"), RepresentationType.TYPE_ACTION_RESULT, url);
151        final JsonRepresentation arguments = argumentsTo(getResourceContext(), "subtype", null);
152        final JsonRepresentation link = linkBuilder.withArguments(arguments).build();
153        return link;
154    }
155
156    public static JsonRepresentation argumentsTo(final IResourceContext resourceContext, final String paramName, final ObjectSpecification objectSpec) {
157        final JsonRepresentation arguments = JsonRepresentation.newMap();
158        final JsonRepresentation link = JsonRepresentation.newMap();
159        arguments.mapPut(paramName, link);
160        if (objectSpec != null) {
161            link.mapPut("href", resourceContext.urlFor("domain-types/" + objectSpec.getSpecId().asString()));
162        } else {
163            link.mapPut("href", NullNode.instance);
164        }
165        return arguments;
166    }
167
168    protected void putExtensionsNames() {
169        final String singularName = objectSpecification.getSingularName();
170        getExtensions().mapPut("friendlyName", singularName);
171
172        final String pluralName = objectSpecification.getPluralName();
173        getExtensions().mapPut("pluralName", pluralName);
174    }
175
176    protected void putExtensionsDescriptionIfAvailable() {
177        final String description = objectSpecification.getDescription();
178        if (!_Strings.isNullOrEmpty(description)) {
179            getExtensions().mapPut("description", description);
180        }
181    }
182
183    protected void putExtensionsIfService() {
184        getExtensions().mapPut("isService", objectSpecification.isManagedBean());
185    }
186
187}