EnglishWordsInstance.java

/*
 * Copyright (c) 2020, Stein Eldar Johnsen
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package net.morimekta.testing.text;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.ThreadLocalRandom.current;

/**
 * This is a very light-weight version of fairy, which just generates
 * simple pseudo-sentences in repeated SVO patterns.
 */
class EnglishWordsInstance {
    public String noun() {
        return requireNonNull(nouns.get(current().nextInt(nouns.size())));
    }

    public String thing() {
        if (current().nextBoolean()) {
            return adjective() + " " + noun();
        }
        return noun();
    }

    public String adjective() {
        return requireNonNull(adjectives.get(current().nextInt(adjectives.size())));
    }

    public String verb() {
        return requireNonNull(verbs.get(current().nextInt(verbs.size())));
    }

    public String word() {
        return requireNonNull(words.get(current().nextInt(words.size())));
    }

    public String sentence(int minLength) {
        StringBuilder sentence = new StringBuilder();
        while (sentence.length() < minLength) {
            if (sentence.length() > 0) {
                sentence.append(", and ")
                        .append(thing());
            } else {
                sentence.append(capitalize(thing()));
            }
            sentence.append(" ")
                    .append(verb())
                    .append(" ")
                    .append(thing());
        }
        return sentence.toString();
    }

    public String paragraph(int sentences) {
        return IntStream.range(0, sentences)
                        .sequential()
                        .map(i -> current().nextInt(50) + 20)
                        .mapToObj(this::sentence)
                        .collect(Collectors.joining(". ")) + ".";
    }

    public String cicero() {
        return cicero;
    }

    public String loremIpsum() {
        return loremIpsum;
    }

    public String loremIpsumOneline() {
        return loremIpsumOneline;
    }

    // --- Protected ---

    static EnglishWordsInstance getInstance() {
        return INSTANCE.updateAndGet(i -> {
            if (i == null) {
                i = new EnglishWordsInstance();
            }
            return i;
        });
    }

    // --- Private ---

    private static final System.Logger                         LOGGER   =
            System.getLogger(EnglishWordsInstance.class.getName());
    private static final AtomicReference<EnglishWordsInstance> INSTANCE =
            new AtomicReference<>();

    private final List<String> nouns;
    private final List<String> adjectives;
    private final List<String> verbs;
    private final List<String> words;
    private final String       loremIpsum;
    private final String       loremIpsumOneline;
    private final String       cicero;

    private EnglishWordsInstance() {
        try (InputStream in = requireNonNull(
                EnglishWordsInstance.class.getResourceAsStream("/net/morimekta/testing/text/words.txt"),
                "in == null");
             InputStreamReader reader = new InputStreamReader(in, UTF_8);
             BufferedReader bufferedReader = new BufferedReader(reader);
             Stream<String> lines = requireNonNull(bufferedReader.lines(), "line == null")) {
            List<String> nounsB = new ArrayList<>();
            List<String> adjectivesB = new ArrayList<>();
            List<String> verbsB = new ArrayList<>();
            List<String> wordsB = new ArrayList<>();

            lines.forEach(line -> {
                String[] word = line.split(" ", 3);
                if (word.length != 3 || word[0].isEmpty()) {
                    return;
                } else if (word[2].contains("adjective")) {
                    adjectivesB.add(word[0]);
                } else if (word[2].contains("verb")) {
                    verbsB.add(word[0]);
                } else if (word[2].contains("noun")) {
                    nounsB.add(word[0]);
                }
                wordsB.add(word[0]);
            });

            nouns = List.copyOf(nounsB);
            adjectives = List.copyOf(adjectivesB);
            verbs = List.copyOf(verbsB);
            words = List.copyOf(wordsB);
        } catch (IOException e) {
            LOGGER.log(System.Logger.Level.ERROR, "Failed to load words: " + e.getMessage(), e);
            throw new UncheckedIOException(e.getMessage(), e);
        }

        try (InputStream in = requireNonNull(
                EnglishWordsInstance.class.getResourceAsStream("/net/morimekta/testing/text/cicero.txt"),
                "in == null");
             InputStreamReader reader = new InputStreamReader(in, UTF_8);
             StringWriter writer = new StringWriter()) {
            reader.transferTo(writer);
            cicero = writer.toString();
        } catch (IOException e) {
            LOGGER.log(System.Logger.Level.ERROR, "Failed to load cicero: " + e.getMessage(), e);
            throw new UncheckedIOException(e.getMessage(), e);
        }

        try (InputStream in = requireNonNull(
                EnglishWordsInstance.class.getResourceAsStream("/net/morimekta/testing/text/loremipsum.txt"),
                "in == null");
             InputStreamReader reader = new InputStreamReader(in, UTF_8);
             StringWriter writer = new StringWriter()) {
            reader.transferTo(writer);
            loremIpsum = writer.toString();
            loremIpsumOneline = loremIpsum.replaceAll("\n", " ").trim();
        } catch (IOException e) {
            LOGGER.log(System.Logger.Level.ERROR, "Failed to load lorem ipsum: " + e.getMessage(), e);
            throw new UncheckedIOException(e.getMessage(), e);
        }
    }

    private static String capitalize(String word) {
        return word.substring(0, 1).toUpperCase(Locale.US) + word.substring(1);
    }
}