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 2009 Sun Microsystems Inc.
015 * Portions Copyright 2010-2011 ApexIdentity Inc.
016 * Portions Copyright 2011-2015 ForgeRock AS.
017 */
018
019package org.forgerock.openig.util;
020
021import java.io.BufferedReader;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.InputStreamReader;
025import java.nio.charset.Charset;
026import java.text.Normalizer;
027import java.util.Arrays;
028import java.util.Iterator;
029
030/**
031 * Miscellaneous string utility methods.
032 */
033public final class StringUtil {
034
035    /** Platform specific end of line character. */
036    private static final String EOL = System.getProperty("line.separator");
037
038    /**
039     * Static methods only.
040     */
041    private StringUtil() { }
042
043    /**
044     * Joins a collection of elements into a single string value, with a specified separator.
045     *
046     * @param separator the separator to place between joined elements.
047     * @param elements the collection of strings to be joined.
048     * @return the string containing the joined elements.
049     */
050    public static String join(String separator, Iterable<?> elements) {
051        StringBuilder sb = new StringBuilder();
052        for (Iterator<?> i = elements.iterator(); i.hasNext();) {
053            sb.append(i.next());
054            if (i.hasNext() && separator != null) {
055                sb.append(separator);
056            }
057        }
058        return sb.toString();
059    }
060
061    /**
062     * Joins an array of strings into a single string value, with a specified separator.
063     *
064     * @param separator the separator to place between joined elements.
065     * @param elements the array of strings to be joined.
066     * @return the string containing the joined string array.
067     */
068    public static String join(String separator, Object... elements) {
069        return join(separator, Arrays.asList(elements));
070    }
071
072    /**
073     * Reads the provided input stream as a string and then closes the stream.
074     *
075     * @param is
076     *            the input stream to be read.
077     * @param charset
078     *            the character set encoding of the input stream.
079     * @return the content of the stream.
080     * @throws IOException
081     *             If an I/O error occurs.
082     */
083    public static String asString(final InputStream is, Charset charset) throws IOException {
084        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset))) {
085            final String firstLine = reader.readLine();
086            if (firstLine == null) {
087                return "";
088            }
089            final StringBuilder builder = new StringBuilder(firstLine);
090            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
091                builder.append(EOL);
092                builder.append(line);
093            }
094            return builder.toString();
095        }
096    }
097
098    /**
099     * Appends a final slash on a given value.
100     *
101     * @param value
102     *            The given string.
103     * @return A string ending with a slash.
104     */
105    public static String trailingSlash(String value) {
106        if (value == null
107                || (value != null && value.endsWith("/"))) {
108            return value;
109        }
110        return value.concat("/");
111    }
112
113    /**
114     * Transform the input String value into a slug: a simpler adaptation that is compatible for usage inside an URI
115     * (without requiring URL encoding).
116     *
117     * <p>Examples:
118     * <pre>
119     *     {@code slug("A sentence  with blanks, commas and extra punctuation !  ")
120     *            .equals("a-sentence-with-blanks-commas-and-extra-punctuation");
121     *       slug("{ClientHandler}/heap/2").equals(clienthandler-heap-2);
122     *     }
123     * </pre>
124     *
125     * @param value
126     *         value to be transformed
127     * @return A slug version of the input
128     */
129    public static String slug(String value) {
130        if (value == null) {
131            return null;
132        }
133        // 1. Decompose unicode characters
134        // 2. Remove all combining diacritical marks and also everything that isn't a word, a whitespace character, a
135        // dash or a slash
136        // 3. Replace all occurrences of whitespaces or dashes or slashes with one single whitespace
137        // 4. Trim
138        // 5. Replace all (middle) blanks with a dash
139        return Normalizer.normalize(value.toLowerCase(), Normalizer.Form.NFD)
140                         .replaceAll("\\p{InCombiningDiacriticalMarks}|[^\\w\\s\\-/]", "")
141                         .replaceAll("[\\s\\-/]+", " ")
142                         .trim()
143                         .replaceAll("\\s", "-");
144    }
145
146}