private Collection getAllMappingsInternal()

in src/main/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImpl.java [107:203]


    private Collection<String> getAllMappingsInternal(final String resourcePath, final RequestContext requestContext) {

        // A note on the usage of the 'mappings' variable and the order of the results
        //
        // The API contract of the ResourceMapper does not specify the order in which the elements are returned
        // As an implementation detail however the getMapping method picks the first element of the return value
        // as the 'winner'.
        //
        // Therefore we take care to add the entries in a very particular order, which preserves backwards
        // compatibility with the existing implementation. Previously the order was
        //
        //   resource path → aliases → mapping (with aliases potentially being empty)
        //
        // To ensure we keep the same behaviour but expose all possible mappings, we now have the following
        // flow
        //
        //  resource path → mapping
        //  resource path → aliases
        //  aliases → mappings
        //
        // After all are processed we reverse the order to preserve the logic of the old ResourceResolver.map() method
        // (last
        // found wins) and also make sure that no duplicates are added.
        //
        // There is some room for improvement here by using a data structure that does not need reversing ( ArrayList
        // .add moves the elements every time ) or reversal of duplicates but since we will have a small number of
        // entries ( <= 4 in case of single aliases) the time spent here should be negligible.

        List<String> mappings = new ArrayList<>();

        // 1. parse parameters

        // find a fragment or query
        int fragmentQueryMark = resourcePath.indexOf('#');
        if (fragmentQueryMark < 0) {
            fragmentQueryMark = resourcePath.indexOf('?');
        }

        // cut fragment or query off the resource path
        String mappedPath;
        final String fragmentQuery;
        if (fragmentQueryMark >= 0) {
            fragmentQuery = resourcePath.substring(fragmentQueryMark);
            mappedPath = resourcePath.substring(0, fragmentQueryMark);
            logger.debug(
                    "map: Splitting resource path '{}' into '{}' and '{}'", resourcePath, mappedPath, fragmentQuery);
        } else {
            fragmentQuery = null;
            mappedPath = resourcePath;
        }

        ParsedParameters parsed = new ParsedParameters(mappedPath);

        // 2. load mappings from the resource path
        populateMappingsFromMapEntries(mappings, Collections.singletonList(mappedPath), requestContext);

        // 3. load aliases
        final Resource nonDecoratedResource = resolver.resolveInternal(parsed.getRawPath(), parsed.getParameters());
        if (nonDecoratedResource != null) {
            List<String> aliases = loadAliasesIfApplicable(nonDecoratedResource);
            // ensure that the first declared alias will be returned first
            Collections.reverse(aliases);

            // 4. load mappings for alias
            mappings.addAll(aliases);
            populateMappingsFromMapEntries(mappings, aliases, requestContext);
        }

        // 5. add the requested path itself, if not already populated
        if (!mappedPath.isEmpty() && !mappings.contains(mappedPath)) mappings.add(0, mappedPath);

        // 6. add vanity paths
        List<String> vanityPaths = mapEntries.getVanityPathMappings().getOrDefault(mappedPath, Collections.emptyList());
        // vanity paths are prepended to make sure they get returned last
        mappings.addAll(0, vanityPaths);

        // 7. final effort to make sure we have at least one mapped path
        if (mappings.isEmpty()) {
            mappings.add(nonDecoratedResource != null ? nonDecoratedResource.getPath() : "/");
        }

        // 8. apply context path if needed
        mappings.replaceAll(new ApplyContextPath(requestContext));

        // 9. set back the fragment query if needed
        if (fragmentQuery != null) {
            mappings.replaceAll(path -> path.concat(fragmentQuery));
        }

        if (logger.isDebugEnabled()) {
            mappings.forEach(path -> logger.debug("map: Returning URL {} as mapping for path {}", path, resourcePath));
        }

        Collections.reverse(mappings);

        return new LinkedHashSet<>(mappings);
    }