All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.composum.sling.core.util.ResourceUtil Maven / Gradle / Ivy

There is a newer version: 4.3.4
Show newest version
package com.composum.sling.core.util;

import com.composum.sling.core.JcrResource;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.JcrConstants;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.jcr.base.util.AccessControlUtil;
import org.slf4j.Logger;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.jcr.Binary;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.nodetype.NodeType;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * A collection of loads of utilities dealing with {@link Resource}s.
 */
public class ResourceUtil extends org.apache.sling.api.resource.ResourceUtil implements CoreConstants {

    private static final Logger LOG = getLogger(ResourceUtil.class);

    public static final Map> NODE_TYPE_MAP;

    static {
        NODE_TYPE_MAP = new HashMap<>();
        NODE_TYPE_MAP.put(NT_HIERARCHYNODE,
                Arrays.asList(NT_FOLDER, TYPE_SLING_FOLDER, TYPE_SLING_ORDERED_FOLDER));
    }

    /**
     * retrieves all children of one of a type from a type set (node type or resource type)
     */
    public static List getChildrenByType(final Resource resource, List typeSet) {
        final ArrayList children = new ArrayList<>();
        if (resource != null) {
            for (final Resource child : resource.getChildren()) {
                for (String type : typeSet) {
                    if (isResourceType(child, type)) {
                        children.add(child);
                        break;
                    }
                }
            }
        }
        return children;
    }

    /**
     * retrieves all children of a type (node type or resource type)
     */
    public static List getChildrenByType(final Resource resource, String type) {
        final ArrayList children = new ArrayList<>();
        if (resource != null) {
            for (final Resource child : resource.getChildren()) {
                if (isResourceType(child, type)) {
                    children.add(child);
                }
            }
        }
        return children;
    }

    /**
     * retrieves all children of a sling:resourceType
     */
    public static List getChildrenByResourceType(final Resource resource, String resourceType) {
        final ArrayList children = new ArrayList<>();
        if (resource != null) {
            for (final Resource child : resource.getChildren()) {
                if (child.isResourceType(resourceType)) {
                    children.add(child);
                }
            }
        }
        return children;
    }

    public static int getIndexOfSameType(Resource resource) {
        if (resource != null) {
            String name = resource.getName();
            String type = resource.getResourceType();
            Resource parent = resource.getParent();
            if (parent != null) {
                int index = 0;
                for (Resource child : parent.getChildren()) {
                    if (child.isResourceType(type)) {
                        if (name.equals(child.getName())) {
                            return index;
                        }
                        index++;
                    }
                }
            }
        }
        return -1;
    }

    public static Resource getNextOfSameType(Resource resource, boolean wrapAround) {
        if (resource != null) {
            String name = resource.getName();
            String type = resource.getResourceType();
            Resource parent = resource.getParent();
            if (parent != null) {
                boolean returnNext = false;
                for (Resource child : parent.getChildren()) {
                    if (child.isResourceType(type)) {
                        if (returnNext) {
                            return child;
                        }
                        if (name.equals(child.getName())) {
                            returnNext = true;
                        }
                    }
                }
                if (returnNext && wrapAround) {
                    for (Resource child : parent.getChildren()) {
                        if (child.isResourceType(type)) {
                            return child;
                        }
                    }
                }
            }
        }
        return null;
    }

    public static String getNameExtension(Resource resource) {
        String extension;
        String name = resource.getName();
        if (ResourceUtil.CONTENT_NODE.equals(name)) {
            name = Objects.requireNonNull(resource.getParent()).getName();
        }
        int dot = name.lastIndexOf('.');
        extension = (dot >= 0 ? name.substring(dot + 1).toLowerCase() : "");
        return extension;
    }

    /**
     * retrieves the primary type of the resources node
     */
    public static String getPrimaryType(Resource resource) {
        String result = null;
        if (resource != null) {
            if (resource instanceof JcrResource) {
                // use the resource itself if it implements the JcrResource interface (maybe a version of a resource)
                result = ((JcrResource) resource).getPrimaryType();
            } else {
                Node node = resource.adaptTo(Node.class);
                if (node != null) {
                    try {
                        NodeType type = node.getPrimaryNodeType();
                        if (type != null) {
                            result = type.getName();
                        }
                    } catch (RepositoryException ignore) {
                    }
                }
                if (result == null) {
                    ValueMap values = resource.getValueMap();
                    result = values.get(JcrConstants.JCR_PRIMARYTYPE, String.class);
                }
            }
        }
        return result;
    }

    /**
     * Checks whether any of the resource's primary type, super types, sling resource type and supertypes is {resourceType}.
     */
    public static boolean isResourceType(@Nullable Resource resource, String resourceType) {
        return (resource != null && (resource.isResourceType(resourceType)
                || isPrimaryType(resource, resourceType) || isNodeType(resource, resourceType)));
    }

    /**
     * True if the {@link #getPrimaryType(Resource)} of the resource is exactly primaryType.
     */
    public static boolean isPrimaryType(Resource resource, String primaryType) {
        return (primaryType.equals(getPrimaryType(resource)));
    }

    /**
     * Returns true if this node is of the specified primary node type or mixin type, or a subtype thereof. Returns false otherwise.
     */
    public static boolean isNodeType(Resource resource, String primaryType) {
        if (resource != null) {
            try {
                final Node node = resource.adaptTo(Node.class);
                if (node != null) {
                    return node.isNodeType(primaryType);
                }
                ValueMap values = resource.getValueMap();
                if (isNodeType(primaryType, values.get(JcrConstants.JCR_PRIMARYTYPE, String.class))) {
                    return true;
                }
                String[] mixins = values.get(JcrConstants.JCR_MIXINTYPES, new String[0]);
                for (String mixin : mixins) {
                    if (isNodeType(primaryType, mixin)) {
                        return true;
                    }
                }
            } catch (RepositoryException ignore) {
            }
        }
        return false;
    }

    public static boolean isNodeType(String wanted, String probe) {
        if (wanted.equals(probe)) {
            return true;
        }
        List variations = NODE_TYPE_MAP.get(wanted);
        return variations != null && variations.contains(probe);
    }

    public static Resource getResourceType(Resource resource) {
        return resource != null ? getResourceType(resource.getResourceResolver(),
                resource.getResourceType()) : null;
    }

    public static Resource getResourceType(ResourceResolver resolver, String resourceTypeName) {
        Resource resourceType = null;
        if (StringUtils.isNotBlank(resourceTypeName)) {
            if (resourceTypeName.startsWith("/")) {
                resourceType = resolver.getResource(resourceTypeName);
            } else {
                final String[] searchPath = resolver.getSearchPath();
                for (String path : searchPath) {
                    resourceType = resolver.getResource(path + resourceTypeName);
                    if (resourceType != null) {
                        return resourceType;
                    }
                }
            }
        }
        return resourceType;
    }

    public static boolean isResourceType(Resource resource, Pattern pattern) {
        return resource != null && isResourceType(resource.getResourceResolver(), resource.getResourceType(), pattern);
    }

    public static boolean isResourceType(ResourceResolver resolver, String resourceTypeName, Pattern pattern) {
        if (StringUtils.isNotBlank(resourceTypeName)) {
            if (pattern.matcher(resourceTypeName).find()) {
                return true;
            } else {
                Resource resourceType = getResourceType(resolver, resourceTypeName);
                if (resourceType == null) {
                    return false;
                } else {
                    ValueMap values = resourceType.getValueMap();
                    String resourceSuperTypeName = values.get(PROP_RESOURCE_SUPER_TYPE, "");
                    return isResourceType(resolver, resourceSuperTypeName, pattern);
                }
            }
        }
        return false;
    }

    public static  T getTypeProperty(Resource resource, String name, T defaultValue) {
        T value = getTypeProperty(resource, name, PropertyUtil.getType(defaultValue));
        return value != null ? value : defaultValue;
    }

    public static  T getTypeProperty(Resource resource, String name, Class type) {
        return resource != null ? getTypeProperty(resource.getResourceResolver(),
                resource.getResourceType(), name, type) : null;
    }

    public static  T getTypeProperty(ResourceResolver resolver, String resourceTypeName,
                                        String name, Class type) {
        T value = null;
        Resource resourceType = getResourceType(resolver, resourceTypeName);
        if (resourceType != null) {
            ValueMap values = resourceType.getValueMap();
            value = values.get(name, type);
            if (value == null) {
                String resourceSuperTypeName = values.get(PROP_RESOURCE_SUPER_TYPE, "");
                value = getTypeProperty(resolver, resourceSuperTypeName, name, type);
            }
        }
        return value;
    }

    public static Resource getOrCreateResource(ResourceResolver resolver, String path)
            throws RepositoryException {
        return getOrCreateResource(resolver, path, null);
    }

    public static Resource getOrCreateResource(ResourceResolver resolver, String path, String primaryTypes)
            throws RepositoryException {
        Resource resource = resolver.getResource(path);
        if (resource == null && (path = normalize(path)) != null) {
            int lastPathSegment = path.lastIndexOf('/');
            String parentPath = "/";
            String name = path;
            if (lastPathSegment >= 0) {
                name = path.substring(lastPathSegment + 1);
                parentPath = path.substring(0, lastPathSegment);
                if (StringUtils.isBlank(parentPath)) {
                    parentPath = "/";
                }
            }
            int lastTypeSegment;
            String parentTypes = primaryTypes;
            String type = primaryTypes;
            if (primaryTypes != null && (lastTypeSegment = primaryTypes.lastIndexOf('/')) >= 0) {
                type = primaryTypes.substring(lastTypeSegment + 1);
                parentTypes = primaryTypes.substring(0, lastTypeSegment);
            }
            Resource parent = getOrCreateResource(resolver, parentPath, parentTypes);
            if (parent != null) {
                Node node = parent.adaptTo(Node.class);
                if (node != null) {
                    if (StringUtils.isNotBlank(type)) {
                        node.addNode(name, type);
                    } else {
                        node.addNode(name);
                    }
                } else {
                    Map properties = new HashMap<>();
                    if (StringUtils.isNotBlank(type)) {
                        properties.put(JcrConstants.JCR_PRIMARYTYPE, type);
                    }
                    try {
                        resolver.create(parent, name, properties);
                    } catch (PersistenceException pex) {
                        LOG.error(pex.getMessage(), pex);
                        throw new RepositoryException(pex);
                    }
                }
                resource = parent.getChild(name);
            }
        }
        return resource;
    }

    public static boolean containsPath(List collection, Resource resource) {
        return containsPath(collection, resource.getPath());
    }

    public static boolean containsPath(List collection, String path) {
        for (Resource item : collection) {
            if (item.getPath().equals(path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns an array of two elements for a nontrivial path (containing /): the parent path and the name.
     * The parent of a toplevel node like "/var" is returned as "/".
     * If it doesn't contain a / , the result has null in the first place.
     */
    @NotNull
    public static String[] splitPathAndName(@NotNull String path) {
        String[] result = new String[2];
        int nameSeparator = path.lastIndexOf('/');
        if (nameSeparator >= 0) {
            result[0] = path.substring(0, nameSeparator);
            result[1] = path.substring(nameSeparator + 1);
            if (result[0].isEmpty()) // absolute path at toplevel, like /var
                result[0] = "/";
        } else {
            result[0] = null; // unclear what to return here - null or "" .
            result[1] = path;
        }
        return result;
    }

    /**
     * Retrieves the resources child resource, creates this child if not existing.
     *
     * @param resource     the resource to extend
     * @param relPath      the path to the requested child resource
     * @param primaryTypes the 'path' of primary types for the new nodes (optional, can be 'null')
     * @return the requested child
     */
    public static Resource getOrCreateChild(Resource resource, String relPath, String primaryTypes)
            throws RepositoryException {
        Resource child = null;
        if (resource != null) {
            ResourceResolver resolver = resource.getResourceResolver();
            String path = resource.getPath();
            while (relPath.startsWith("/")) {
                relPath = relPath.substring(1);
            }
            if (StringUtils.isNotBlank(relPath)) {
                path += "/" + relPath;
            }
            child = getOrCreateResource(resolver, path, primaryTypes);
        }
        return child;
    }

    /**
     * Checks the access control policies for enabled changes (node creation and property change).
     */
    public static boolean isWriteEnabled(Resource resource, String relPath) throws RepositoryException {

        ResourceResolver resolver = resource.getResourceResolver();
        Session session = Objects.requireNonNull(resolver.adaptTo(Session.class));
        AccessControlManager accessManager = AccessControlUtil.getAccessControlManager(session);

        String resourcePath = resource.getPath();
        Privilege[] addPrivileges = new Privilege[]{
                accessManager.privilegeFromName(Privilege.JCR_ADD_CHILD_NODES)
        };
        boolean result = accessManager.hasPrivileges(resourcePath, addPrivileges);

        if (StringUtils.isNotBlank(relPath)) {
            if (!resourcePath.endsWith("/")) {
                resourcePath += "/";
            }
            resourcePath += relPath;
        }
        Privilege[] changePrivileges = new Privilege[]{
                accessManager.privilegeFromName(Privilege.JCR_MODIFY_PROPERTIES)
        };
        try {
            result = result && accessManager.hasPrivileges(resourcePath, changePrivileges);
        } catch (PathNotFoundException pnfex) {
            // ok, let's create it
        }

        return result;
    }

    /**
     * Returns 'true' is this resource represents a 'file' witch can be displayed (a HTML file).
     */
    public static boolean isRenderableFile(Resource resource) {
        boolean result = false;
        try {
            String typeName;
            Node node = resource.adaptTo(Node.class);
            if (node != null) {
                NodeType type = node.getPrimaryNodeType();
                typeName = type.getName();
            } else {
                typeName = resource.getValueMap().get(JcrConstants.JCR_PRIMARYTYPE, "");
            }
            if (ResourceUtil.TYPE_FILE.equals(typeName)) {
                String resoureName = resource.getName();
                result = resoureName.toLowerCase().endsWith(LinkUtil.EXT_HTML);
            }
        } catch (RepositoryException e) {
            // ok, not renderable
        }
        return result;
    }

    /**
     * Returns 'true' is this resource represents a 'file'.
     */
    public static boolean isFile(Resource resource) {
        try {
            String typeName = null;
            Node node = resource.adaptTo(Node.class);
            if (node != null) {
                NodeType type = node.getPrimaryNodeType();
                if (type != null) {
                    typeName = type.getName();
                }
            } else {
                typeName = resource.getValueMap().get(JcrConstants.JCR_PRIMARYTYPE, String.class);
            }
            if (typeName != null) {
                switch (typeName) {
                    case TYPE_FILE:
                        return true;
                    case TYPE_RESOURCE:
                    case TYPE_UNSTRUCTURED:
                        String mimeType = null;
                        try {
                            if (node != null) {
                                Property propMimeType = node.getProperty(PROP_MIME_TYPE);
                                if (propMimeType != null) {
                                    mimeType = propMimeType.getString();
                                }
                                // PathNotFountException if not present
                                node.getProperty(ResourceUtil.PROP_DATA);
                            } else {
                                mimeType = resource.getValueMap().get(PROP_MIME_TYPE, "");
                            }
                            return StringUtils.isNotBlank(mimeType);
                        } catch (PathNotFoundException ignore) {
                            // ok, was a check only
                        }
                        break;
                }
            }
        } catch (RepositoryException e) {
            LOG.error(e.getMessage(), e);
        }
        return false;
    }

    public static Resource getDataResource(Resource resource) {
        Node node = resource.adaptTo(Node.class);
        if (node != null) {
            try {
                try {
                    node.getProperty(PROP_DATA);
                    return resource;
                } catch (PathNotFoundException pnfex) {
                    Node contentNode = node.getNode(CONTENT_NODE);
                    contentNode.getProperty(PROP_DATA);
                    return resource.getChild(CONTENT_NODE);
                }
            } catch (RepositoryException rex) {
                // ok, property doesn't exist
            }
        } else {
            if (resource.getValueMap().get(PROP_DATA) != null) {
                return resource;
            } else {
                Resource contentRes = resource.getChild(CONTENT_NODE);
                if (contentRes != null) {
                    if (contentRes.getValueMap().get(PROP_DATA) != null) {
                        return contentRes;
                    }
                }
            }
        }
        return null;
    }

    public static Binary getBinaryData(Resource resource) {
        return PropertyUtil.getBinaryData(resource.adaptTo(Node.class));
    }

    /**
     * Finds a mix:referenceable by its jcr:uuid. This works only for JCR resources.
     */
    @Nullable
    public static Resource getByUuid(@NotNull ResourceResolver resolver, @Nullable String uuid) throws RepositoryException {
        if (StringUtils.isBlank(uuid)) return null;
        Resource result = null;
        Session session = resolver.adaptTo(Session.class);
        if (session != null) {
            try {
                Node node = session.getNodeByIdentifier(uuid);
                result = resolver.getResource(node.getPath());
            } catch (ItemNotFoundException e) { // OK.
                LOG.debug("Node not found by uuid {}", uuid);
            }
        } else {
            throw new RepositoryException("Cannot get JCR Session from resolver " + resolver);
        }
        return result;
    }

    /**
     * Finds the referenced node from a property of type REFERENCE, WEAKREFERENCE or PATH or convertable to that.
     */
    @Nullable
    public static Resource getReferredResource(@Nullable Resource propertyResource) throws RepositoryException {
        if (propertyResource == null) return null;
        Property property = propertyResource.adaptTo(Property.class);
        Node node = property != null ? property.getNode() : null;
        String path = node != null ? node.getPath() : null;
        Resource referredResource = path != null ? propertyResource.getResourceResolver().getResource(path) : null;
        if (referredResource == null) {
            if (property == null) { // strange!
                LOG.debug("Could not get Property for {}", propertyResource.getPath());
            } else if (StringUtils.isNotEmpty( property.getString())) {
                if (property.getDefinition() != null && PropertyType.REFERENCE == property.getDefinition().getRequiredType()) {
                    LOG.warn("Inconsistent JCR: could not find referred resource for strong reference at {}", propertyResource.getPath());
                }
            }
        }
        return referredResource;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy