public Collection getAllMappings()

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


    public Collection<String> getAllMappings(String resourcePath, HttpServletRequest request) {
        
        resolver.checkClosed();
        
        // 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;
        }

        final RequestContext requestContext = new RequestContext(request, 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(request));
       
        // 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);
    }