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-2016 ForgeRock AS.
015 */
016package org.forgerock.util;
017
018import java.io.Closeable;
019import java.io.IOException;
020import java.util.Arrays;
021import java.util.Iterator;
022import java.util.concurrent.ThreadFactory;
023import java.util.concurrent.atomic.AtomicInteger;
024
025/**
026 * Common utility methods.
027 */
028public final class Utils {
029
030    /**
031     * Closes the provided resources ignoring any errors which occurred.
032     *
033     * @param resources
034     *            The resources to be closed, which may be {@code null}.
035     */
036    public static void closeSilently(final Closeable... resources) {
037        if (resources == null) {
038            return;
039        }
040        closeSilently(Arrays.asList(resources));
041    }
042
043    /**
044     * Closes the provided resources ignoring any errors which occurred.
045     *
046     * @param resources
047     *            The resources to be closed, which may be {@code null}.
048     */
049    public static void closeSilently(final Iterable<? extends Closeable> resources) {
050        if (resources == null) {
051            return;
052        }
053        for (final Closeable r : resources) {
054            try {
055                if (r != null) {
056                    r.close();
057                }
058            } catch (final IOException ignored) {
059                // Ignore.
060            }
061        }
062    }
063
064    /**
065     * Returns a string whose content is the string representation of the
066     * provided objects concatenated together using the provided separator.
067     *
068     * @param separator
069     *            The separator string.
070     * @param values
071     *            The objects to be joined.
072     * @return A string whose content is the string representation of the
073     *         provided objects concatenated together using the provided
074     *         separator.
075     * @throws NullPointerException
076     *             If {@code values} or {@code separator} were {@code null}.
077     */
078    public static String joinAsString(final String separator, final Object... values) {
079        return joinAsString(separator, Arrays.asList(values));
080    }
081
082    /**
083     * Returns a string whose content is the string representation of the
084     * objects contained in the provided iterable concatenated together using
085     * the provided separator.
086     *
087     * @param separator
088     *            The separator string.
089     * @param values
090     *            The iterable whose elements are to be joined.
091     * @return A string whose content is the string representation of the
092     *         objects contained in the provided iterable concatenated
093     *         together using the provided separator.
094     * @throws NullPointerException
095     *             If {@code separator} or {@code values} were {@code null}.
096     */
097    public static String joinAsString(final String separator, final Iterable<?> values) {
098        Reject.ifNull(separator);
099        Reject.ifNull(values);
100
101        final StringBuilder builder = new StringBuilder();
102        joinAsString(builder, separator, values);
103        return builder.toString();
104    }
105
106    /**
107     * Appends into the provided {@link StringBuilder} the string representation
108     * of the provided objects concatenated together using the provided separator.
109     *
110     * @param builder
111     *            The String builder where to append.
112     * @param separator
113     *            The separator string.
114     * @param values
115     *            The objects to be joined.
116     * @throws NullPointerException
117     *             If {@code builder}, {@code separator} or {@code values} were {@code null}.
118     */
119    public static void joinAsString(final StringBuilder builder, final String separator, final Object... values) {
120        joinAsString(builder, separator, Arrays.asList(values));
121    }
122
123    /**
124     * Appends into the provided {@link StringBuilder} the string representation
125     * of the objects contained in the provided iterable concatenated together
126     * using the provided separator.
127     *
128     * @param builder
129     *            The String builder where to append.
130     * @param separator
131     *            The separator string.
132     * @param values
133     *            The iterable whose elements are to be joined.
134     * @throws NullPointerException
135     *             If {@code builder}, {@code separator} or {@code values} were {@code null}.
136     */
137    public static void joinAsString(final StringBuilder builder, final String separator, final Iterable<?> values) {
138        Reject.ifNull(builder);
139        Reject.ifNull(separator);
140        Reject.ifNull(values);
141
142        final Iterator<?> iterator = values.iterator();
143        if (iterator.hasNext()) {
144            builder.append(iterator.next());
145
146            while (iterator.hasNext()) {
147                builder.append(separator);
148                builder.append(iterator.next());
149            }
150        }
151    }
152
153    /**
154     * Creates a new thread factory which will create threads using the
155     * specified thread group, naming template, and daemon status.
156     *
157     * @param group
158     *            The thread group, which may be {@code null}.
159     * @param nameTemplate
160     *            The thread name format string which may contain a "%d" format
161     *            option which will be substituted with the thread count.
162     * @param isDaemon
163     *            Indicates whether or not threads should be daemon threads.
164     * @return The new thread factory.
165     */
166    public static ThreadFactory newThreadFactory(final ThreadGroup group,
167            final String nameTemplate, final boolean isDaemon) {
168        return new ThreadFactory() {
169            private final AtomicInteger count = new AtomicInteger();
170
171            @Override
172            public Thread newThread(final Runnable r) {
173                final String name = String.format(nameTemplate, count.getAndIncrement());
174                final Thread t = new Thread(group, r, name);
175                t.setDaemon(isDaemon);
176                return t;
177            }
178        };
179    }
180
181    /**
182     * Returns the string value as an enum constant of the specified enum
183     * type. The string value and enum constants are compared, ignoring case
184     * considerations. If the string value is {@code null}, this method returns
185     * {@code null}.
186     *
187     * @param <T>
188     *            the enum type sub-class.
189     * @param value
190     *            the string value
191     * @param type
192     *            the enum type to match constants with the value.
193     * @return the enum constant represented by the string value.
194     * @throws IllegalArgumentException
195     *             if {@code type} does not represent an enum type,
196     *             of if {@code value} does not match one of the enum constants
197     * @throws NullPointerException
198     *             if {@code type} is {@code null}.
199     */
200    public static <T extends Enum<T>> T asEnum(final String value, final Class<T> type) {
201        if (value == null) {
202            return null;
203        }
204        final T[] constants = type.getEnumConstants();
205        if (constants == null) {
206            throw new IllegalArgumentException("Type is not an enum class");
207        }
208        for (final T constant : constants) {
209            if (value.equalsIgnoreCase(constant.toString())) {
210                return constant;
211            }
212        }
213        final StringBuilder sb = new StringBuilder("Expecting String containing one of: ");
214        sb.append(joinAsString(" ", (Object[]) constants));
215        throw new IllegalArgumentException(sb.toString());
216    }
217
218    /**
219     * Check to see if the provided String is {@code null} or empty.
220     * @param value The value to check.
221     * @return {@code true} if the value is either {@code null} or is empty.
222     */
223    public static boolean isNullOrEmpty(String value) {
224        return value == null || value.isEmpty();
225    }
226
227    /**
228     * Check to see if a character sequence is null or blank.
229     *
230     * @param charSeq Sequence to test (String is also a CharSequence)
231     * @return true if the char sequence is null or blank.
232     */
233    public static boolean isBlank(CharSequence charSeq) {
234        if (charSeq == null) {
235            return true;
236        }
237        final int length = charSeq.length();
238        for (int i = 0; i < length; i++) {
239            if (!Character.isWhitespace(charSeq.charAt(i))) {
240                return false;
241            }
242        }
243        return true;
244    }
245
246
247    private Utils() {
248        // Prevent instantiation.
249    }
250}