001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015 ForgeRock AS.
015 */
016
017package org.forgerock.http.util;
018
019import static java.util.Collections.*;
020
021import java.util.ArrayList;
022import java.util.List;
023import java.util.regex.Pattern;
024
025/**
026 * Utilities for manipulating paths.
027 */
028public final class Paths {
029    private static final Pattern PATH_SPLIT_PATTERN = Pattern.compile("/");
030
031    /**
032     * Returns the URL path decoding of the provided object's string
033     * representation.
034     *
035     * @param value
036     *            The value to be URL path decoded.
037     * @return The URL path decoding of the provided object's string
038     *         representation.
039     */
040    public static String urlDecode(final Object value) {
041        return Uris.urlDecodePathElement(value.toString());
042    }
043
044    /**
045     * Returns the URL path encoding of the provided object's string
046     * representation.
047     *
048     * @param value
049     *            The value to be URL path encoded.
050     * @return The URL path encoding of the provided object's string
051     *         representation.
052     */
053    public static String urlEncode(final Object value) {
054        return Uris.urlEncodePathElement(value.toString());
055    }
056
057    /**
058     * Converts a path into a list of URL-decoded path elements. If the leading path element
059     * is empty it is dropped, meaning that {@code null}, {@code ""} and {@code "/"} will
060     * all return an empty list, and {@code "//"} will return a list with two elements, both
061     * empty strings, as all intermediate and trailing empty paths are retained.
062     *
063     * @param rawPath The raw, URL-encoded path string.
064     * @return An immutable list of the path elements.
065     */
066    public static List<String> getPathElements(String rawPath) {
067        String[] pathElements = null;
068        if (rawPath != null) {
069            if (rawPath.startsWith("/")) {
070                rawPath = rawPath.substring(1);
071            }
072            pathElements = PATH_SPLIT_PATTERN.split(rawPath, -1);
073            if (pathElements.length == 1 && pathElements[0].isEmpty()) {
074                pathElements = null;
075            }
076        }
077
078        List<String> elements;
079        if (pathElements == null) {
080            elements = emptyList();
081        } else {
082            List<String> decodedElements = new ArrayList<>(pathElements.length);
083            for (String element : pathElements) {
084                decodedElements.add(Paths.urlDecode(element));
085            }
086            elements = decodedElements;
087        }
088        return unmodifiableList(elements);
089    }
090
091    /**
092     * Joins a list of URL-decoded path elements into a url-encoded path.
093     * @param elements The list of (URL-decoded) elements.
094     * @return The raw path.
095     */
096    public static String joinPath(List<String> elements) {
097        if (elements == null) {
098            return "";
099        }
100        StringBuilder s = new StringBuilder();
101        for (String element : elements) {
102            if (s.length() > 0) {
103                s.append("/");
104            }
105            s.append(urlEncode(element));
106        }
107        return s.toString();
108    }
109
110    private Paths() {
111        // utilities only.
112    }
113}