Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
grails.rest.render.util.AbstractLinkingRenderer.groovy Maven / Gradle / Ivy
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package grails.rest.render.util
import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.MessageSource
import org.springframework.http.HttpMethod
import grails.core.support.proxy.DefaultProxyHandler
import grails.core.support.proxy.EntityProxyHandler
import grails.core.support.proxy.ProxyHandler
import grails.rest.Link
import grails.rest.render.AbstractIncludeExcludeRenderer
import grails.rest.render.RenderContext
import grails.rest.render.Renderer
import grails.rest.render.RendererRegistry
import grails.util.Environment
import grails.util.GrailsWebUtil
import grails.web.mapping.LinkGenerator
import grails.web.mime.MimeType
import org.grails.core.artefact.DomainClassArtefactHandler
import org.grails.datastore.mapping.model.MappingContext
import org.grails.datastore.mapping.model.PersistentEntity
import org.grails.datastore.mapping.model.PersistentProperty
import org.grails.datastore.mapping.model.types.Association
import org.grails.datastore.mapping.model.types.Basic
import org.grails.datastore.mapping.model.types.Embedded
import org.grails.datastore.mapping.model.types.ToOne
import org.grails.gsp.io.GroovyPageScriptSource
import org.grails.plugins.web.rest.render.html.DefaultHtmlRenderer
import org.grails.web.gsp.io.GrailsConventionGroovyPageLocator
/**
* Abstract base class for HAL renderers
*
* @author Graeme Rocher
* @since 2.3
*/
@CompileStatic
abstract class AbstractLinkingRenderer extends AbstractIncludeExcludeRenderer {
protected static final List DEFAULT_EXCLUDES = ['metaClass', 'class']
public static final String RELATIONSHIP_SELF = 'self'
public static final String HREF_ATTRIBUTE = 'href'
public static final String TITLE_ATTRIBUTE = 'title'
public static final String HREFLANG_ATTRIBUTE = 'hreflang'
public static final String TYPE_ATTRIBUTE = 'type'
public static final String TEMPLATED_ATTRIBUTE = 'templated'
public static final String DEPRECATED_ATTRIBUTE = 'deprecated'
@Autowired
MessageSource messageSource
@Autowired
LinkGenerator linkGenerator
@Autowired
@Qualifier('grailsDomainClassMappingContext')
MappingContext mappingContext
@Autowired
RendererRegistry rendererRegistry
@Autowired(required = false)
ProxyHandler proxyHandler = new DefaultProxyHandler()
@Autowired(required = false)
GrailsConventionGroovyPageLocator groovyPageLocator
boolean prettyPrint = Environment.isDevelopmentMode()
boolean absoluteLinks = true
String encoding = GrailsWebUtil.DEFAULT_ENCODING
AbstractLinkingRenderer(Class targetType, MimeType mimeType) {
super(targetType, mimeType)
}
AbstractLinkingRenderer(Class targetType, MimeType[] mimeTypes) {
super(targetType, mimeTypes)
}
@Override
void render(T object, RenderContext context) {
MimeType mimeType = context.acceptMimeType ?: getMimeTypes()[0]
context.setContentType(GrailsWebUtil.getContentType(mimeType.name, encoding))
String viewName = context.viewName ?: context.defaultViewName
GroovyPageScriptSource view = groovyPageLocator?.findViewForFormat(context.controllerName, viewName, mimeType.extension)
if (view) {
// if a view is provided, we use the HTML renderer to return an appropriate model to the view
Renderer htmlRenderer = rendererRegistry?.findRenderer(MimeType.HTML, object)
if (htmlRenderer == null) {
htmlRenderer = new DefaultHtmlRenderer(targetType)
}
htmlRenderer.render((Object) object, context)
}
else {
renderInternal(object, context)
}
}
abstract void renderInternal(T object, RenderContext context)
protected boolean isDomainResource(Class clazz) {
if (mappingContext != null) {
return mappingContext.isPersistentEntity(clazz)
}
DomainClassArtefactHandler.isDomainClass(clazz, true)
}
protected String getLinkTitle(PersistentEntity entity, Locale locale) {
String propertyName = entity.decapitalizedName
messageSource.getMessage("resource.${propertyName}.href.title", [propertyName, entity.name] as Object[], '', locale)
}
protected String getResourceTitle(String uri, Locale locale) {
if (uri.startsWith('/')) {
uri = uri.substring(1)
}
if (uri.endsWith('/')) {
uri = uri.substring(0, uri.length() - 1)
}
uri = uri.replace('/', '.')
messageSource.getMessage("resource.${uri}.href.title", [uri] as Object[], '', locale)
}
@CompileStatic(TypeCheckingMode.SKIP)
Collection getLinksForObject(def object) {
if (object.respondsTo('links')) {
return object.links()
}
Collections.emptyList()
}
protected Map writeAssociationLinks(RenderContext context, object, Locale locale, writer,
PersistentEntity entity, MetaClass metaClass) {
writeExtraLinks(object, locale, writer)
Map associationMap = [:]
for (Association a in entity.associations) {
String propertyName = a.name
if (!shouldIncludeProperty(context, object, propertyName)) {
continue
}
PersistentEntity associatedEntity = a.associatedEntity
if (!associatedEntity) {
continue
}
if (proxyHandler.isInitialized(object, propertyName)) {
if (a instanceof ToOne) {
Object value = proxyHandler.unwrapIfProxy(metaClass.getProperty(object, propertyName))
if (a instanceof Embedded) {
// no links for embedded
associationMap[a] = value
}
else if (value != null) {
String href = linkGenerator.link(resource: value, method: HttpMethod.GET, absolute: absoluteLinks)
String associationTitle = getLinkTitle(associatedEntity, locale)
Link link = new Link(propertyName, href)
link.title = associationTitle
link.hreflang = locale
writeLink(link, locale, writer)
associationMap[a] = value
}
}
else if (!(a instanceof Basic)) {
associationMap[a] = metaClass.getProperty(object, propertyName)
}
}
else if ((a instanceof ToOne) && (proxyHandler instanceof EntityProxyHandler)) {
if (associatedEntity) {
Object proxy = mappingContext.getEntityReflector(a.owner).getProperty(object, propertyName)
Object id = proxyHandler.getProxyIdentifier(proxy)
String href = linkGenerator.link(resource: associatedEntity.decapitalizedName, id: id,
method: HttpMethod.GET, absolute: absoluteLinks)
String associationTitle = getLinkTitle(associatedEntity, locale)
Link link = new Link(propertyName, href)
link.title = associationTitle
link.hreflang = locale
writeLink(link, locale, writer)
}
}
}
associationMap
}
protected void writeExtraLinks(object, Locale locale, writer) {
Collection extraLinks = getLinksForObject(object)
for (Link l in extraLinks) {
writeLink(l, locale, writer)
}
}
/**
* Writes a domain instance
*
* @param clazz The class
* @param object The object
* @param writer The writer
* @return Any associations embedded within the object
*/
protected void writeDomain(RenderContext context, MetaClass metaClass, PersistentEntity entity, Object object, writer) {
if (entity) {
for (PersistentProperty p in entity.persistentProperties) {
String propertyName = p.name
if (!shouldIncludeProperty(context, object, propertyName)) {
continue
}
if ((p instanceof Basic) || !(p instanceof Association)) {
Object value = metaClass.getProperty(object, propertyName)
if (value != null) {
writeDomainProperty(value, propertyName, writer)
}
}
}
}
}
protected abstract void writeLink(Link link, Locale locale, writerObject)
protected abstract void writeDomainProperty(value, String propertyName, writer)
}