001// Copyright 2007, 2013 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.test;
016
017import org.slf4j.Logger;
018import org.slf4j.LoggerFactory;
019
020import java.io.*;
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Random;
024
025/**
026 * Provides access to random data that can be used when populating a test database with "reasonable" data. The majority
027 * of this is access to random words from an american english dictionary, which can be strung together to form names,
028 * sentences and paragraphs.
029 */
030public final class RandomDataSource
031{
032    private static final Logger LOGGER = LoggerFactory.getLogger(RandomDataSource.class);
033
034    private final Random random = new Random(System.currentTimeMillis());
035
036    private final static List<String> words = new ArrayList<String>();
037
038    private static void loadWords()
039    {
040        if (words.isEmpty())
041        {
042
043            for (int i = 0; i < 4; i++)
044                readWords("english." + i);
045
046            for (int i = 0; i < 3; i++)
047                readWords("american." + i);
048
049            LOGGER.info(String.format("Dictionary contains %,d words.", words.size()));
050        }
051    }
052
053    private static void readWords(String name)
054    {
055        LOGGER.info("Reading {} ...", name);
056
057        int count = 0;
058
059        InputStream is = RandomDataSource.class.getResourceAsStream(name);
060
061        if (is == null) throw new RuntimeException(String.format("File '%s' not found.", name));
062
063        try
064        {
065            BufferedInputStream bis = new BufferedInputStream(is);
066            InputStreamReader isr = new InputStreamReader(bis);
067            LineNumberReader r = new LineNumberReader(isr);
068
069            while (true)
070            {
071                String word = r.readLine();
072
073                if (word == null) break;
074
075                count++;
076                words.add(word);
077            }
078
079            r.close();
080        } catch (IOException ex)
081        {
082            throw new RuntimeException(String.format("Error reading '%s': %s", name + ex.getMessage()), ex);
083        }
084
085        LOGGER.info(String.format("... %,d words", count));
086    }
087
088    public boolean maybe(int percent)
089    {
090        assert percent > 0 && percent <= 100;
091
092        return random.nextInt(100) < percent;
093    }
094
095    public int random(int min, int max)
096    {
097        assert min <= max;
098
099        return random.nextInt(max - min + 1) + min;
100    }
101
102    /**
103     * Returns a random word frm the dictionary. These words are usually all lowercase.
104     */
105    public String word()
106    {
107        loadWords();
108
109        int index = random.nextInt(words.size());
110
111        return words.get(index);
112    }
113
114    /**
115     * Returns a random word, capitalized. Useful when create random names.
116     */
117    public String capitalizedWord()
118    {
119        String word = word();
120
121        char[] chars = word.toCharArray();
122
123        chars[0] = Character.toUpperCase(chars[0]);
124
125        return new String(chars);
126    }
127
128    /**
129     * Returns a word that is "safe" for use in an email address.
130     */
131    public String safeWord()
132    {
133        String word = word();
134
135        int x = word.indexOf('\'');
136
137        return x < 0 ? word : word.substring(0, x);
138    }
139
140    /**
141     * Returns a random value from the list of values supplied.
142     */
143    public <T> T oneOf(T... values)
144    {
145        assert values.length > 0;
146
147        int index = random.nextInt(values.length);
148
149        return values[index];
150    }
151
152    /**
153     * Returns a random enum value, given the enum type.
154     */
155    public <T extends Enum> T oneOf(Class<T> enumClass)
156    {
157        return oneOf(enumClass.getEnumConstants());
158    }
159
160    /**
161     * Creates a space-separated list of random words. If in sentence form, then the first word is capitalized, and a
162     * period is appended.
163     *
164     * @param minWords
165     *         minimun number of words in the list
166     * @param maxWords
167     *         maximum number of words in the list
168     * @param asSentence
169     *         if true, the output is "dressed up" as a non-sensical sentence
170     * @return the word list / sentence
171     */
172    public String wordList(int minWords, int maxWords, boolean asSentence)
173    {
174        assert minWords <= maxWords;
175        assert minWords > 0;
176
177        StringBuilder builder = new StringBuilder();
178
179        int count = random(minWords, maxWords);
180
181        for (int i = 0; i < count; i++)
182        {
183
184            if (i > 0) builder.append(' ');
185
186            if (i == 0 && asSentence)
187                builder.append(capitalizedWord());
188            else
189                builder.append(word());
190        }
191
192        if (asSentence) builder.append('.');
193
194        return builder.toString();
195    }
196
197    /**
198     * Strings together a random number of word lists (in sentence form) to create something that looks like a
199     * paragraph.
200     *
201     * @param minSentences
202     *         per paragraph
203     * @param maxSentences
204     *         per paragraph
205     * @param minWords
206     *         per sentence
207     * @param maxWords
208     *         per sentence
209     * @return the random paragraph
210     */
211    public String paragraph(int minSentences, int maxSentences, int minWords, int maxWords)
212    {
213        assert minSentences < maxSentences;
214        assert minSentences > 0;
215
216        int count = random(minSentences, maxSentences);
217
218        StringBuilder builder = new StringBuilder();
219
220        for (int i = 0; i < count; i++)
221        {
222            if (i > 0) builder.append(' ');
223
224            builder.append(wordList(minWords, maxWords, true));
225        }
226
227        return builder.toString();
228    }
229}