io.kestros.commons.structuredslingmodels.utils.SlingModelUtils Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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 io.kestros.commons.structuredslingmodels.utils;
import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import io.kestros.commons.structuredslingmodels.BaseResource;
import io.kestros.commons.structuredslingmodels.BaseSlingModel;
import io.kestros.commons.structuredslingmodels.exceptions.ChildResourceNotFoundException;
import io.kestros.commons.structuredslingmodels.exceptions.InvalidResourceTypeException;
import io.kestros.commons.structuredslingmodels.exceptions.MatchingResourceTypeNotFoundException;
import io.kestros.commons.structuredslingmodels.exceptions.ModelAdaptionException;
import io.kestros.commons.structuredslingmodels.exceptions.NoParentResourceException;
import io.kestros.commons.structuredslingmodels.exceptions.NoValidAncestorException;
import io.kestros.commons.structuredslingmodels.exceptions.ResourceNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.JcrConstants;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.factory.ModelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Util Class for getting Sling Models that extend BaseResource. Looks to the Model's {@link Model}
* resourceType value to determine if the Resource is valid for the specified Class.
*/
public final class SlingModelUtils {
private static final Logger LOG = LoggerFactory.getLogger(SlingModelUtils.class);
private static final String PREFIX_LIBS = "/libs/";
private static final String PREFIX_APPS = "/apps/";
private static final String RESOURCE_TYPE_SYNTHETIC = "sling:syntheticResourceProviderResource";
/**
* Default Constructor.
*/
private SlingModelUtils() {
}
/**
* Adapts the passed Resource to the specified type. The type must extend BaseResource.
*
* @param resource Resource to adapt.
* @param type Class to adapt the Resource to. Class must extend BaseResource and have the
* @param Class to adapt the Resource to. Class must extend BaseResource and have the
* @return The current Resource, adapted the specified type.
* @throws InvalidResourceTypeException thrown when the Resource cannot be adapted to the
* specified type, due to a mismatch between the resourceType of the Resource, and the
* resourceType value of the type's {@link Model} annotation.
*/
@Nonnull
public static T adaptTo(@Nonnull final Resource resource,
@Nonnull final Class type) throws InvalidResourceTypeException {
final String resourcePath = resource.getPath();
if (isValidResourceType(resource, type)) {
final T model = resource.adaptTo(type);
if (model != null) {
return model;
} else {
LOG.debug("Unable to adapt {} to {} due to null response. "
+ " checking jcr:content resource, if it exists.", resourcePath,
type.getSimpleName());
}
}
LOG.trace("Unable to adapt {} to {} due InvalidResourceType. Attempting to adapt jcr:content "
+ "resource, if it exists", resourcePath, type.getSimpleName());
try {
// Calling getChildAsType to catch exception if jcr:content is not found.
getChildAsType(JCR_CONTENT, resource, type);
final T model = resource.adaptTo(type);
if (model != null) {
return model;
} else {
LOG.trace("Unable to adapt {} jcr:content to {} due to null response.", resourcePath,
type.getSimpleName());
}
} catch (final ChildResourceNotFoundException exception) {
LOG.trace(
"Unable to find jcr:content resource to fall back to while adapting {} to {}, throwing "
+ "InvalidResourceTypeException", resourcePath, type.getSimpleName());
}
throw new InvalidResourceTypeException(resourcePath, type);
}
/**
* This method is functionally the same as {@link #adaptTo(Resource, Class)} but accepts {@link
* BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to adapt.
* @param type Class to adapt the Resource to. Class must extend BaseResource and have the
* @param Class to adapt the Resource to. Class must extend BaseResource and have the
* @return The current Resource, adapted the specified type.
* @throws InvalidResourceTypeException thrown when the Resource cannot be adapted to the
* specified type, due to a mismatch between the resourceType of the Resource, and the
* resourceType value of the type's {@link Model} annotation.
*/
@Nonnull
public static T adaptTo(@Nonnull final BaseResource baseResource,
@Nonnull final Class type) throws InvalidResourceTypeException {
return adaptTo(baseResource.getResource(), type);
}
/**
* Adapts the passed Resource to a BaseResource Model. Use this instead of adaptTo when adapting
* to BaseResource to avoid impossible InvalidResourceType exceptions.
*
* @param resource Resource to adapt.
* @return The current Resource, adapted to a BaseResource Model.
* @throws IllegalStateException BaseResource should never be considered invalid.
*/
@Nonnull
public static BaseResource adaptToBaseResource(@Nonnull final Resource resource) {
try {
return adaptTo(resource, BaseResource.class);
} catch (final InvalidResourceTypeException exception) {
throw new IllegalStateException();
}
}
/**
* This method is functionally the same as {@link #adaptToBaseResource(Resource)} but accepts
* {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to adapt.
* @return The current Resource, adapted to a BaseResource Model.
*/
@Nonnull
public static BaseResource adaptToBaseResource(@Nonnull final BaseResource baseResource) {
return adaptToBaseResource(baseResource.getResource());
}
/**
* The specified child Resource adapted to the specified type, if it is valid. Will *
* automatically check the jcr:content resource is no immediate child is found ( and the *
* jcr:content resource is).
*
* @param childName Name of child Resource to retrieve.
* @param resource Resource to retrieve child from.
* @param type Class to adapt the child to. Class must extend BaseResource and have the {@link
* Model} annotation, with the resourceType value set.
* @param Class to adapt the child to. Class must extend BaseResource and have the {@link
* Model} annotation, with the resourceType value set.
* @return The specified child Resource adapted to the specified type, if it is valid. Will
* automatically check the jcr:content resource is no immediate child is found ( and the
* jcr:content resource is).
* @throws InvalidResourceTypeException thrown when the child Resource cannot be adapted to
* the specified type, due to a mismatch between the resourceType of the Resource, and the
* resourceType value of the type's {@link Model} annotation.
* @throws ChildResourceNotFoundException thrown when the specified childBaseResource to
* retrieve child of. Resource does not exist, or cannot be found (possibly due to
* permissions).
*/
@Nonnull
public static T getChildAsType(@Nonnull final String childName,
@Nonnull final Resource resource, @Nonnull final Class type)
throws InvalidResourceTypeException, ChildResourceNotFoundException {
return adaptTo(getChildAsBaseResource(childName, resource), type);
}
/**
* This method is functionally the same as {@link #getChildAsType(String, Resource, Class)} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param childName Name of child Resource to retrieve.
* @param baseResource Resource to retrieve child from.
* @param type Class to adapt the child to. Class must extend BaseResource and have the {@link
* Model} annotation, with the resourceType value set.
* @param Class to adapt the child to. Class must extend BaseResource and have the {@link
* Model} annotation, with the resourceType value set.
* @return The specified child Resource adapted to the specified type, if it is valid. Will
* automatically check the jcr:content resource is no immediate child is found ( and the
* jcr:content resource is).
* @throws InvalidResourceTypeException thrown when the child Resource cannot be adapted to
* the specified type, due to a mismatch between the resourceType of the Resource, and the
* resourceType value of the type's {@link Model} annotation.
* @throws ChildResourceNotFoundException thrown when the specified childBaseResource to
* retrieve child of. Resource does not exist, or cannot be found (possibly due to
* permissions).
*/
@Nonnull
public static T getChildAsType(@Nonnull final String childName,
@Nonnull final BaseResource baseResource, @Nonnull final Class type)
throws InvalidResourceTypeException, ChildResourceNotFoundException {
return getChildAsType(childName, baseResource.getResource(), type);
}
/**
* Specified child Resource, as a BaseResource.
*
* @param childName name of the child Resource to return.
* @param resource Resource to retrieve the child from.
* @return Specified child Resource, as a BaseResource.
* @throws ChildResourceNotFoundException No resource with the specified name is found.
*/
@Nonnull
public static BaseResource getChildAsBaseResource(@Nonnull final String childName,
@Nonnull final Resource resource) throws ChildResourceNotFoundException {
if (StringUtils.isNotBlank(childName)) {
final Resource child = resource.getChild(childName);
if (child != null) {
return adaptToBaseResource(child);
} else {
final Resource jcrContent = resource.getChild(JCR_CONTENT);
if (jcrContent != null) {
return getChildAsBaseResource(childName, jcrContent);
}
}
throw new ChildResourceNotFoundException(childName, resource.getPath());
} else {
throw new ChildResourceNotFoundException(childName, resource.getPath(),
"Child name not specified.");
}
}
/**
* This method is functionally the same as {@link #getChildAsBaseResource(String, Resource)} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param childName name of the child Resource to return.
* @param baseResource Resource to retrieve the child from.
* @return Specified child Resource, as a BaseResource.
* @throws ChildResourceNotFoundException No resource with the specified name is found.
*/
@Nonnull
public static BaseResource getChildAsBaseResource(@Nonnull final String childName,
@Nonnull final BaseResource baseResource) throws ChildResourceNotFoundException {
return getChildAsBaseResource(childName, baseResource.getResource());
}
/**
* List of all children, adapted to BaseResource.
*
* @param resource Resource to retrieve children from.
* @return List of all children, adapted to BaseResource.
*/
@Nonnull
public static List getChildrenAsBaseResource(@Nonnull final Resource resource) {
final List children = new ArrayList<>();
for (final Resource child : resource.getChildren()) {
children.add(adaptToBaseResource(child));
}
return children;
}
/**
* This method is functionally the same as {@link #getChildrenAsBaseResource(Resource)} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param resource Resource to retrieve children from.
* @return List of all children, adapted to BaseResource.
*/
@Nonnull
public static List getChildrenAsBaseResource(@Nonnull final BaseResource resource) {
return SlingModelUtils.getChildrenAsBaseResource(resource.getResource());
}
/**
* List of all valid children, adapted to the specified type. Resources that fail adaption * will
* not be added to the List.
*
* @param resource Resource to retrieve children from.
* @param type Class to adapt the children to. Class must extend BaseResource and have the
* @param Class to adapt the children to. Class must extend BaseResource and have the
* @return List of all valid children, adapted to the specified type. Resources that fail adaption
* will not be added to the List.
*/
@Nonnull
public static List getChildrenOfType(@Nonnull final Resource resource,
@Nonnull final Class type) {
final List children = new ArrayList<>();
for (final Resource child : resource.getChildren()) {
try {
children.add(adaptTo(child, type));
} catch (final InvalidResourceTypeException exception) {
LOG.debug("Unable to adapt resource {} to {} due to "
+ "InvalidResourceType while getting children" + " of {}", child.getPath(),
type.getSimpleName(), resource.getPath());
}
}
return children;
}
/**
* This method is functionally the same as {@link #getChildrenOfType(Resource, Class)} )} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to retrieve children from.
* @param type Class to adapt the children to. Class must extend BaseResource and have the
* @param Class to adapt the children to. Class must extend BaseResource and have the
* @return List of all valid children, adapted to the specified type. Resources that fail adaption
* will not be added to the List.
*/
@Nonnull
public static List getChildrenOfType(
@Nonnull final BaseResource baseResource, @Nonnull final Class type) {
return getChildrenOfType(baseResource.getResource(), type);
}
/**
* Retrieves a filtered list of children, of a specified SlingModel type.
*
* @param resource Resource to retrieve children from.
* @param allowedChildNames List of names that are allowed.
* @param type Class to adapt the children to. Class must extend BaseResource and have the
* @param Class to adapt the children to. Class must extend BaseResource and have the
* @return List of all allowed and valid children, adapted to the specified type. Resources that
* fail adaption will not be added to the List.
*/
@Nonnull
public static List getChildrenOfType(@Nonnull final Resource resource,
@Nonnull final List allowedChildNames, @Nonnull final Class type) {
final List filteredChildren = new ArrayList<>();
for (final T child : getChildrenOfType(resource, type)) {
if (allowedChildNames.contains(child.getName())) {
filteredChildren.add(child);
}
}
return filteredChildren;
}
/**
* This method is functionally the same as {@link #getChildrenOfType(Resource, List, Class)} )}
* but accepts {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to retrieve children from.
* @param allowedChildNames List of names that are allowed.
* @param type Class to adapt the children to. Class must extend BaseResource and have the
* @param Class to adapt the children to. Class must extend BaseResource and have the
* @return List of all allowed and valid children, adapted to the specified type. Resources that
* fail adaption will not be added to the List.
*/
@Nonnull
public static List getChildrenOfType(
@Nonnull final BaseResource baseResource, @Nonnull final List allowedChildNames,
@Nonnull final Class type) {
return getChildrenOfType(baseResource.getResource(), allowedChildNames, type);
}
/**
* Retrieves a specified Resource as a BaseResource.
*
* @param resourcePath Path to the resource to retrieve.
* @param resolver ResourceResolver
* @return Specified Resource as a BaseResource.
* @throws ResourceNotFoundException Throw when the specified Resource cannot be found.
* Resource does not exist, or cannot be found (possibly due to permissions).
*/
@Nonnull
public static BaseResource getResourceAsBaseResource(@Nonnull final String resourcePath,
@Nonnull final ResourceResolver resolver) throws ResourceNotFoundException {
if (StringUtils.isNotEmpty(resourcePath)) {
Resource resource = resolver.getResource(resourcePath);
if (resource != null) {
if (RESOURCE_TYPE_SYNTHETIC.equals(resource.getResourceType())) {
resource = resolver.getResource(PREFIX_APPS + resourcePath);
if (resource == null || RESOURCE_TYPE_SYNTHETIC.equals(resource.getResourceType())) {
resource = resolver.getResource(PREFIX_LIBS + resourcePath);
}
if (resource == null || RESOURCE_TYPE_SYNTHETIC.equals(resource.getResourceType())) {
throw new ResourceNotFoundException(resourcePath);
}
}
return adaptToBaseResource(resource);
}
throw new ResourceNotFoundException(resourcePath);
}
throw new ResourceNotFoundException(resourcePath, "Resource path not specified.");
}
/**
* The specified Resource, adapted to the specified type.
*
* @param resourcePath Path to a Resource
* @param resolver ResourceResolver
* @param type Class to adapt the specified Resource to. Class must extend BaseResource and
* have the {@link Model} annotation, with the resourceType value set.
* @param Class to adapt the specified Resource to. Class must extend BaseResource and
* have the {@link Model} annotation, with the resourceType value set.
* @return The specified Resource, adapted to the specified type.
* @throws InvalidResourceTypeException thrown when the specified Resource is found, but
* cannot be adapted to the specified type, due to a mismatch between the resourceType of the
* Resource, and the resourceType value of the type's {@link Model} annotation.
* @throws ResourceNotFoundException Throw when the specified Resource cannot be found.
* Resource does not exist, or cannot be found (possibly due to permissions).
*/
@Nonnull
public static T getResourceAsType(@Nonnull final String resourcePath,
@Nonnull final ResourceResolver resolver, @Nonnull final Class type)
throws InvalidResourceTypeException, ResourceNotFoundException {
return adaptTo(getResourceAsBaseResource(resourcePath, resolver), type);
}
/**
* Returns a List of Resources paths adapted to the specified SlingModel type.
*
* @param resourcePaths Resources to lookup and adapt.
* @param resolver Resource Resolver
* @param type Class to adapt the specified Resource to. Class must extend BaseResource and
* have the {@link Model} annotation, with the resourceType value set.
* @param Class to adapt the specified Resource to. Class must extend BaseResource and
* have the {@link Model} annotation, with the resourceType value set.
* @return The requested resources, adapted to the specified type.
*/
public static List getResourcesAsType(
@Nonnull final List resourcePaths, @Nonnull final ResourceResolver resolver,
@Nonnull final Class type) {
final List models = new ArrayList<>();
for (final String path : resourcePaths) {
try {
models.add(getResourceAsType(path, resolver, type));
} catch (final InvalidResourceTypeException exception) {
LOG.warn("Unable to adapt {} to {} while adapting list of paths to {} due to "
+ "InvalidResourceTypeException", path, type.getSimpleName(),
type.getSimpleName());
} catch (final ResourceNotFoundException exception) {
LOG.warn("Unable to adapt {} to {} while adapting list of paths to {} due to "
+ "ResourceNotFoundException", path, type.getSimpleName(), type.getSimpleName());
}
}
return models;
}
/**
* Retrieves the specified Resource's parent Resource as a BaseResource, if one exists.
*
* @param resource Resource to retrieve the parent of.
* @return the specified Resource's parent Resource as a BaseResource.
* @throws NoParentResourceException Thrown when no parent of the passed Resource can be
* found.
*/
@Nonnull
public static BaseResource getParentResourceAsBaseResource(@Nonnull final Resource resource)
throws NoParentResourceException {
final Resource parentResource = resource.getParent();
if (parentResource != null) {
return adaptToBaseResource(parentResource);
}
throw new NoParentResourceException(resource.getPath());
}
/**
* This method is functionally the same as {@link #getParentResourceAsBaseResource(Resource)} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to retrieve the parent of.
* @return the specified Resource's parent Resource as a BaseResource.
* @throws NoParentResourceException Thrown when no parent of the passed Resource can be
* found.
*/
@Nonnull
public static BaseResource getParentResourceAsBaseResource(
@Nonnull final BaseResource baseResource) throws NoParentResourceException {
return getParentResourceAsBaseResource(baseResource.getResource());
}
/**
* The parent Resource, adapted to the specified type.
*
* @param resource Resource to get the parent of.
* @param type Class to adapt the parent Resource to. Class must extend BaseResource and have
* the
* @param Class to adapt the parent Resource to. Class must extend BaseResource and have
* the
* @return The parent Resource, adapted to the specified type.
* @throws InvalidResourceTypeException thrown when the parent Resource is found, but cannot
* be adapted to the specified type, due to a mismatch between the resourceType of the
* Resource, and the resourceType value of the type's {@link Model} annotation.
* @throws NoParentResourceException thrown when the parent Resource cannot be found. This
* should only happen at the root level.
*/
@Nonnull
public static T getParentResourceAsType(@Nonnull final Resource resource,
@Nonnull final Class type) throws InvalidResourceTypeException, NoParentResourceException {
final Resource parentResource = resource.getParent();
if (parentResource != null) {
return adaptTo(parentResource, type);
}
throw new NoParentResourceException(resource.getPath());
}
/**
* This method is functionally the same as {@link #getParentResourceAsType(Resource, Class)} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to get the parent of.
* @param type Class to adapt the parent Resource to. Class must extend BaseResource and have
* the
* @param Class to adapt the parent Resource to. Class must extend BaseResource and have
* the
* @return The parent Resource, adapted to the specified type.
* @throws InvalidResourceTypeException thrown when the parent Resource is found, but cannot
* be adapted to the specified type, due to a mismatch between the resourceType of the
* Resource, and the resourceType value of the type's {@link Model} annotation.
* @throws NoParentResourceException thrown when the parent Resource cannot be found. This
* should only happen at the root level.
*/
@Nonnull
public static T getParentResourceAsType(
@Nonnull final BaseResource baseResource, @Nonnull final Class type)
throws InvalidResourceTypeException, NoParentResourceException {
return getParentResourceAsType(baseResource.getResource(), type);
}
/**
* The first ancestor Resource that can be adapted to the specified type.
*
* @param resource Resource to look for ancestors of
* @param type Class to attempt to adapt the ancestor Resource to. Class must extend
* BaseResource and have the {@link Model} annotation, with the resourceType value set.
* @param Class to attempt to adapt the ancestor Resource to. Class must extend
* BaseResource and have the {@link Model} annotation, with the resourceType value set.
* @return The first ancestor Resource that can be adapted to the specified type.
* @throws NoValidAncestorException thrown when ancestry ends without having found a valid
* Resource.
*/
@Nonnull
public static T getFirstAncestorOfType(@Nonnull final Resource resource,
@Nonnull final Class type) throws NoValidAncestorException {
try {
return getParentResourceAsType(resource, type);
} catch (final InvalidResourceTypeException exception) {
try {
return getFirstAncestorOfType(getParentResourceAsBaseResource(resource), type);
} catch (final NoParentResourceException | NoValidAncestorException e1) {
throw new NoValidAncestorException(resource.getPath(), type);
}
} catch (final NoParentResourceException exception) {
throw new NoValidAncestorException(resource.getPath(), type);
}
}
/**
* This method is functionally the same as {@link #getFirstAncestorOfType(Resource, Class)} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to look for ancestors of
* @param type Class to attempt to adapt the ancestor Resource to. Class must extend
* BaseResource and have the {@link Model} annotation, with the resourceType value set.
* @param Class to attempt to adapt the ancestor Resource to. Class must extend
* BaseResource and have the {@link Model} annotation, with the resourceType value set.
* @return The first ancestor Resource that can be adapted to the specified type.
* @throws NoValidAncestorException thrown when ancestry ends without having found a valid
* Resource.
*/
@Nonnull
public static T getFirstAncestorOfType(
@Nonnull final BaseResource baseResource, @Nonnull final Class type)
throws NoValidAncestorException {
return getFirstAncestorOfType(baseResource.getResource(), type);
}
/**
* Traverses the JCR (using the passed Resource as the origin) to find all Resources that can be
* adapted to the specified type.
*
* @param resource Resource to originate JCR traversal from.
* @param type Class to adapt the descendants to. Class must extend BaseResource and have the
* {@link Model} annotation, with the resourceType value set.
* @param Class to adapt the descendants to. Class must extend BaseResource and have the
* {@link Model} annotation, with the resourceType value set.
* @return List of all descendant Resources that can be adapted to the specified type, as the
* specified type.
*/
@Nonnull
public static List getAllDescendantsOfType(
@Nonnull final Resource resource, @Nonnull final Class type) {
final List descendants = getChildrenOfType(resource, type);
for (final BaseResource child : getChildrenOfType(resource, BaseResource.class)) {
descendants.addAll(getAllDescendantsOfType(child.getResource(), type));
}
return descendants;
}
/**
* This method is functionally the same as {@link #getAllDescendantsOfType(Resource, Class)} but
* accepts {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource BaseResource to originate JCR traversal from.
* @param type Class to adapt the descendants to. Class must extend BaseResource and have the
* {@link Model} annotation, with the resourceType value set.
* @param Class to adapt the descendants to. Class must extend BaseResource and have the
* {@link Model} annotation, with the resourceType value set.
* @return List of all descendant Resources that can be adapted to the specified type, as the
* specified type.
*/
@Nonnull
public static List getAllDescendantsOfType(
@Nonnull final BaseResource baseResource, @Nonnull final Class type) {
return getAllDescendantsOfType(baseResource.getResource(), type);
}
/**
* Adapts the passed Resource to the closest matching SlingModel type that extends BaseResource.
*
* @param resource Resource to adapt.
* @param modelFactory modelFactory used to match the model type to the Resource's
* resourceType.
* @param Generic class that extends BaseResource.
* @return The passed Resource adapted to the closest matching SlingModel type that extends
* BaseResource.
* @throws MatchingResourceTypeNotFoundException Thrown when the passed Resource cannot be
* dynamically adapted to a Model type.
*/
@SuppressWarnings("unchecked")
@Nonnull
public static T getResourceAsClosestType(
@Nonnull final Resource resource, @Nonnull final ModelFactory modelFactory)
throws MatchingResourceTypeNotFoundException {
Object model = null;
try {
model = modelFactory.getModelFromResource(resource);
} catch (final Exception exception) {
LOG.debug("Unable to retrieve adapted model for resource {}.", resource.getPath());
}
if (model instanceof BaseResource) {
try {
final BaseResource contentResource = getChildAsBaseResource(JCR_CONTENT, resource);
if (modelFactory.isModelAvailableForResource(contentResource.getResource())) {
final T contentResourceModel = (T) modelFactory.getModelFromResource(
contentResource.getResource());
model = adaptTo(contentResourceModel, contentResourceModel.getClass());
}
} catch (final InvalidResourceTypeException | ChildResourceNotFoundException exception) {
if (resource.getPath().endsWith(JCR_CONTENT)) {
throw new MatchingResourceTypeNotFoundException(resource.getPath());
}
}
return (T) model;
}
throw new MatchingResourceTypeNotFoundException(resource.getPath());
}
/**
* This method is functionally the same as {@link #getResourceAsClosestType(Resource,
* ModelFactory)} but accepts {@link BaseResource} instead of {@link Resource}.
*
* @param resource Resource to adapt.
* @param modelFactory modelFactory used to match the model type to the Resource's
* resourceType.
* @param Generic class that extends BaseResource.
* @return The passed Resource adapted to the closest matching SlingModel type that extends
* BaseResource.
* @throws MatchingResourceTypeNotFoundException Thrown when the passed Resource cannot be
* dynamically adapted to a Model type.
*/
@Nonnull
public static T getResourceAsClosestType(
@Nonnull final BaseResource resource, @Nonnull final ModelFactory modelFactory)
throws MatchingResourceTypeNotFoundException {
return getResourceAsClosestType(resource.getResource(), modelFactory);
}
/**
* Returns a List of all children, adapted to the closest matching SlingModel type that extends
* BaseResource.
*
* @param resource Resource to retrieve children from.
* @param modelFactory modelFactory used to match the model type to the Resource's
* resourceType.
* @param Generic class that extends BaseResource.
* @return List of all children, adapted to the closest matching SlingModel type that extends
*/
@Nonnull
public static List getChildrenAsClosestTypes(
@Nonnull final Resource resource, @Nonnull final ModelFactory modelFactory) {
final List children = new ArrayList<>();
for (final BaseResource child : getChildrenOfType(resource, BaseResource.class)) {
try {
children.add(getResourceAsClosestType(child.getResource(), modelFactory));
} catch (final InvalidResourceTypeException exception) {
LOG.debug("Unable to retrieve adapted model for resource {} while retrieving "
+ "children for {}, this resource will not be included.", child.getName(),
resource.getPath());
}
}
return children;
}
/**
* This method is functionally the same as {@link #getChildrenAsClosestTypes(Resource,
* ModelFactory)} but accepts {@link BaseResource} instead of {@link Resource}.
*
* @param baseResource Resource to retrieve children from.
* @param modelFactory modelFactory used to match the model type to the Resource's
* resourceType.
* @param Generic class that extends BaseResource.
* @return List of all children, adapted to the closest matching SlingModel type that extends
*/
@Nonnull
public static List getChildrenAsClosestTypes(
@Nonnull final BaseResource baseResource, @Nonnull final ModelFactory modelFactory) {
return getChildrenAsClosestTypes(baseResource.getResource(), modelFactory);
}
private static boolean isValidResourceTypeBasedOnSuperTypes(@Nonnull final Resource resource,
@Nonnull final List validResourceTypes) {
final Resource resourceTypeResource = resource.getResourceResolver().getResource(
resource.getResourceType());
if (resourceTypeResource != null) {
BaseResource currentResourceTypeResource = adaptToBaseResource(resourceTypeResource);
while (currentResourceTypeResource != null) {
if (validResourceTypes.contains(getResourceTypePath(currentResourceTypeResource))
|| validResourceTypes.contains(currentResourceTypeResource.getResourceSuperType())) {
return true;
}
try {
currentResourceTypeResource = getResourceAsType(
currentResourceTypeResource.getResourceSuperType(), resource.getResourceResolver(),
BaseResource.class);
} catch (final ModelAdaptionException exception) {
LOG.debug("Resource {} not found while checking if it is a valid resourceType based on "
+ "superTypes. Returning false.", resource.getPath());
currentResourceTypeResource = null;
}
}
}
return false;
}
/**
* Whether the passed BaseResource is valid, based on the `resourceType` value of the type's
* {@link Model} annotation.
*
* @param resource BaseResource to validate.
* @param type Class to validate the BaseResource against. Class must extend BaseResource and
* have the {@link Model} annotation, with the resourceType value set.
* @param Class to validate the BaseResource against. Class must extend BaseResource and
* have the {@link Model} annotation, with the resourceType value set.
* @return Whether the passed BaseResource is valid, based on the `resourceType` value of the
* type's {@link Model} annotation.
*/
static boolean isValidResourceType(@Nonnull Resource resource,
@Nonnull final Class type) {
final List validResourceTypes = Arrays.asList(
type.getAnnotation(Model.class).resourceType());
if (resource.getPath().startsWith(PREFIX_APPS) && JcrConstants.NT_FOLDER.equals(
resource.getResourceType())) {
final String libsResourcePath = resource.getPath().replaceFirst(PREFIX_APPS, PREFIX_LIBS);
try {
resource = getResourceAsBaseResource(libsResourcePath,
resource.getResourceResolver()).getResource();
} catch (final ResourceNotFoundException e) {
LOG.trace("Attempted to retrieve /libs resource matching {}, but none could be found.",
resource.getPath());
}
}
if (validResourceTypes.contains("sling/servlet/default")) {
return true;
}
if (validResourceTypes.contains(resource.getResourceType())) {
return true;
}
if (validResourceTypes.contains(
resource.getValueMap().get(JCR_PRIMARYTYPE, StringUtils.EMPTY))) {
return true;
}
return isValidResourceTypeBasedOnSuperTypes(resource, validResourceTypes);
}
static String getResourceTypePath(final BaseResource resourceTypeResource) {
String resourceType = resourceTypeResource.getPath();
if (resourceType.startsWith(PREFIX_APPS)) {
resourceType = resourceType.split(PREFIX_APPS)[1];
}
if (resourceType.startsWith(PREFIX_LIBS)) {
resourceType = resourceType.split(PREFIX_LIBS)[1];
}
return resourceType;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy