![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.sling.api.resource.ResourceUtil Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/* * 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 org.apache.sling.api.resource; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import org.apache.sling.api.wrappers.ValueMapDecorator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * The
, * which is normalized by {@link #normalize(String)} before resolving the * parent. * * @param path The path whose parent is to be returned. * @returnResourceUtil
class provides helper methods dealing with * resources. ** This class is not intended to be extended or instantiated because it just * provides static utility methods not intended to be overwritten. */ public class ResourceUtil { /** * Resolves relative path segments '.' and '..' in the path. * The path can either be relative or absolute. Relative paths are treated * the same as an absolute path. * Returns {@code null} if not possible (for example .. points above root). * * @param path The path to normalize * @return The normalized path or {@code null}. */ public static @Nullable String normalize(@NotNull String path) { // remove trailing slashes path = removeTrailingSlashes(path); // don't care for empty paths or just slash if (path.isEmpty() || "/".equals(path)) { return path; } // ignore leading slashes int startPos = 0; while ( startPos < path.length() && path.charAt(startPos) == '/' ) { startPos++; } // split into segments final String[] parts = path.substring(startPos).split("/"); String[] newParts = new String[parts.length]; int newPartsPos = 0; for (final String part : parts) { // check each segment for empty and dots final int dotCount = countDotsSegment(part); if (part.isEmpty() || dotCount == 1) { // ignore } else if (dotCount == 2) { if (newPartsPos == 0) { // can't go above root return null; } newPartsPos--; } else if (dotCount > 2) { // invalid return null; } else { newParts[newPartsPos++] = part; } } // nothing changed ? if (newPartsPos == newParts.length && startPos < 2) { return path; } // only slash if (newPartsPos == 0 && startPos > 0) { return "/"; } // reconstruct (we don't use String.join as array elements might be null) final StringBuilder sb = new StringBuilder(); for(int i=0;i
0 || startPos > 0) { sb.append('/'); } sb.append(newParts[i]); } return sb.toString(); } /** * Remove all trailing slashes except for the root */ private static String removeTrailingSlashes(@NotNull final String path) { int endPos = path.length() - 1; while ( endPos >= 0 && path.charAt(endPos) == '/' ) { endPos--; } if (endPos == path.length() - 1) { return path; } // only slashes, just return one if ( endPos == -1 ) { return "/"; } return path.substring(0, endPos + 1); } /** * Return the number of dots in the segment, if the segment only contains dots * @param segment The segment * @return The number of dots or 0 if the segment contains no dot or other characters. */ private static int countDotsSegment(final String segment) { for(int i=0;i path null
ifpath
is the root path ( */
) or ifpath
is a single name * containing no slash (/
) characters. * @throws IllegalArgumentException If the path cannot be normalized by the * {@link #normalize(String)} method. * @throws NullPointerException Ifpath
isnull
. */ public static @Nullable String getParent(@NotNull String path) { if ("/".equals(path)) { return null; } // normalize path (remove . and ..) path = normalize(path); // if normalized to root, there is no parent if (path == null || "/".equals(path)) { return null; } String workspaceName = null; final int wsSepPos = path.indexOf(":/"); if (wsSepPos != -1) { workspaceName = path.substring(0, wsSepPos); path = path.substring(wsSepPos + 1); } // find the last slash, after which to cut off int lastSlash = path.lastIndexOf('/'); if (lastSlash < 0) { // no slash in the path return null; } else if (lastSlash == 0) { // parent is root if (workspaceName != null) { return workspaceName + ":/"; } return "/"; } String parentPath = path.substring(0, lastSlash); if (workspaceName != null) { return workspaceName + ":" + parentPath; } return parentPath; } /** * Utility method returns the ancestor's path at the givenlevel
* relative topath
, which is normalized by {@link #normalize(String)} * before resolving the ancestor. * *
-
*
level
= 0 returns thepath
.
* level
= 1 returns the parent ofpath
, if it exists,null
otherwise.
* level
= 2 returns the grandparent ofpath
, if it exists,null
otherwise.
*
path
.
* @return null
if path
doesn't have an ancestor at the
* specified level
.
* @throws IllegalArgumentException If the path cannot be normalized by the
* {@link #normalize(String)} method or if level
< 0.
* @throws NullPointerException If path
is null
.
* @since 2.2 (Sling API Bundle 2.2.0)
*/
public static String getParent(final String path, final int level) {
if ( level < 0 ) {
throw new IllegalArgumentException("level must be non-negative");
}
String result = path;
for(int i=0; inull
.
* @throws org.apache.sling.api.SlingException If an error occurs trying to
* get the resource object from the path.
* @throws IllegalStateException if the resource resolver has already been
* closed}.
* @deprecated since 2.1.0, use {@link Resource#getParent()} instead
*/
@Deprecated
public static @Nullable Resource getParent(@NotNull Resource rsrc) {
return rsrc.getParent();
}
/**
* Utility method returns the name of the resource.
*
* @param rsrc The resource to get the name from.
* @return The name of the resource
* @throws NullPointerException If rsrc
is null
.
* @deprecated since 2.1.0, use {@link Resource#getName()} instead
*/
@Deprecated
public static @NotNull String getName(@NotNull Resource rsrc) {
/*
* Same as AbstractResource.getName() implementation to prevent problems
* if there are implementations of the pre-2.1.0 Resource interface in
* the framework.
*/
return getName(rsrc.getPath());
}
/**
* Utility method returns the name of the given path
, which is
* normalized by {@link #normalize(String)} before resolving the name.
*
* @param path The path whose name (the last path element) is to be
* returned.
* @return The empty string if path
is the root path (
* /
) or if path
is a single name
* containing no slash (/
) characters.
* @throws IllegalArgumentException If the path cannot be normalized by the
* {@link #normalize(String)} method.
* @throws NullPointerException If path
is null
.
*/
public static @NotNull String getName(@NotNull String path) {
if ("/".equals(path)) {
return "";
}
// normalize path (remove . and ..)
path = normalize(path);
if ("/".equals(path)) {
return "";
}
// find the last slash
return path.substring(path.lastIndexOf('/') + 1);
}
/**
* Returns true
if the resource res
is a synthetic
* resource.
*
* This method checks whether the resource is an instance of the
* org.apache.sling.resource.SyntheticResource
class.
*
* @param res The Resource
to check whether it is a synthetic
* resource.
* @return true
if res
is a synthetic resource.
* false
is returned if res
is
* null
or not an instance of the
* org.apache.sling.resource.SyntheticResource
class.
*/
public static boolean isSyntheticResource(@NotNull Resource res) {
if (res instanceof SyntheticResource) {
return true;
}
if (!(res instanceof ResourceWrapper)) {
return false;
}
do {
res = ((ResourceWrapper) res).getResource();
} while (res instanceof ResourceWrapper);
return res instanceof SyntheticResource;
}
/**
* Returns true
if the resource res
is a "star
* resource". A star resource is a resource returned from the
* ResourceResolver.resolve(HttpServletRequest)
whose path
* terminates in a /*
. Generally such resource result from
* requests to something like /some/path/*
or
* /some/path/*.html
which may be used web applications to
* uniformly handle resources to be created.
*
* This method checks whether the resource path ends with a /*
* indicating such a star resource.
*
* @param res The Resource
to check whether it is a star
* resource.
* @return true
if res
is to be considered a star
* resource.
* @throws NullPointerException if res
is null
.
*/
public static boolean isStarResource(@NotNull Resource res) {
return res.getPath().endsWith("/*");
}
/**
* Returns true
if the resource res
is a
* non-existing resource.
*
* This method checks the resource type of the resource to match the
* well-known resource type sling:nonexisting
of the
* NonExistingResource
class defined in the Sling API.
*
* @param res The Resource
to check whether it is a
* non-existing resource.
* @return true
if res
is to be considered a
* non-existing resource.
* @throws NullPointerException if res
is null
.
*/
public static boolean isNonExistingResource(@NotNull Resource res) {
return Resource.RESOURCE_TYPE_NON_EXISTING.equals(res.getResourceType());
}
/**
* Returns an Iterator
of {@link Resource} objects loaded from
* the children of the given Resource
.
*
* This is a convenience method for
* {@link ResourceResolver#listChildren(Resource)}.
*
* @param parent The {@link Resource Resource} whose children are requested.
* @return An For backward compatibility reasons the map returned is not immutable,
* but it is not recommend to rely on this behavior.Iterator
of {@link Resource} objects.
* @throws NullPointerException If parent
is null
.
* @throws IllegalStateException if the resource resolver has already been
* closed}.
* @throws org.apache.sling.api.SlingException If any error occurs acquiring
* the child resource iterator.
* @see ResourceResolver#listChildren(Resource)
* @deprecated since 2.1.0, use {@link Resource#listChildren()} instead
*/
@Deprecated
public static @NotNull IteratorValueMap
object for the given
* Resource
. This method calls {@link Resource#getValueMap()}.
* If null
is provided as the resource an empty map is returned as
* well.
* Resource
to adapt to the value map.
* @return A value map.
*/
public static @NotNull ValueMap getValueMap(final Resource res) {
if ( res == null ) {
// use empty map
return new ValueMapDecorator(new HashMapresourceResolver
to get the corresponding resource. If the
* resource exists, the {@link Resource#getResourceSuperType()} method is
* called.
*
* @param resourceResolver The ResourceResolver
used to access
* the resource whose path (relative or absolute) is given by the
* resourceType
parameter.
* @param resourceType The resource type whose super type is to be returned.
* This type is turned into a path by calling the
* {@link #resourceTypeToPath(String)} method before trying to
* get the resource through the resourceResolver
.
* @return the super type of the resourceType
or
* null
if the resource type does not exists or returns
* null
for its super type.
* @throws IllegalStateException if the resource resolver has already been
* closed}.
* @since 2.0.6 (Sling API Bundle 2.0.6)
* @deprecated Use {@link ResourceResolver#getParentResourceType(String)}
*/
@Deprecated
public static @Nullable String getResourceSuperType(
final @NotNull ResourceResolver resourceResolver, final String resourceType) {
return resourceResolver.getParentResourceType(resourceType);
}
/**
* Returns the super type of the given resource. This method checks first if
* the resource itself knows its super type by calling
* {@link Resource#getResourceSuperType()}. If that returns
* null
{@link #getResourceSuperType(ResourceResolver, String)}
* is invoked with the resource type of the resource.
*
* @param resource The resource to return the resource super type for.
* @return the super type of the resource
or null
* if no super type could be computed.
* @throws IllegalStateException if the resource resolver has already been
* closed}.
* @since 2.0.6 (Sling API Bundle 2.0.6)
* @deprecated Use {@link ResourceResolver#getParentResourceType(Resource)}
*/
@Deprecated
public static @Nullable String findResourceSuperType(@NotNull final Resource resource) {
if ( resource == null ) {
return null;
}
return resource.getResourceResolver().getParentResourceType(resource);
}
/**
* Check if the resource is of the given type. This method first checks the
* resource type of the resource, then its super resource type and continues
* to go up the resource super type hierarchy.
*
* In case the type of the given resource or the given resource type starts with one of the resource resolver's search paths
* it is converted to a relative resource type by stripping off the resource resolver's search path
* before doing the comparison.
*
* @param resource the resource to check
* @param resourceType the resource type to check the resource against
* @return false
if resource
is null
.
* Otherwise returns the result of calling
* {@link Resource#isResourceType(String)} with the given
* resourceType
.
* @throws IllegalStateException if the resource resolver has already been
* closed}.
* @since 2.0.6 (Sling API Bundle 2.0.6)
* @deprecated Use {@link ResourceResolver#isResourceType(Resource, String)}
*/
@Deprecated
public static boolean isA(@NotNull final Resource resource, final String resourceType) {
if ( resource == null ) {
return false;
}
return resource.getResourceResolver().isResourceType(resource, resourceType);
}
/**
* Return an iterator for objects of the specified type. A new iterator is
* returned which tries to adapt the provided resources to the given type
* (using {@link Resource#adaptTo(Class)}. If a resource in the original
* iterator is not adaptable to the given class, this object is skipped.
* This implies that the number of objects returned by the new iterator
* might be less than the number of resource objects.
*
* @param iterator A resource iterator.
* @param parent
.
* Creates a unique name and test if child already exists.
* If child resource with the same name exists, iterate until a unique one is found.
*
* @param parent The parent resource
* @param name The name of the child resource
* @return a unique non-existing name for child resource for a given parent
*
* @throws PersistenceException if it can not find unique name for child resource.
* @throws NullPointerException if parent
is null
* @throws IllegalStateException if the resource resolver has already been
* closed}.
* @since 2.5 (Sling API Bundle 2.7.0)
*/
public static String createUniqueChildName(final Resource parent, final String name)
throws PersistenceException {
if (parent.getChild(name) != null) {
// leaf node already exists, create new unique name
String childNodeName = null;
int i = 0;
do {
childNodeName = name + String.valueOf(i);
//just so that it does not run into an infinite loop
// this should not happen though :)
if (i == Integer.MAX_VALUE) {
String message = MessageFormat.format("can not find a unique name {0} for {1}", name, parent.getPath());
throw new PersistenceException(message);
}
i++;
} while (parent.getChild(childNodeName) != null);
return childNodeName;
}
return name;
}
/**
* Unwrap the resource and return the wrapped implementation.
* @param rsrc The resource to unwrap
* @return The unwrapped resource
* @since 2.5 (Sling API Bundle 2.7.0)
*/
public static @NotNull Resource unwrap(final @NotNull Resource rsrc) {
Resource result = rsrc;
while ( result instanceof ResourceWrapper ) {
result = ((ResourceWrapper)result).getResource();
}
return result;
}
/**
* A batch resource remover deletes resources in batches. Once the batch
* size (threshold) is reached, an intermediate commit is performed. Resource
* trees are deleted resource by resource starting with the deepest children first.
* Once all resources have been passed to the batch resource remover, a final
* commit needs to be called on the resource resolver.
* @since 2.6 (Sling API Bundle 2.8.0)
*/
public static class BatchResourceRemover {
private final int max;
private int count;
public BatchResourceRemover(final int batchSize) {
this.max = (batchSize < 1 ? 50 : batchSize);
}
public void delete(@NotNull final Resource rsrc)
throws PersistenceException {
final ResourceResolver resolver = rsrc.getResourceResolver();
for(final Resource child : rsrc.getChildren()) {
delete(child);
}
resolver.delete(rsrc);
count++;
if ( count >= max ) {
resolver.commit();
count = 0;
}
}
}
/**
* Create a batch resource remover.
* A batch resource remove can be used to delete resources in batches.
* Once the passed in threshold of deleted resources is reached, an intermediate
* commit is called on the resource resolver. In addition the batch remover
* deletes a resource recursively.
* Once all resources to delete are passed to the remover, a final commit needs
* to be call on the resource resolver.
* @param threshold The threshold for the intermediate saves.
* @return A new batch resource remover.
* Since 2.6
*/
public static @NotNull BatchResourceRemover getBatchResourceRemover(final int threshold) {
return new BatchResourceRemover(threshold);
}
}