001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 * 
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 * 
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3;
018
019import java.util.Random;
020
021/**
022 * <p>Operations for random {@code String}s.</p>
023 * <p>Currently <em>private high surrogate</em> characters are ignored. 
024 * These are Unicode characters that fall between the values 56192 (db80)
025 * and 56319 (dbff) as we don't know how to handle them. 
026 * High and low surrogates are correctly dealt with - that is if a 
027 * high surrogate is randomly chosen, 55296 (d800) to 56191 (db7f) 
028 * then it is followed by a low surrogate. If a low surrogate is chosen, 
029 * 56320 (dc00) to 57343 (dfff) then it is placed after a randomly 
030 * chosen high surrogate. </p>
031 *
032 * <p>#ThreadSafe#</p>
033 * @since 1.0
034 * @version $Id: RandomStringUtils.java 1532684 2013-10-16 08:28:42Z bayard $
035 */
036public class RandomStringUtils {
037
038    /**
039     * <p>Random object used by random method. This has to be not local
040     * to the random method so as to not return the same value in the 
041     * same millisecond.</p>
042     */
043    private static final Random RANDOM = new Random();
044
045    /**
046     * <p>{@code RandomStringUtils} instances should NOT be constructed in
047     * standard programming. Instead, the class should be used as
048     * {@code RandomStringUtils.random(5);}.</p>
049     *
050     * <p>This constructor is public to permit tools that require a JavaBean instance
051     * to operate.</p>
052     */
053    public RandomStringUtils() {
054      super();
055    }
056
057    // Random
058    //-----------------------------------------------------------------------
059    /**
060     * <p>Creates a random string whose length is the number of characters
061     * specified.</p>
062     *
063     * <p>Characters will be chosen from the set of all characters.</p>
064     *
065     * @param count  the length of random string to create
066     * @return the random string
067     */
068    public static String random(final int count) {
069        return random(count, false, false);
070    }
071
072    /**
073     * <p>Creates a random string whose length is the number of characters
074     * specified.</p>
075     *
076     * <p>Characters will be chosen from the set of characters whose
077     * ASCII value is between {@code 32} and {@code 126} (inclusive).</p>
078     *
079     * @param count  the length of random string to create
080     * @return the random string
081     */
082    public static String randomAscii(final int count) {
083        return random(count, 32, 127, false, false);
084    }
085    
086    /**
087     * <p>Creates a random string whose length is the number of characters
088     * specified.</p>
089     *
090     * <p>Characters will be chosen from the set of alphabetic
091     * characters.</p>
092     *
093     * @param count  the length of random string to create
094     * @return the random string
095     */
096    public static String randomAlphabetic(final int count) {
097        return random(count, true, false);
098    }
099    
100    /**
101     * <p>Creates a random string whose length is the number of characters
102     * specified.</p>
103     *
104     * <p>Characters will be chosen from the set of alpha-numeric
105     * characters.</p>
106     *
107     * @param count  the length of random string to create
108     * @return the random string
109     */
110    public static String randomAlphanumeric(final int count) {
111        return random(count, true, true);
112    }
113    
114    /**
115     * <p>Creates a random string whose length is the number of characters
116     * specified.</p>
117     *
118     * <p>Characters will be chosen from the set of numeric
119     * characters.</p>
120     *
121     * @param count  the length of random string to create
122     * @return the random string
123     */
124    public static String randomNumeric(final int count) {
125        return random(count, false, true);
126    }
127
128    /**
129     * <p>Creates a random string whose length is the number of characters
130     * specified.</p>
131     *
132     * <p>Characters will be chosen from the set of alpha-numeric
133     * characters as indicated by the arguments.</p>
134     *
135     * @param count  the length of random string to create
136     * @param letters  if {@code true}, generated string may include
137     *  alphabetic characters
138     * @param numbers  if {@code true}, generated string may include
139     *  numeric characters
140     * @return the random string
141     */
142    public static String random(final int count, final boolean letters, final boolean numbers) {
143        return random(count, 0, 0, letters, numbers);
144    }
145    
146    /**
147     * <p>Creates a random string whose length is the number of characters
148     * specified.</p>
149     *
150     * <p>Characters will be chosen from the set of alpha-numeric
151     * characters as indicated by the arguments.</p>
152     *
153     * @param count  the length of random string to create
154     * @param start  the position in set of chars to start at
155     * @param end  the position in set of chars to end before
156     * @param letters  if {@code true}, generated string may include
157     *  alphabetic characters
158     * @param numbers  if {@code true}, generated string may include
159     *  numeric characters
160     * @return the random string
161     */
162    public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers) {
163        return random(count, start, end, letters, numbers, null, RANDOM);
164    }
165
166    /**
167     * <p>Creates a random string based on a variety of options, using
168     * default source of randomness.</p>
169     *
170     * <p>This method has exactly the same semantics as
171     * {@link #random(int,int,int,boolean,boolean,char[],Random)}, but
172     * instead of using an externally supplied source of randomness, it uses
173     * the internal static {@link Random} instance.</p>
174     *
175     * @param count  the length of random string to create
176     * @param start  the position in set of chars to start at
177     * @param end  the position in set of chars to end before
178     * @param letters  only allow letters?
179     * @param numbers  only allow numbers?
180     * @param chars  the set of chars to choose randoms from.
181     *  If {@code null}, then it will use the set of all chars.
182     * @return the random string
183     * @throws ArrayIndexOutOfBoundsException if there are not
184     *  {@code (end - start) + 1} characters in the set array.
185     */
186    public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers, final char... chars) {
187        return random(count, start, end, letters, numbers, chars, RANDOM);
188    }
189
190    /**
191     * <p>Creates a random string based on a variety of options, using
192     * supplied source of randomness.</p>
193     *
194     * <p>If start and end are both {@code 0}, start and end are set
195     * to {@code ' '} and {@code 'z'}, the ASCII printable
196     * characters, will be used, unless letters and numbers are both
197     * {@code false}, in which case, start and end are set to
198     * {@code 0} and {@code Integer.MAX_VALUE}.
199     *
200     * <p>If set is not {@code null}, characters between start and
201     * end are chosen.</p>
202     *
203     * <p>This method accepts a user-supplied {@link Random}
204     * instance to use as a source of randomness. By seeding a single 
205     * {@link Random} instance with a fixed seed and using it for each call,
206     * the same random sequence of strings can be generated repeatedly
207     * and predictably.</p>
208     *
209     * @param count  the length of random string to create
210     * @param start  the position in set of chars to start at
211     * @param end  the position in set of chars to end before
212     * @param letters  only allow letters?
213     * @param numbers  only allow numbers?
214     * @param chars  the set of chars to choose randoms from, must not be empty.
215     *  If {@code null}, then it will use the set of all chars.
216     * @param random  a source of randomness.
217     * @return the random string
218     * @throws ArrayIndexOutOfBoundsException if there are not
219     *  {@code (end - start) + 1} characters in the set array.
220     * @throws IllegalArgumentException if {@code count} &lt; 0 or the provided chars array is empty.
221     * @since 2.0
222     */
223    public static String random(int count, int start, int end, final boolean letters, final boolean numbers,
224                                final char[] chars, final Random random) {
225        if (count == 0) {
226            return "";
227        } else if (count < 0) {
228            throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
229        }
230        if (chars != null && chars.length == 0) {
231            throw new IllegalArgumentException("The chars array must not be empty");
232        }
233
234        if (start == 0 && end == 0) {
235            if (chars != null) {
236                end = chars.length;
237            } else {
238                if (!letters && !numbers) {
239                    end = Integer.MAX_VALUE;
240                } else {
241                    end = 'z' + 1;
242                    start = ' ';                
243                }
244            }
245        } else {
246            if (end <= start) {
247                throw new IllegalArgumentException("Parameter end (" + end + ") must be greater than start (" + start + ")");
248            }
249        }
250
251        final char[] buffer = new char[count];
252        final int gap = end - start;
253
254        while (count-- != 0) {
255            char ch;
256            if (chars == null) {
257                ch = (char) (random.nextInt(gap) + start);
258            } else {
259                ch = chars[random.nextInt(gap) + start];
260            }
261            if (letters && Character.isLetter(ch)
262                    || numbers && Character.isDigit(ch)
263                    || !letters && !numbers) {
264                if(ch >= 56320 && ch <= 57343) {
265                    if(count == 0) {
266                        count++;
267                    } else {
268                        // low surrogate, insert high surrogate after putting it in
269                        buffer[count] = ch;
270                        count--;
271                        buffer[count] = (char) (55296 + random.nextInt(128));
272                    }
273                } else if(ch >= 55296 && ch <= 56191) {
274                    if(count == 0) {
275                        count++;
276                    } else {
277                        // high surrogate, insert low surrogate before putting it in
278                        buffer[count] = (char) (56320 + random.nextInt(128));
279                        count--;
280                        buffer[count] = ch;
281                    }
282                } else if(ch >= 56192 && ch <= 56319) {
283                    // private high surrogate, no effing clue, so skip it
284                    count++;
285                } else {
286                    buffer[count] = ch;
287                }
288            } else {
289                count++;
290            }
291        }
292        return new String(buffer);
293    }
294
295    /**
296     * <p>Creates a random string whose length is the number of characters
297     * specified.</p>
298     *
299     * <p>Characters will be chosen from the set of characters
300     * specified by the string, must not be empty. 
301     * If null, the set of all characters is used.</p>
302     *
303     * @param count  the length of random string to create
304     * @param chars  the String containing the set of characters to use,
305     *  may be null, but must not be empty
306     * @return the random string
307     * @throws IllegalArgumentException if {@code count} &lt; 0 or the string is empty.
308     */
309    public static String random(final int count, final String chars) {
310        if (chars == null) {
311            return random(count, 0, 0, false, false, null, RANDOM);
312        }
313        return random(count, chars.toCharArray());
314    }
315
316    /**
317     * <p>Creates a random string whose length is the number of characters
318     * specified.</p>
319     *
320     * <p>Characters will be chosen from the set of characters specified.</p>
321     *
322     * @param count  the length of random string to create
323     * @param chars  the character array containing the set of characters to use,
324     *  may be null
325     * @return the random string
326     * @throws IllegalArgumentException if {@code count} &lt; 0.
327     */
328    public static String random(final int count, final char... chars) {
329        if (chars == null) {
330            return random(count, 0, 0, false, false, null, RANDOM);
331        }
332        return random(count, 0, chars.length, false, false, chars, RANDOM);
333    }
334    
335}