All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.data.LazyString Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj.data;

import fj.*;

import static fj.Function.compose;
import static fj.Function.curry;
import static fj.P.p;
import static fj.data.Option.none;
import static fj.data.Option.some;
import static fj.data.Stream.join;
import static fj.function.Booleans.or;
import static fj.function.Characters.isSpaceChar;
import static fj.Equal.charEqual;
import static fj.Equal.streamEqual;

import java.util.regex.Pattern;

/**
 * A lazy (non-evaluated) immutable character string.
 */
public final class LazyString implements CharSequence {
  private final Stream s;

  private LazyString(final Stream s) {
    this.s = s;
  }

  /**
   * Constructs a lazy string from a String.
   *
   * @param s A string from which to construct a lazy string.
   * @return A lazy string with the characters from the given string.
   */
  public static LazyString str(final String s) {
    return new LazyString(Stream.unfold(o -> {
        final String s2 = o._1();
        final int n = o._2();
        final Option>> none = none();
        return s2.length() <= n ? none : some(p(s2.charAt(n), p(s2, n + 1)));
      }, p(s, 0)));
  }

  /**
   * The empty string.
   */
  public static final LazyString empty = str("");

  /**
   * Constructs a lazy string from a stream of characters.
   *
   * @param s A stream of characters.
   * @return A lazy string with the characters from the given stream.
   */
  public static LazyString fromStream(final Stream s) {
    return new LazyString(s);
  }

  /**
   * Gives a stream representation of this lazy string.
   *
   * @return A stream representation of this lazy string.
   */
  public Stream toStream() {
    return s;
  }

  /**
   * The length of the lazy string. Note that this operation is O(n).
   *
   * @return The length of this lazy string.
   */
  public int length() {
    return s.length();
  }

  /**
   * Returns the caracter at the specified index.
   *
   * @param index The index for the character to be returned.
   * @return The character at the specified index.
   */
  public char charAt(final int index) {
    return s.index(index);
  }

  /**
   * Gets the specified subsequence of this lazy string.
   * This operation does not fail for indexes that are out of bounds. If the start index is past the end
   * of this lazy string, then the resulting character sequence will be empty. If the end index is past the
   * end of this lazy string, then the resulting character sequence will be truncated.
   *
   * @param start The character index of this lazy string at which to start the subsequence.
   * @param end   The character index of this lazy string at which to end the subsequence.
   * @return A character sequence containing the specified character subsequence.
   */
  public CharSequence subSequence(final int start, final int end) {
    return fromStream(s.drop(start).take(end - start));
  }

  /**
   * Returns the String representation of this lazy string.
   *
   * @return The String representation of this lazy string.
   */
  public String toStringEager() {
    final StringBuilder builder = new StringBuilder(length() + 16);
    s.foreachDoEffect(c -> builder.append(c.charValue()));
    return builder.toString();
  }

  public String toStringLazy() {
    return s.isEmpty() ? "" : "LazyString(" + Show.charShow.showS(s.head()) + ", ?)";
  }

  @Override
  public String toString() {
    return toStringLazy();
  }

  public String eval() {
    return toStringEager();
  }

  /**
   * Appends the given lazy string to the end of this lazy string.
   *
   * @param cs A lazy string to append to this one.
   * @return A new lazy string that is the concatenation of this string and the given string.
   */
  public LazyString append(final LazyString cs) {
    return fromStream(s.append(cs.s));
  }

  /**
   * Appends the given String to the end of this lazy string.
   *
   * @param s A String to append to this lazy string.
   * @return A new lazy string that is the concatenation of this lazy string and the given string.
   */
  public LazyString append(final String s) {
    return append(str(s));
  }

  /**
   * Returns true if the given lazy string is a substring of this lazy string.
   *
   * @param cs A substring to find in this lazy string.
   * @return True if the given string is a substring of this string, otherwise False.
   */
  public boolean contains(final LazyString cs) {
    return or(s.tails().map(compose(startsWith().f(cs), fromStream)));
  }

  /**
   * Returns true if the given lazy string is a suffix of this lazy string.
   *
   * @param cs A string to find at the end of this lazy string.
   * @return True if the given string is a suffix of this lazy string, otherwise False.
   */
  public boolean endsWith(final LazyString cs) {
    return reverse().startsWith(cs.reverse());
  }

  /**
   * Returns true if the given lazy string is a prefix of this lazy string.
   *
   * @param cs A string to find at the start of this lazy string.
   * @return True if the given string is a prefix of this lazy string, otherwise False.
   */
  public boolean startsWith(final LazyString cs) {
    return cs.isEmpty() || !isEmpty() && charEqual.eq(head(), cs.head()) && tail().startsWith(cs.tail());
  }


  /**
   * First-class prefix check.
   *
   * @return A function that yields true if the first argument is a prefix of the second.
   */
  public static F> startsWith() {
    return curry((needle, haystack) -> haystack.startsWith(needle));
  }

  /**
   * Returns the first character of this string.
   *
   * @return The first character of this string, or error if the string is empty.
   */
  public char head() {
    return s.head();
  }

  /**
   * Returns all but the first character of this string.
   *
   * @return All but the first character of this string, or error if the string is empty.
   */
  public LazyString tail() {
    return fromStream(s.tail()._1());
  }

  /**
   * Checks if this string is empty.
   *
   * @return True if there are no characters in this string, otherwise False.
   */
  public boolean isEmpty() {
    return s.isEmpty();
  }

  /**
   * Returns the reverse of this string.
   *
   * @return the reverse of this string.
   */
  public LazyString reverse() {
    return fromStream(s.reverse());
  }

  /**
   * Returns the first index of the given character in this lazy string, if present.
   *
   * @param c A character to find in this lazy string.
   * @return The first index of the given character in this lazy string, or None if the character is not present.
   */
  public Option indexOf(final char c) {
    return s.indexOf(charEqual.eq(c));
  }

  /**
   * Returns the first index of the given substring in this lazy string, if present.
   *
   * @param cs A substring to find in this lazy string.
   * @return The first index of the given substring in this lazy string, or None if there is no such substring.
   */
  public Option indexOf(final LazyString cs) {
    return s.substreams().indexOf(eqS.eq(cs.s));
  }

  /**
   * Regular expression pattern matching.
   *
   * @param regex A regular expression to match this lazy string.
   * @return True if this string mathches the given regular expression, otherwise False.
   */
  public boolean matches(final String regex) {
    return Pattern.matches(regex, this);
  }

  /**
   * Splits this lazy string by characters matching the given predicate.
   *
   * @param p A predicate that matches characters to be considered delimiters.
   * @return A stream of the substrings in this lazy string, when separated by the given predicate.
   */
  public Stream split(final F p) {
    final Stream findIt = s.dropWhile(p);
    final P2, Stream> ws = findIt.split(p);
    return findIt.isEmpty() ? Stream.nil()
                            : Stream.cons(fromStream(ws._1()), () -> fromStream(ws._2()).split(p));
  }

  public LazyString map(F f) {
    return fromStream(s.map(f));
  }

  public LazyString bind(F f) {
    return fromStream(s.bind(c -> f.f(c).toStream()));
  }

  /**
   * Splits this lazy string by the given delimiter character.
   *
   * @param c A delimiter character at which to split.
   * @return A stream of substrings of this lazy string, when separated by the given delimiter.
   */
  public Stream split(final char c) {
    return split(charEqual.eq(c));
  }

  /**
   * Splits this lazy string into words by spaces.
   *
   * @return A stream of the words in this lazy string, when split by spaces.
   */
  public Stream words() {
    return split(isSpaceChar);
  }

  /**
   * Splits this lazy string into lines.
   *
   * @return A stream of the lines in this lazy string, when split by newlines.
   */
  public Stream lines() {
    return split('\n');
  }

  public static F> lines_() {
    return LazyString::lines;
  }

  /**
   * Joins the given stream of lazy strings into one, separated by newlines.
   *
   * @param str A stream of lazy strings to join by newlines.
   * @return A new lazy string, consisting of the given strings separated by newlines.
   */
  public static LazyString unlines(final Stream str) {
    return fromStream(join(str.intersperse(str("\n")).map(toStream)));
  }

  public static F, LazyString> unlines_() {
    return LazyString::unlines;
  }

  /**
   * Joins the given stream of lazy strings into one, separated by spaces.
   *
   * @param str A stream of lazy strings to join by spaces.
   * @return A new lazy string, consisting of the given strings with spaces in between.
   */
  public static LazyString unwords(final Stream str) {
    return fromStream(join(str.intersperse(str(" ")).map(toStream)));
  }

  /**
   * First-class conversion from lazy strings to streams.
   */
  public static final F> toStream =
          LazyString::toStream;

  /**
   * First-class conversion from lazy strings to String.
   */
  public static final F toString =
          LazyString::toString;

  /**
   * First-class conversion from character streams to lazy strings.
   */
  public static final F, LazyString> fromStream =
          LazyString::fromStream;

  private static final Equal> eqS = streamEqual(charEqual);

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy