/* __ __ __ __ __ ___ * \ \ / / \ \ / / __/ * \ \/ / /\ \ \/ / / * \____/__/ \__\____/__/.ɪᴏ * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ */ package io.vavr.collection; import io.vavr.*; import io.vavr.collection.CharSeqModule.Combinations; import io.vavr.control.Option; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.*; import java.util.function.*; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import java.util.stream.Collector; import static io.vavr.collection.JavaConverters.ChangePolicy.IMMUTABLE; import static io.vavr.collection.JavaConverters.ChangePolicy.MUTABLE; /** * The CharSeq (read: character sequence) collection essentially is a rich String wrapper having all operations * we know from the functional Vavr collections. * <p> * <strong>Note:</strong>Because CharSeq represents a sequence of primitive characters (i.e. a String), * it breaks the Liskov Substitution Principle in the way, that the CharSeq cannot contain {@code null} elements. * In future version of Java, CharSeq should extend IndexedSeq<char> instead. * * @author Ruslan Sennov, Daniel Dietrich */ public final class CharSeq implements CharSequence, IndexedSeq<Character>, Serializable { private static final long serialVersionUID = 1L; private static final CharSeq EMPTY = new CharSeq(""); private final String back; private CharSeq(String javaString) { this.back = javaString; } public static CharSeq empty() { return EMPTY; } /** * Returns a {@link java.util.stream.Collector} which may be used in conjunction with * {@link java.util.stream.Stream#collect(java.util.stream.Collector)} to obtain a {@link CharSeq}. * * @return A {@code CharSeq} Collector. */ public static Collector<Character, ArrayList<Character>, CharSeq> collector() { final Supplier<ArrayList<Character>> supplier = ArrayList::new; final BiConsumer<ArrayList<Character>, Character> accumulator = ArrayList::add; final BinaryOperator<ArrayList<Character>> combiner = (left, right) -> { left.addAll(right); return left; }; final Function<ArrayList<Character>, CharSeq> finisher = CharSeq::ofAll; return Collector.of(supplier, accumulator, combiner, finisher); } /** * Creates a String of {@code CharSequence}. * * @param sequence {@code CharSequence} instance. * @return A new {@link io.vavr.collection.CharSeq} */ // DEV-NOTE: Needs to be 'of' instead of 'ofAll' because 'ofAll(CharSeq)' is ambiguous. public static CharSeq of(CharSequence sequence) { Objects.requireNonNull(sequence, "sequence is null"); if (sequence instanceof CharSeq) { return (CharSeq) sequence; } else { return sequence.length() == 0 ? empty() : new CharSeq(sequence.toString()); } } /** * Returns a singleton {@code CharSeq}, i.e. a {@code CharSeq} of one character. * * @param character A character. * @return A new {@code CharSeq} instance containing the given element */ public static CharSeq of(char character) { return new CharSeq(new String(new char[] { character })); } /** * Creates a String of the given characters. * * @param characters Zero or more characters. * @return A string containing the given characters in the same order. * @throws NullPointerException if {@code elements} is null */ public static CharSeq of(char... characters) { Objects.requireNonNull(characters, "characters is null"); if (characters.length == 0) { return empty(); } else { final char[] chrs = new char[characters.length]; System.arraycopy(characters, 0, chrs, 0, characters.length); return new CharSeq(new String(chrs)); } } /** * Creates a String of the given elements. * <p> * The resulting string has the same iteration order as the given iterable of elements * if the iteration order of the elements is stable. * * @param elements An Iterable of elements. * @return A string containing the given elements in the same order. * @throws NullPointerException if {@code elements} is null or {@code elements} contains null */ public static CharSeq ofAll(Iterable<? extends Character> elements) { Objects.requireNonNull(elements, "elements is null"); if (elements instanceof CharSeq) { return (CharSeq) elements; } final StringBuilder sb = new StringBuilder(); for (char character : elements) { sb.append(character); } return sb.length() == 0 ? EMPTY : of(sb); } /** * Returns a CharSeq containing {@code n} values of a given Function {@code f} * over a range of integer values from 0 to {@code n - 1}. * * @param n The number of elements in the CharSeq * @param f The Function computing element values * @return A CharSeq consisting of elements {@code f(0),f(1), ..., f(n - 1)} * @throws NullPointerException if {@code f} is null */ public static CharSeq tabulate(int n, Function<? super Integer, ? extends Character> f) { Objects.requireNonNull(f, "f is null"); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { sb.append(f.apply(i).charValue()); } return of(sb); } /** * Returns a CharSeq containing {@code n} values supplied by a given Supplier {@code s}. * * @param n The number of elements in the CharSeq * @param s The Supplier computing element values * @return A CharSeq of size {@code n}, where each element contains the result supplied by {@code s}. * @throws NullPointerException if {@code s} is null */ public static CharSeq fill(int n, Supplier<? extends Character> s) { return tabulate(n, anything -> s.get()); } /** * Creates a CharSeq starting from character {@code from}, extending to character {@code toExclusive - 1}. * <p> * Examples: * <pre> * <code> * CharSeq.range('a', 'c') // = "ab" * CharSeq.range('c', 'a') // = "" * </code> * </pre> * * @param from the first character * @param toExclusive the successor of the last character * @return a range of characters as specified or the empty range if {@code from >= toExclusive} */ public static CharSeq range(char from, char toExclusive) { return new CharSeq(io.vavr.collection.Iterator.range(from, toExclusive).mkString()); } public static CharSeq rangeBy(char from, char toExclusive, int step) { return new CharSeq(io.vavr.collection.Iterator.rangeBy(from, toExclusive, step).mkString()); } /** * Creates a CharSeq starting from character {@code from}, extending to character {@code toInclusive}. * <p> * Examples: * <pre> * <code> * CharSeq.rangeClosed('a', 'c') // = "abc" * CharSeq.rangeClosed('c', 'a') // = "" * </code> * </pre> * * @param from the first character * @param toInclusive the last character * @return a range of characters as specified or the empty range if {@code from > toInclusive} */ public static CharSeq rangeClosed(char from, char toInclusive) { return new CharSeq(io.vavr.collection.Iterator.rangeClosed(from, toInclusive).mkString()); } /** * Creates a CharSeq starting from character {@code from}, extending to character {@code toInclusive}, * with {@code step}. * <p> * Examples: * <pre> * <code> * CharSeq.rangeClosedBy('a', 'c', 1) // = ('a', 'b', 'c') * CharSeq.rangeClosedBy('a', 'd', 2) // = ('a', 'c') * CharSeq.rangeClosedBy('d', 'a', -2) // = ('d', 'b') * CharSeq.rangeClosedBy('d', 'a', 2) // = () * </code> * </pre> * * @param from the first character * @param toInclusive the last character * @param step the step * @return a range of characters as specified or the empty range if {@code step * (from - toInclusive) > 0}. * @throws IllegalArgumentException if {@code step} is zero */ public static CharSeq rangeClosedBy(char from, char toInclusive, int step) { return new CharSeq(io.vavr.collection.Iterator.rangeClosedBy(from, toInclusive, step).mkString()); } /** * Creates a CharSeq from a seed value and a function. * The function takes the seed at first. * The function should return {@code None} when it's * done generating the CharSeq, otherwise {@code Some} {@code Tuple} * of the element for the next call and the value to add to the * resulting CharSeq. * <p> * Example: * <pre> * <code> * CharSeq.unfoldRight('j', x -> x == 'a' * ? Option.none() * : Option.of(new Tuple2<>(new Character(x), (char)(x-1)))); * // CharSeq.of("jihgfedcb")) * </code> * </pre> * * @param <T> type of seeds * @param seed the start value for the iteration * @param f the function to get the next step of the iteration * @return a CharSeq with the values built up by the iteration * @throws NullPointerException if {@code f} is null */ public static <T> CharSeq unfoldRight(T seed, Function<? super T, Option<Tuple2<? extends Character, ? extends T>>> f) { return CharSeq.ofAll(io.vavr.collection.Iterator.unfoldRight(seed, f)); } /** * Creates a CharSeq from a seed value and a function. * The function takes the seed at first. * The function should return {@code None} when it's * done generating the CharSeq, otherwise {@code Some} {@code Tuple} * of the value to add to the resulting CharSeq and * the element for the next call. * <p> * Example: * <pre> * <code> * CharSeq.unfoldLeft('j', x -> x == 'a' * ? Option.none() * : Option.of(new Tuple2<>((char)(x-1), new Character(x)))); * // CharSeq.of("bcdefghij")) * </code> * </pre> * * @param <T> type of seeds * @param seed the start value for the iteration * @param f the function to get the next step of the iteration * @return a CharSeq with the values built up by the iteration * @throws NullPointerException if {@code f} is null */ public static <T> CharSeq unfoldLeft(T seed, Function<? super T, Option<Tuple2<? extends T, ? extends Character>>> f) { return CharSeq.ofAll(io.vavr.collection.Iterator.unfoldLeft(seed, f)); } /** * Creates a CharSeq from a seed value and a function. * The function takes the seed at first. * The function should return {@code None} when it's * done generating the CharSeq, otherwise {@code Some} {@code Tuple} * of the value to add to the resulting CharSeq and * the element for the next call. * <p> * Example: * <pre> * <code> * CharSeq.unfold('j', x -> x == 'a' * ? Option.none() * : Option.of(new Tuple2<>((char)(x-1), new Character(x)))); * // CharSeq.of("bcdefghij")) * </code> * </pre> * * @param seed the start value for the iteration * @param f the function to get the next step of the iteration * @return a CharSeq with the values built up by the iteration * @throws NullPointerException if {@code f} is null */ public static CharSeq unfold(Character seed, Function<? super Character, Option<Tuple2<? extends Character, ? extends Character>>> f) { return CharSeq.ofAll(io.vavr.collection.Iterator.unfold(seed, f)); } private Tuple2<CharSeq, CharSeq> splitByBuilder(StringBuilder sb) { if (sb.length() == 0) { return Tuple.of(EMPTY, this); } else if (sb.length() == length()) { return Tuple.of(this, EMPTY); } else { return Tuple.of(of(sb), of(back.substring(sb.length()))); } } /** * Repeats a character {@code times} times. * * @param character A character * @param times Repetition count * @return A CharSeq representing {@code character * times} */ public static CharSeq repeat(char character, int times) { final int length = Math.max(times, 0); final char[] characters = new char[length]; java.util.Arrays.fill(characters, character); return new CharSeq(String.valueOf(characters)); } /** * Repeats this CharSeq {@code times} times. * <p> * Example: {@code CharSeq.of("ja").repeat(13) = "jajajajajajajajajajajajaja"} * * @param times Repetition count * @return A CharSeq representing {@code this * times} */ public CharSeq repeat(int times) { if (times <= 0 || isEmpty()) { return empty(); } else if (times == 1) { return this; } else { final int finalLength = length() * times; final char[] result = new char[finalLength]; back.getChars(0, length(), result, 0); int i = length(); for (; i <= (finalLength >>> 1); i <<= 1) { System.arraycopy(result, 0, result, i, i); } System.arraycopy(result, 0, result, i, finalLength - i); return of(new String(result)); } } // // // IndexedSeq // // @Override public CharSeq append(Character element) { // DEV-NOTE: we need to unbox, otherwise "null" will be appended to back final char c = element; return of(back + c); } @Override public CharSeq appendAll(Iterable<? extends Character> elements) { Objects.requireNonNull(elements, "elements is null"); if (Collections.isEmpty(elements)) { return this; } final StringBuilder sb = new StringBuilder(back); for (char element : elements) { sb.append(element); } return of(sb); } @GwtIncompatible @Override public java.util.List<Character> asJava() { return JavaConverters.asJava(this, IMMUTABLE); } @GwtIncompatible @Override public CharSeq asJava(Consumer<? super java.util.List<Character>> action) { return Collections.asJava(this, action, IMMUTABLE); } @GwtIncompatible @Override public java.util.List<Character> asJavaMutable() { return JavaConverters.asJava(this, MUTABLE); } @GwtIncompatible @Override public CharSeq asJavaMutable(Consumer<? super java.util.List<Character>> action) { return Collections.asJava(this, action, MUTABLE); } @Override public <R> IndexedSeq<R> collect(PartialFunction<? super Character, ? extends R> partialFunction) { return Vector.ofAll(iterator().<R> collect(partialFunction)); } @Override public IndexedSeq<CharSeq> combinations() { return Vector.rangeClosed(0, length()).map(this::combinations).flatMap(Function.identity()); } @Override public IndexedSeq<CharSeq> combinations(int k) { return Combinations.apply(this, Math.max(k, 0)); } @Override public io.vavr.collection.Iterator<CharSeq> crossProduct(int power) { return io.vavr.collection.Collections.crossProduct(CharSeq.empty(), this, power); } @Override public CharSeq distinct() { return distinctBy(Function.identity()); } @Override public CharSeq distinctBy(Comparator<? super Character> comparator) { Objects.requireNonNull(comparator, "comparator is null"); final java.util.Set<Character> seen = new java.util.TreeSet<>(comparator); return filter(seen::add); } @Override public <U> CharSeq distinctBy(Function<? super Character, ? extends U> keyExtractor) { Objects.requireNonNull(keyExtractor, "keyExtractor is null"); final java.util.Set<U> seen = new java.util.HashSet<>(); return filter(t -> seen.add(keyExtractor.apply(t))); } @Override public CharSeq drop(int n) { if (n <= 0) { return this; } else if (n >= length()) { return EMPTY; } else { return of(back.substring(n)); } } @Override public CharSeq dropUntil(Predicate<? super Character> predicate) { return io.vavr.collection.Collections.dropUntil(this, predicate); } @Override public CharSeq dropWhile(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); return dropUntil(predicate.negate()); } @Override public CharSeq dropRight(int n) { if (n <= 0) { return this; } else if (n >= length()) { return EMPTY; } else { return of(back.substring(0, length() - n)); } } @Override public CharSeq dropRightWhile(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); return dropRightUntil(predicate.negate()); } @Override public CharSeq dropRightUntil(Predicate<? super Character> predicate) { return io.vavr.collection.Collections.dropRightUntil(this, predicate); } @Override public CharSeq filter(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < back.length(); i++) { final char ch = get(i); if (predicate.test(ch)) { sb.append(ch); } } if (sb.length() == 0) { return EMPTY; } else if (sb.length() == length()) { return this; } else { return of(sb); } } @Override public <U> IndexedSeq<U> flatMap(Function<? super Character, ? extends Iterable<? extends U>> mapper) { Objects.requireNonNull(mapper, "mapper is null"); if (isEmpty()) { return Vector.empty(); } else { IndexedSeq<U> result = Vector.empty(); for (int i = 0; i < length(); i++) { for (U u : mapper.apply(get(i))) { result = result.append(u); } } return result; } } public CharSeq flatMapChars(CharFunction<? extends CharSequence> mapper) { Objects.requireNonNull(mapper, "mapper is null"); if (isEmpty()) { return this; } else { final StringBuilder builder = new StringBuilder(); for (int i = 0; i < back.length(); i++) { builder.append(mapper.apply(back.charAt(i))); } return of(builder); } } @Override public <C> Map<C, CharSeq> groupBy(Function<? super Character, ? extends C> classifier) { return io.vavr.collection.Collections.groupBy(this, classifier, CharSeq::ofAll); } @Override public io.vavr.collection.Iterator<CharSeq> grouped(int size) { return sliding(size, size); } @Override public boolean hasDefiniteSize() { return true; } @Override public CharSeq init() { if (isEmpty()) { throw new UnsupportedOperationException("init of empty string"); } else { return of(back.substring(0, length() - 1)); } } @Override public Option<CharSeq> initOption() { return isEmpty() ? Option.none() : Option.some(init()); } @Override public CharSeq insert(int index, Character element) { if (index < 0) { throw new IndexOutOfBoundsException("insert(" + index + ", e)"); } if (index > length()) { throw new IndexOutOfBoundsException("insert(" + index + ", e) on String of length " + length()); } final char c = element; return of(new StringBuilder(back).insert(index, c).toString()); } @Override public CharSeq insertAll(int index, Iterable<? extends Character> elements) { Objects.requireNonNull(elements, "elements is null"); if (index < 0) { throw new IndexOutOfBoundsException("insertAll(" + index + ", elements)"); } if (index > length()) { throw new IndexOutOfBoundsException("insertAll(" + index + ", elements) on String of length " + length()); } final StringBuilder sb = new StringBuilder(back.substring(0, index)); for (char element : elements) { sb.append(element); } sb.append(back.substring(index)); return of(sb); } @Override public io.vavr.collection.Iterator<Character> iterator() { return io.vavr.collection.Iterator.ofAll(toCharArray()); } @Override public CharSeq intersperse(Character element) { final char c = element; // intentionally throw when element is null if (isEmpty()) { return EMPTY; } else { final StringBuilder sb = new StringBuilder().append(head()); for (int i = 1; i < length(); i++) { sb.append(c).append(get(i)); } return of(sb); } } @Override public <U> IndexedSeq<U> map(Function<? super Character, ? extends U> mapper) { Objects.requireNonNull(mapper, "mapper is null"); IndexedSeq<U> result = Vector.empty(); for (int i = 0; i < length(); i++) { result = result.append(mapper.apply(get(i))); } return result; } @Override public String mkString() { return back; } @Override public CharSeq padTo(int length, Character element) { final int actualLength = back.length(); if (length <= actualLength) { return this; } else { return new CharSeq(back + padding(element, length - actualLength)); } } @Override public CharSeq leftPadTo(int length, Character element) { final int actualLength = back.length(); if (length <= actualLength) { return this; } else { return of(padding(element, length - actualLength).append(back)); } } @Override public CharSeq orElse(Iterable<? extends Character> other) { return isEmpty() ? ofAll(other) : this; } @Override public CharSeq orElse(Supplier<? extends Iterable<? extends Character>> supplier) { return isEmpty() ? ofAll(supplier.get()) : this; } private static StringBuilder padding(char element, int limit) { final StringBuilder padding = new StringBuilder(); for (int i = 0; i < limit; i++) { padding.append(element); } return padding; } @Override public CharSeq patch(int from, Iterable<? extends Character> that, int replaced) { from = from < 0 ? 0 : from > length() ? length() : from; replaced = replaced < 0 ? 0 : replaced; final StringBuilder sb = new StringBuilder(back.substring(0, from)); for (char character : that) { sb.append(character); } from += replaced; if (from < length()) { sb.append(back.substring(from)); } return sb.length() == 0 ? EMPTY : of(sb); } public CharSeq mapChars(CharUnaryOperator mapper) { Objects.requireNonNull(mapper, "mapper is null"); if (isEmpty()) { return this; } else { final char[] chars = back.toCharArray(); for (int i = 0; i < chars.length; i++) { chars[i] = mapper.apply(chars[i]); } return CharSeq.of(chars); } } @Override public Tuple2<CharSeq, CharSeq> partition(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); if (isEmpty()) { return Tuple.of(EMPTY, EMPTY); } final StringBuilder left = new StringBuilder(); final StringBuilder right = new StringBuilder(); for (int i = 0; i < length(); i++) { final Character t = get(i); (predicate.test(t) ? left : right).append(t); } if (left.length() == 0) { return Tuple.of(EMPTY, of(right.toString())); } else if (right.length() == 0) { return Tuple.of(of(left.toString()), EMPTY); } else { return Tuple.of(of(left.toString()), of(right.toString())); } } @Override public CharSeq peek(Consumer<? super Character> action) { Objects.requireNonNull(action, "action is null"); if (!isEmpty()) { action.accept(get(0)); } return this; } @Override public IndexedSeq<CharSeq> permutations() { if (isEmpty()) { return Vector.empty(); } else { if (length() == 1) { return Vector.of(this); } else { IndexedSeq<CharSeq> result = Vector.empty(); for (Character t : distinct()) { for (CharSeq ts : remove(t).permutations()) { result = result.append(CharSeq.of(t).appendAll(ts)); } } return result; } } } @Override public CharSeq prepend(Character element) { final char c = element; return of(c + back); } @Override public CharSeq prependAll(Iterable<? extends Character> elements) { Objects.requireNonNull(elements, "elements is null"); final StringBuilder sb = new StringBuilder(); for (char element : elements) { sb.append(element); } sb.append(back); return sb.length() == 0 ? EMPTY : of(sb); } @Override public CharSeq remove(Character element) { if (element == null) { return this; } final StringBuilder sb = new StringBuilder(); boolean found = false; for (int i = 0; i < length(); i++) { final char c = get(i); if (!found && c == element) { found = true; } else { sb.append(c); } } return sb.length() == 0 ? EMPTY : sb.length() == length() ? this : of(sb); } @Override public CharSeq removeFirst(Predicate<Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); final StringBuilder sb = new StringBuilder(); boolean found = false; for (int i = 0; i < back.length(); i++) { final char ch = get(i); if (predicate.test(ch)) { if (found) { sb.append(ch); } found = true; } else { sb.append(ch); } } return found ? (sb.length() == 0 ? EMPTY : of(sb.toString())) : this; } @Override public CharSeq removeLast(Predicate<Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); for (int i = length() - 1; i >= 0; i--) { if (predicate.test(get(i))) { return removeAt(i); } } return this; } @Override public CharSeq removeAt(int index) { final String removed = back.substring(0, index) + back.substring(index + 1); return removed.isEmpty() ? EMPTY : of(removed); } @Override public CharSeq removeAll(Character element) { if (element == null) { return this; } return io.vavr.collection.Collections.removeAll(this, element); } @Override public CharSeq removeAll(Iterable<? extends Character> elements) { return io.vavr.collection.Collections.removeAll(this, elements); } @Override public CharSeq removeAll(Predicate<? super Character> predicate) { return io.vavr.collection.Collections.removeAll(this, predicate); } @Override public CharSeq replace(Character currentElement, Character newElement) { if (currentElement == null) { return this; } final char currentChar = currentElement; final char newChar = newElement; final StringBuilder sb = new StringBuilder(); boolean found = false; for (int i = 0; i < length(); i++) { final char c = get(i); if (!found && c == currentChar) { sb.append(newChar); found = true; } else { sb.append(c); } } return found ? of(sb) : this; } @Override public CharSeq replaceAll(Character currentElement, Character newElement) { if (currentElement == null) { return this; } final char currentChar = currentElement; final char newChar = newElement; final StringBuilder sb = new StringBuilder(); boolean found = false; for (int i = 0; i < length(); i++) { final char c = get(i); if (c == currentChar) { sb.append(newChar); found = true; } else { sb.append(c); } } return found ? of(sb) : this; } @Override public CharSeq retainAll(Iterable<? extends Character> elements) { return io.vavr.collection.Collections.retainAll(this, elements); } @Override public CharSeq reverse() { return of(new StringBuilder(back).reverse().toString()); } @Override public CharSeq scan(Character zero, BiFunction<? super Character, ? super Character, ? extends Character> operation) { return io.vavr.collection.Collections.scanLeft(this, zero, operation, io.vavr.collection.Iterator::toCharSeq); } @Override public <U> IndexedSeq<U> scanLeft(U zero, BiFunction<? super U, ? super Character, ? extends U> operation) { return io.vavr.collection.Collections.scanLeft(this, zero, operation, io.vavr.collection.Iterator::toVector); } @Override public <U> IndexedSeq<U> scanRight(U zero, BiFunction<? super Character, ? super U, ? extends U> operation) { return io.vavr.collection.Collections.scanRight(this, zero, operation, io.vavr.collection.Iterator::toVector); } @Override public CharSeq shuffle() { return io.vavr.collection.Collections.shuffle(this, CharSeq::ofAll); } @Override public CharSeq slice(int beginIndex, int endIndex) { final int from = beginIndex < 0 ? 0 : beginIndex; final int to = endIndex > length() ? length() : endIndex; if (from >= to) { return EMPTY; } if (from <= 0 && to >= length()) { return this; } return CharSeq.of(back.substring(from, to)); } @Override public io.vavr.collection.Iterator<CharSeq> slideBy(Function<? super Character, ?> classifier) { return iterator().slideBy(classifier).map(CharSeq::ofAll); } @Override public io.vavr.collection.Iterator<CharSeq> sliding(int size) { return sliding(size, 1); } @Override public io.vavr.collection.Iterator<CharSeq> sliding(int size, int step) { return iterator().sliding(size, step).map(CharSeq::ofAll); } @Override public CharSeq sorted() { return isEmpty() ? this : toJavaStream().sorted().collect(CharSeq.collector()); } @Override public CharSeq sorted(Comparator<? super Character> comparator) { Objects.requireNonNull(comparator, "comparator is null"); return isEmpty() ? this : toJavaStream().sorted(comparator).collect(CharSeq.collector()); } @Override public <U extends Comparable<? super U>> CharSeq sortBy(Function<? super Character, ? extends U> mapper) { return sortBy(U::compareTo, mapper); } @Override public <U> CharSeq sortBy(Comparator<? super U> comparator, Function<? super Character, ? extends U> mapper) { final Function<? super Character, ? extends U> domain = Function1.of(mapper::apply).memoized(); return toJavaStream().sorted((e1, e2) -> comparator.compare(domain.apply(e1), domain.apply(e2))) .collect(collector()); } @Override public Tuple2<CharSeq, CharSeq> span(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < length(); i++) { final char c = get(i); if (predicate.test(c)) { sb.append(c); } else { break; } } return splitByBuilder(sb); } @Override public CharSeq subSequence(int beginIndex) { if (beginIndex < 0 || beginIndex > length()) { throw new IndexOutOfBoundsException("begin index " + beginIndex + " < 0"); } if (beginIndex == 0) { return this; } else if (beginIndex == length()) { return EMPTY; } else { return CharSeq.of(back.substring(beginIndex)); } } @Override public CharSeq tail() { if (isEmpty()) { throw new UnsupportedOperationException("tail of empty string"); } else { return CharSeq.of(back.substring(1)); } } @Override public Option<CharSeq> tailOption() { return isEmpty() ? Option.none() : Option.some(tail()); } @Override public CharSeq take(int n) { if (n <= 0) { return EMPTY; } else if (n >= length()) { return this; } else { return CharSeq.of(back.substring(0, n)); } } @Override public CharSeq takeRight(int n) { if (n <= 0) { return EMPTY; } else if (n >= length()) { return this; } else { return CharSeq.of(back.substring(length() - n)); } } @Override public CharSeq takeUntil(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); return takeWhile(predicate.negate()); } @Override public CharSeq takeWhile(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < length(); i++) { final char c = get(i); if (!predicate.test(c)) { break; } sb.append(c); } return sb.length() == length() ? this : sb.length() == 0 ? EMPTY : of(sb); } /** * Transforms this {@code CharSeq}. * * @param f A transformation * @param <U> Type of transformation result * @return An instance of type {@code U} * @throws NullPointerException if {@code f} is null */ public <U> U transform(Function<? super CharSeq, ? extends U> f) { Objects.requireNonNull(f, "f is null"); return f.apply(this); } @Override public <T1, T2> Tuple2<IndexedSeq<T1>, IndexedSeq<T2>> unzip(Function<? super Character, Tuple2<? extends T1, ? extends T2>> unzipper) { Objects.requireNonNull(unzipper, "unzipper is null"); IndexedSeq<T1> xs = Vector.empty(); IndexedSeq<T2> ys = Vector.empty(); for (int i = 0; i < length(); i++) { final Tuple2<? extends T1, ? extends T2> t = unzipper.apply(get(i)); xs = xs.append(t._1); ys = ys.append(t._2); } return Tuple.of(xs, ys); } @Override public <T1, T2, T3> Tuple3<IndexedSeq<T1>, IndexedSeq<T2>, IndexedSeq<T3>> unzip3(Function<? super Character, Tuple3<? extends T1, ? extends T2, ? extends T3>> unzipper) { Objects.requireNonNull(unzipper, "unzipper is null"); IndexedSeq<T1> xs = Vector.empty(); IndexedSeq<T2> ys = Vector.empty(); IndexedSeq<T3> zs = Vector.empty(); for (int i = 0; i < length(); i++) { final Tuple3<? extends T1, ? extends T2, ? extends T3> t = unzipper.apply(get(i)); xs = xs.append(t._1); ys = ys.append(t._2); zs = zs.append(t._3); } return Tuple.of(xs, ys, zs); } @Override public CharSeq update(int index, Character element) { if ((index < 0) || (index >= length())) { throw new IndexOutOfBoundsException("update(" + index + ")"); } else { char c = element; return of(back.substring(0, index) + c + back.substring(index + 1)); } } @Override public CharSeq update(int index, Function<? super Character, ? extends Character> updater) { Objects.requireNonNull(updater, "updater is null"); final char c = updater.apply(get(index)); return update(index, c); } @Override public <U> IndexedSeq<Tuple2<Character, U>> zip(Iterable<? extends U> that) { return zipWith(that, Tuple::of); } @Override public <U, R> IndexedSeq<R> zipWith(Iterable<? extends U> that, BiFunction<? super Character, ? super U, ? extends R> mapper) { Objects.requireNonNull(that, "that is null"); Objects.requireNonNull(mapper, "mapper is null"); IndexedSeq<R> result = Vector.empty(); final io.vavr.collection.Iterator<Character> list1 = iterator(); final java.util.Iterator<? extends U> list2 = that.iterator(); while (list1.hasNext() && list2.hasNext()) { result = result.append(mapper.apply(list1.next(), list2.next())); } return result; } @Override public <U> IndexedSeq<Tuple2<Character, U>> zipAll(Iterable<? extends U> that, Character thisElem, U thatElem) { Objects.requireNonNull(that, "that is null"); IndexedSeq<Tuple2<Character, U>> result = Vector.empty(); final io.vavr.collection.Iterator<Character> list1 = iterator(); final java.util.Iterator<? extends U> list2 = that.iterator(); while (list1.hasNext() || list2.hasNext()) { final Character elem1 = list1.hasNext() ? list1.next() : thisElem; final U elem2 = list2.hasNext() ? list2.next() : thatElem; result = result.append(Tuple.of(elem1, elem2)); } return result; } @Override public IndexedSeq<Tuple2<Character, Integer>> zipWithIndex() { return zipWithIndex(Tuple::of); } @Override public <U> IndexedSeq<U> zipWithIndex(BiFunction<? super Character, ? super Integer, ? extends U> mapper) { Objects.requireNonNull(mapper, "mapper is null"); IndexedSeq<U> result = Vector.empty(); for (int i = 0; i < length(); i++) { result = result.append(mapper.apply(get(i), i)); } return result; } @Override public Character get(int index) { return back.charAt(index); } @Override public int indexOf(Character element, int from) { return back.indexOf(element, from); } @Override public int lastIndexOf(Character element, int end) { return back.lastIndexOf(element, end); } @Override public Tuple2<CharSeq, CharSeq> splitAt(int n) { if (n <= 0) { return Tuple.of(EMPTY, this); } else if (n >= length()) { return Tuple.of(this, EMPTY); } else { return Tuple.of(of(back.substring(0, n)), of(back.substring(n))); } } @Override public Tuple2<CharSeq, CharSeq> splitAt(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); if (isEmpty()) { return Tuple.of(EMPTY, EMPTY); } final StringBuilder left = new StringBuilder(); for (int i = 0; i < length(); i++) { final Character t = get(i); if (!predicate.test(t)) { left.append(t); } else { break; } } return splitByBuilder(left); } @Override public Tuple2<CharSeq, CharSeq> splitAtInclusive(Predicate<? super Character> predicate) { Objects.requireNonNull(predicate, "predicate is null"); if (isEmpty()) { return Tuple.of(EMPTY, EMPTY); } final StringBuilder left = new StringBuilder(); for (int i = 0; i < length(); i++) { final Character t = get(i); left.append(t); if (predicate.test(t)) { break; } } return splitByBuilder(left); } @Override public boolean startsWith(Iterable<? extends Character> that, int offset) { return startsWith(CharSeq.ofAll(that), offset); } @Override public Character head() { if (isEmpty()) { throw new NoSuchElementException("head of empty string"); } else { return get(0); } } /** * A {@code CharSeq} is computed synchronously. * * @return false */ @Override public boolean isAsync() { return false; } @Override public boolean isEmpty() { return back.isEmpty(); } /** * A {@code CharSeq} is computed eagerly. * * @return false */ @Override public boolean isLazy() { return false; } @Override public boolean isTraversableAgain() { return true; } private Object readResolve() { return isEmpty() ? EMPTY : this; } @Override public boolean equals(Object o) { return io.vavr.collection.Collections.equals(this, o); } @Override public int hashCode() { return io.vavr.collection.Collections.hashOrdered(this); } // // // CharSequence // // /** * Returns the {@code char} value at the * specified index. An index ranges from {@code 0} to * {@code length() - 1}. The first {@code char} value of the sequence * is at index {@code 0}, the next at index {@code 1}, * and so on, as for array indexing. * <p> * If the {@code char} value specified by the index is a * <a href="Character.html#unicode">surrogate</a>, the surrogate * value is returned. * * @param index the index of the {@code char} value. * @return the {@code char} value at the specified index of this string. * The first {@code char} value is at index {@code 0}. * @throws IndexOutOfBoundsException if the {@code index} * argument is negative or not less than the length of this * string. */ @Override public char charAt(int index) { return get(index); } /** * Returns the length of this string. * The length is equal to the number of <a href="Character.html#unicode">Unicode * code units</a> in the string. * * @return the length of the sequence of characters represented by this * object. */ @Override public int length() { return back.length(); } // // // String // // /** * Returns the character (Unicode code point) at the specified * index. The index refers to {@code char} values * (Unicode code units) and ranges from {@code 0} to * {@link #length()}{@code - 1}. * <p> * If the {@code char} value specified at the given index * is in the high-surrogate range, the following index is less * than the length of this {@code CharSeq}, and the * {@code char} value at the following index is in the * low-surrogate range, then the supplementary code point * corresponding to this surrogate pair is returned. Otherwise, * the {@code char} value at the given index is returned. * * @param index the index to the {@code char} values * @return the code point value of the character at the * {@code index} * @throws IndexOutOfBoundsException if the {@code index} * argument is negative or not less than the length of this * string. */ public int codePointAt(int index) { return back.codePointAt(index); } /** * Returns the character (Unicode code point) before the specified * index. The index refers to {@code char} values * (Unicode code units) and ranges from {@code 1} to {@link * CharSequence#length() length}. * <p> * If the {@code char} value at {@code (index - 1)} * is in the low-surrogate range, {@code (index - 2)} is not * negative, and the {@code char} value at {@code (index - * 2)} is in the high-surrogate range, then the * supplementary code point value of the surrogate pair is * returned. If the {@code char} value at {@code index - * 1} is an unpaired low-surrogate or a high-surrogate, the * surrogate value is returned. * * @param index the index following the code point that should be returned * @return the Unicode code point value before the given index. * @throws IndexOutOfBoundsException if the {@code index} * argument is less than 1 or greater than the length * of this string. */ public int codePointBefore(int index) { return back.codePointBefore(index); } /** * Returns the number of Unicode code points in the specified text * range of this {@code CharSeq}. The text range begins at the * specified {@code beginIndex} and extends to the * {@code char} at index {@code endIndex - 1}. Thus the * length (in {@code char}s) of the text range is * {@code endIndex-beginIndex}. Unpaired surrogates within * the text range count as one code point each. * * @param beginIndex the index to the first {@code char} of * the text range. * @param endIndex the index after the last {@code char} of * the text range. * @return the number of Unicode code points in the specified text * range * @throws IndexOutOfBoundsException if the * {@code beginIndex} is negative, or {@code endIndex} * is larger than the length of this {@code CharSeq}, or * {@code beginIndex} is larger than {@code endIndex}. */ public int codePointCount(int beginIndex, int endIndex) { return back.codePointCount(beginIndex, endIndex); } /** * Returns the index within this {@code CharSeq} that is * offset from the given {@code index} by * {@code codePointOffset} code points. Unpaired surrogates * within the text range given by {@code index} and * {@code codePointOffset} count as one code point each. * * @param index the index to be offset * @param codePointOffset the offset in code points * @return the index within this {@code CharSeq} * @throws IndexOutOfBoundsException if {@code index} * is negative or larger then the length of this * {@code CharSeq}, or if {@code codePointOffset} is positive * and the substring starting with {@code index} has fewer * than {@code codePointOffset} code points, * or if {@code codePointOffset} is negative and the substring * before {@code index} has fewer than the absolute value * of {@code codePointOffset} code points. */ public int offsetByCodePoints(int index, int codePointOffset) { return back.offsetByCodePoints(index, codePointOffset); } /** * Copies characters from this string into the destination character * array. * <p> * The first character to be copied is at index {@code srcBegin}; * the last character to be copied is at index {@code srcEnd-1} * (thus the total number of characters to be copied is * {@code srcEnd-srcBegin}). The characters are copied into the * subarray of {@code dst} starting at index {@code dstBegin} * and ending at index: * <blockquote><pre> * dstbegin + (srcEnd-srcBegin) - 1 * </pre></blockquote> * * @param srcBegin index of the first character in the string * to copy. * @param srcEnd index after the last character in the string * to copy. * @param dst the destination array. * @param dstBegin the start offset in the destination array. * @throws IndexOutOfBoundsException If any of the following * is true: * <ul><li>{@code srcBegin} is negative. * <li>{@code srcBegin} is greater than {@code srcEnd} * <li>{@code srcEnd} is greater than the length of this * string * <li>{@code dstBegin} is negative * <li>{@code dstBegin+(srcEnd-srcBegin)} is larger than * {@code dst.length}</ul> */ public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { back.getChars(srcBegin, srcEnd, dst, dstBegin); } /** * Encodes this {@code CharSeq} into a sequence of bytes using the named * charset, storing the result into a new byte array. * <p> * The behavior of this method when this string cannot be encoded in * the given charset is unspecified. The {@link * java.nio.charset.CharsetEncoder} class should be used when more control * over the encoding process is required. * * @param charsetName The name of a supported {@linkplain java.nio.charset.Charset * charset} * @return The resultant byte array * @throws UnsupportedEncodingException If the named charset is not supported */ public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { return back.getBytes(charsetName); } /** * Encodes this {@code CharSeq} into a sequence of bytes using the given * {@linkplain java.nio.charset.Charset charset}, storing the result into a * new byte array. * <p> * This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement byte array. The * {@link java.nio.charset.CharsetEncoder} class should be used when more * control over the encoding process is required. * * @param charset The {@linkplain java.nio.charset.Charset} to be used to encode * the {@code CharSeq} * @return The resultant byte array */ public byte[] getBytes(Charset charset) { return back.getBytes(charset); } /** * Encodes this {@code CharSeq} into a sequence of bytes using the * platform's default charset, storing the result into a new byte array. * <p> * The behavior of this method when this string cannot be encoded in * the default charset is unspecified. The {@link * java.nio.charset.CharsetEncoder} class should be used when more control * over the encoding process is required. * * @return The resultant byte array */ public byte[] getBytes() { return back.getBytes(); } /** * Compares this string to the specified {@code StringBuffer}. The result * is {@code true} if and only if this {@code CharSeq} represents the same * sequence of characters as the specified {@code StringBuffer}. This method * synchronizes on the {@code StringBuffer}. * * @param sb The {@code StringBuffer} to compare this {@code CharSeq} against * @return {@code true} if this {@code CharSeq} represents the same * sequence of characters as the specified {@code StringBuffer}, * {@code false} otherwise */ public boolean contentEquals(StringBuffer sb) { return back.contentEquals(sb); } /** * Compares this string to the specified {@code CharSequence}. The * result is {@code true} if and only if this {@code CharSeq} represents the * same sequence of char values as the specified sequence. Note that if the * {@code CharSequence} is a {@code StringBuffer} then the method * synchronizes on it. * * @param cs The sequence to compare this {@code CharSeq} against * @return {@code true} if this {@code CharSeq} represents the same * sequence of char values as the specified sequence, {@code * false} otherwise */ public boolean contentEquals(CharSequence cs) { return back.contentEquals(cs); } /** * Compares this {@code CharSeq} to another {@code CharSeq}, ignoring case * considerations. Two strings are considered equal ignoring case if they * are of the same length and corresponding characters in the two strings * are equal ignoring case. * <p> * Two characters {@code c1} and {@code c2} are considered the same * ignoring case if at least one of the following is true: * <ul> * <li> The two characters are the same (as compared by the * {@code ==} operator) * <li> Applying the method {@link * Character#toUpperCase(char)} to each character * produces the same result * <li> Applying the method {@link * Character#toLowerCase(char)} to each character * produces the same result * </ul> * * @param anotherString The {@code CharSeq} to compare this {@code CharSeq} against * @return {@code true} if the argument is not {@code null} and it * represents an equivalent {@code CharSeq} ignoring case; {@code * false} otherwise * @see #equals(Object) */ public boolean equalsIgnoreCase(CharSeq anotherString) { return back.equalsIgnoreCase(anotherString.back); } /** * Compares two strings lexicographically. * The comparison is based on the Unicode value of each character in * the strings. The character sequence represented by this * {@code CharSeq} object is compared lexicographically to the * character sequence represented by the argument string. The result is * a negative integer if this {@code CharSeq} object * lexicographically precedes the argument string. The result is a * positive integer if this {@code CharSeq} object lexicographically * follows the argument string. The result is zero if the strings * are equal; {@code compareTo} returns {@code 0} exactly when * the {@link #equals(Object)} method would return {@code true}. * <p> * This is the definition of lexicographic ordering. If two strings are * different, then either they have different characters at some index * that is a valid index for both strings, or their lengths are different, * or both. If they have different characters at one or more index * positions, let <i>k</i> be the smallest such index; then the string * whose character at position <i>k</i> has the smaller value, as * determined by using the < operator, lexicographically precedes the * other string. In this case, {@code compareTo} returns the * difference of the two character values at position {@code k} in * the two string -- that is, the value: * <blockquote><pre> * this.charAt(k)-anotherString.charAt(k) * </pre></blockquote> * If there is no index position at which they differ, then the shorter * string lexicographically precedes the longer string. In this case, * {@code compareTo} returns the difference of the lengths of the * strings -- that is, the value: * <blockquote><pre> * this.length()-anotherString.length() * </pre></blockquote> * * @param anotherString the {@code CharSeq} to be compared. * @return the value {@code 0} if the argument string is equal to * this string; a value less than {@code 0} if this string * is lexicographically less than the string argument; and a * value greater than {@code 0} if this string is * lexicographically greater than the string argument. */ public int compareTo(CharSeq anotherString) { return back.compareTo(anotherString.back); } /** * Compares two strings lexicographically, ignoring case * differences. This method returns an integer whose sign is that of * calling {@code compareTo} with normalized versions of the strings * where case differences have been eliminated by calling * {@code Character.toLowerCase(Character.toUpperCase(character))} on * each character. * <p> * Note that this method does <em>not</em> take locale into account, * and will result in an unsatisfactory ordering for certain locales. * The java.text package provides <em>collators</em> to allow * locale-sensitive ordering. * * @param str the {@code CharSeq} to be compared. * @return a negative integer, zero, or a positive integer as the * specified String is greater than, equal to, or less * than this String, ignoring case considerations. */ public int compareToIgnoreCase(CharSeq str) { return back.compareToIgnoreCase(str.back); } /** * Tests if two string regions are equal. * <p> * A substring of this {@code CharSeq} object is compared to a substring * of the argument other. The result is true if these substrings * represent identical character sequences. The substring of this * {@code CharSeq} object to be compared begins at index {@code toffset} * and has length {@code len}. The substring of other to be compared * begins at index {@code ooffset} and has length {@code len}. The * result is {@code false} if and only if at least one of the following * is true: * <ul><li>{@code toffset} is negative. * <li>{@code ooffset} is negative. * <li>{@code toffset+len} is greater than the length of this * {@code CharSeq} object. * <li>{@code ooffset+len} is greater than the length of the other * argument. * <li>There is some nonnegative integer <i>k</i> less than {@code len} * such that: * {@code this.charAt(toffset + }<i>k</i>{@code ) != other.charAt(ooffset + } * <i>k</i>{@code )} * </ul> * * @param toffset the starting offset of the subregion in this string. * @param other the string argument. * @param ooffset the starting offset of the subregion in the string * argument. * @param len the number of characters to compare. * @return {@code true} if the specified subregion of this string * exactly matches the specified subregion of the string argument; * {@code false} otherwise. */ public boolean regionMatches(int toffset, CharSeq other, int ooffset, int len) { return back.regionMatches(toffset, other.back, ooffset, len); } /** * Tests if two string regions are equal. * <p> * A substring of this {@code CharSeq} object is compared to a substring * of the argument {@code other}. The result is {@code true} if these * substrings represent character sequences that are the same, ignoring * case if and only if {@code ignoreCase} is true. The substring of * this {@code CharSeq} object to be compared begins at index * {@code toffset} and has length {@code len}. The substring of * {@code other} to be compared begins at index {@code ooffset} and * has length {@code len}. The result is {@code false} if and only if * at least one of the following is true: * <ul><li>{@code toffset} is negative. * <li>{@code ooffset} is negative. * <li>{@code toffset+len} is greater than the length of this * {@code CharSeq} object. * <li>{@code ooffset+len} is greater than the length of the other * argument. * <li>{@code ignoreCase} is {@code false} and there is some nonnegative * integer <i>k</i> less than {@code len} such that: * <blockquote><pre> * this.charAt(toffset+k) != other.charAt(ooffset+k) * </pre></blockquote> * <li>{@code ignoreCase} is {@code true} and there is some nonnegative * integer <i>k</i> less than {@code len} such that: * <blockquote><pre> * Character.toLowerCase(this.charAt(toffset+k)) != * Character.toLowerCase(other.charAt(ooffset+k)) * </pre></blockquote> * and: * <blockquote><pre> * Character.toUpperCase(this.charAt(toffset+k)) != * Character.toUpperCase(other.charAt(ooffset+k)) * </pre></blockquote> * </ul> * * @param ignoreCase if {@code true}, ignore case when comparing * characters. * @param toffset the starting offset of the subregion in this * string. * @param other the string argument. * @param ooffset the starting offset of the subregion in the string * argument. * @param len the number of characters to compare. * @return {@code true} if the specified subregion of this string * matches the specified subregion of the string argument; * {@code false} otherwise. Whether the matching is exact * or case insensitive depends on the {@code ignoreCase} * argument. */ public boolean regionMatches(boolean ignoreCase, int toffset, CharSeq other, int ooffset, int len) { return back.regionMatches(ignoreCase, toffset, other.back, ooffset, len); } @Override public CharSeq subSequence(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new IndexOutOfBoundsException("begin index " + beginIndex + " < 0"); } if (endIndex > length()) { throw new IndexOutOfBoundsException("endIndex " + endIndex + " > length " + length()); } final int subLen = endIndex - beginIndex; if (subLen < 0) { throw new IllegalArgumentException("beginIndex " + beginIndex + " > endIndex " + endIndex); } if (beginIndex == 0 && endIndex == length()) { return this; } else { return CharSeq.of(back.subSequence(beginIndex, endIndex)); } } /** * Tests if the substring of this string beginning at the * specified index starts with the specified prefix. * * @param prefix the prefix. * @param toffset where to begin looking in this string. * @return {@code true} if the character sequence represented by the * argument is a prefix of the substring of this object starting * at index {@code toffset}; {@code false} otherwise. * The result is {@code false} if {@code toffset} is * negative or greater than the length of this * {@code CharSeq} object; otherwise the result is the same * as the result of the expression * <pre> * this.substring(toffset).startsWith(prefix) * </pre> */ public boolean startsWith(CharSeq prefix, int toffset) { return back.startsWith(prefix.back, toffset); } /** * Tests if this string starts with the specified prefix. * * @param prefix the prefix. * @return {@code true} if the character sequence represented by the * argument is a prefix of the character sequence represented by * this string; {@code false} otherwise. * Note also that {@code true} will be returned if the * argument is an empty string or is equal to this * {@code CharSeq} object as determined by the * {@link #equals(Object)} method. */ public boolean startsWith(CharSeq prefix) { return back.startsWith(prefix.back); } /** * Tests if this string ends with the specified suffix. * * @param suffix the suffix. * @return {@code true} if the character sequence represented by the * argument is a suffix of the character sequence represented by * this object; {@code false} otherwise. Note that the * result will be {@code true} if the argument is the * empty string or is equal to this {@code CharSeq} object * as determined by the {@link #equals(Object)} method. */ public boolean endsWith(CharSeq suffix) { return back.endsWith(suffix.back); } /** * Returns the index within this string of the first occurrence of * the specified character. If a character with value * {@code ch} occurs in the character sequence represented by * this {@code CharSeq} object, then the index (in Unicode * code units) of the first such occurrence is returned. For * values of {@code ch} in the range from 0 to 0xFFFF * (inclusive), this is the smallest value <i>k</i> such that: * <blockquote><pre> * this.charAt(<i>k</i>) == ch * </pre></blockquote> * is true. For other values of {@code ch}, it is the * smallest value <i>k</i> such that: * <blockquote><pre> * this.codePointAt(<i>k</i>) == ch * </pre></blockquote> * is true. In either case, if no such character occurs in this * string, then {@code -1} is returned. * * @param ch a character (Unicode code point). * @return the index of the first occurrence of the character in the * character sequence represented by this object, or * {@code -1} if the character does not occur. */ public int indexOf(int ch) { return back.indexOf(ch); } /** * Returns the index of the first occurrence of the given element as an {@code Option}. * * @param ch a character (Unicode code point). * @return {@code Some(index)} or {@code None} if not found. */ Option<Integer> indexOfOption(int ch) { return io.vavr.collection.Collections.indexOption(indexOf(ch)); } /** * Returns the index within this string of the first occurrence of the * specified character, starting the search at the specified index. * <p> * If a character with value {@code ch} occurs in the * character sequence represented by this {@code CharSeq} * object at an index no smaller than {@code fromIndex}, then * the index of the first such occurrence is returned. For values * of {@code ch} in the range from 0 to 0xFFFF (inclusive), * this is the smallest value <i>k</i> such that: * <blockquote><pre> * (this.charAt(<i>k</i>) == ch) {@code &&} (<i>k</i> >= fromIndex) * </pre></blockquote> * is true. For other values of {@code ch}, it is the * smallest value <i>k</i> such that: * <blockquote><pre> * (this.codePointAt(<i>k</i>) == ch) {@code &&} (<i>k</i> >= fromIndex) * </pre></blockquote> * is true. In either case, if no such character occurs in this * string at or after position {@code fromIndex}, then * {@code -1} is returned. * <p> * There is no restriction on the value of {@code fromIndex}. If it * is negative, it has the same effect as if it were zero: this entire * string may be searched. If it is greater than the length of this * string, it has the same effect as if it were equal to the length of * this string: {@code -1} is returned. * <p> * All indices are specified in {@code char} values * (Unicode code units). * * @param ch a character (Unicode code point). * @param fromIndex the index to start the search from. * @return the index of the first occurrence of the character in the * character sequence represented by this object that is greater * than or equal to {@code fromIndex}, or {@code -1} * if the character does not occur. */ public int indexOf(int ch, int fromIndex) { return back.indexOf(ch, fromIndex); } /** * Returns the index of the first occurrence of the given element as an {@code Option}, * starting the search at the specified index. * * @param ch a character (Unicode code point). * @param fromIndex the index to start the search from. * @return {@code Some(index)} or {@code None} if not found. */ Option<Integer> indexOfOption(int ch, int fromIndex) { return io.vavr.collection.Collections.indexOption(indexOf(ch, fromIndex)); } /** * Returns the index within this string of the last occurrence of * the specified character. For values of {@code ch} in the * range from 0 to 0xFFFF (inclusive), the index (in Unicode code * units) returned is the largest value <i>k</i> such that: * <blockquote><pre> * this.charAt(<i>k</i>) == ch * </pre></blockquote> * is true. For other values of {@code ch}, it is the * largest value <i>k</i> such that: * <blockquote><pre> * this.codePointAt(<i>k</i>) == ch * </pre></blockquote> * is true. In either case, if no such character occurs in this * string, then {@code -1} is returned. The * {@code CharSeq} is searched backwards starting at the last * character. * * @param ch a character (Unicode code point). * @return the index of the last occurrence of the character in the * character sequence represented by this object, or * {@code -1} if the character does not occur. */ public int lastIndexOf(int ch) { return back.lastIndexOf(ch); } /** * Returns the index of the last occurrence of the given element as an {@code Option}. * * @param ch a character (Unicode code point). * @return {@code Some(index)} or {@code None} if not found. */ Option<Integer> lastIndexOfOption(int ch) { return io.vavr.collection.Collections.indexOption(lastIndexOf(ch)); } /** * Returns the index within this string of the last occurrence of * the specified character, searching backward starting at the * specified index. For values of {@code ch} in the range * from 0 to 0xFFFF (inclusive), the index returned is the largest * value <i>k</i> such that: * <blockquote><pre> * (this.charAt(<i>k</i>) == ch) {@code &&} (<i>k</i> <= fromIndex) * </pre></blockquote> * is true. For other values of {@code ch}, it is the * largest value <i>k</i> such that: * <blockquote><pre> * (this.codePointAt(<i>k</i>) == ch) {@code &&} (<i>k</i> <= fromIndex) * </pre></blockquote> * is true. In either case, if no such character occurs in this * string at or before position {@code fromIndex}, then * {@code -1} is returned. * <p> * All indices are specified in {@code char} values * (Unicode code units). * * @param ch a character (Unicode code point). * @param fromIndex the index to start the search from. There is no * restriction on the value of {@code fromIndex}. If it is * greater than or equal to the length of this string, it has * the same effect as if it were equal to one less than the * length of this string: this entire string may be searched. * If it is negative, it has the same effect as if it were -1: * -1 is returned. * @return the index of the last occurrence of the character in the * character sequence represented by this object that is less * than or equal to {@code fromIndex}, or {@code -1} * if the character does not occur before that point. */ public int lastIndexOf(int ch, int fromIndex) { return back.lastIndexOf(ch, fromIndex); } /** * Returns the index of the last occurrence of the given element as an {@code Option}, * starting the search at the specified index. * * @param ch a character (Unicode code point). * @param fromIndex the index to start the search from. * @return {@code Some(index)} or {@code None} if not found. */ public Option<Integer> lastIndexOfOption(int ch, int fromIndex) { return io.vavr.collection.Collections.indexOption(lastIndexOf(ch, fromIndex)); } /** * Returns the index within this string of the first occurrence of the * specified substring. * <p> * The returned index is the smallest value <i>k</i> for which: * <blockquote><pre> * this.startsWith(str, <i>k</i>) * </pre></blockquote> * If no such value of <i>k</i> exists, then {@code -1} is returned. * * @param str the substring to search for. * @return the index of the first occurrence of the specified substring, * or {@code -1} if there is no such occurrence. */ public int indexOf(CharSeq str) { return back.indexOf(str.back); } /** * Returns the index of the first occurrence of the given element as an {@code Option}. * * @param str the substring to search for. * @return {@code Some(index)} or {@code None} if not found. */ public Option<Integer> indexOfOption(CharSeq str) { return io.vavr.collection.Collections.indexOption(indexOf(str)); } /** * Returns the index within this string of the first occurrence of the * specified substring, starting at the specified index. * <p> * The returned index is the smallest value <i>k</i> for which: * <blockquote><pre> * <i>k</i> >= fromIndex {@code &&} this.startsWith(str, <i>k</i>) * </pre></blockquote> * If no such value of <i>k</i> exists, then {@code -1} is returned. * * @param str the substring to search for. * @param fromIndex the index from which to start the search. * @return the index of the first occurrence of the specified substring, * starting at the specified index, * or {@code -1} if there is no such occurrence. */ public int indexOf(CharSeq str, int fromIndex) { return back.indexOf(str.back, fromIndex); } /** * Returns the index of the first occurrence of the given element as an {@code Option}, * starting the search at the specified index. * * @param str the substring to search for. * @param fromIndex the index from which to start the search. * @return {@code Some(index)} or {@code None} if not found. */ public Option<Integer> indexOfOption(CharSeq str, int fromIndex) { return io.vavr.collection.Collections.indexOption(indexOf(str, fromIndex)); } /** * Returns the index within this string of the last occurrence of the * specified substring. The last occurrence of the empty string "" * is considered to occur at the index value {@code this.length()}. * <p> * The returned index is the largest value <i>k</i> for which: * <blockquote><pre> * this.startsWith(str, <i>k</i>) * </pre></blockquote> * If no such value of <i>k</i> exists, then {@code -1} is returned. * * @param str the substring to search for. * @return the index of the last occurrence of the specified substring, * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(CharSeq str) { return back.lastIndexOf(str.back); } /** * Returns the index of the last occurrence of the given element as an {@code Option}. * * @param str the substring to search for. * @return {@code Some(index)} or {@code None} if not found. */ public Option<Integer> lastIndexOfOption(CharSeq str) { return io.vavr.collection.Collections.indexOption(lastIndexOf(str)); } /** * Returns the index within this string of the last occurrence of the * specified substring, searching backward starting at the specified index. * <p> * The returned index is the largest value <i>k</i> for which: * <blockquote><pre> * <i>k</i> {@code <=} fromIndex {@code &&} this.startsWith(str, <i>k</i>) * </pre></blockquote> * If no such value of <i>k</i> exists, then {@code -1} is returned. * * @param str the substring to search for. * @param fromIndex the index to start the search from. * @return the index of the last occurrence of the specified substring, * searching backward from the specified index, * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(CharSeq str, int fromIndex) { return back.lastIndexOf(str.back, fromIndex); } /** * Returns the index of the last occurrence of the given element as an {@code Option}, * starting the search at the specified index. * * @param str the substring to search for. * @param fromIndex the index to start the search from. * @return {@code Some(index)} or {@code None} if not found. */ public Option<Integer> lastIndexOfOption(CharSeq str, int fromIndex) { return io.vavr.collection.Collections.indexOption(lastIndexOf(str, fromIndex)); } /** * Returns a string that is a substring of this string. The * substring begins with the character at the specified index and * extends to the end of this string. <p> * Examples: * <blockquote><pre> * "unhappy".substring(2) returns "happy" * "Harbison".substring(3) returns "bison" * "emptiness".substring(9) returns "" (an empty string) * </pre></blockquote> * * @param beginIndex the beginning index, inclusive. * @return the specified substring. * @throws IndexOutOfBoundsException if * {@code beginIndex} is negative or larger than the * length of this {@code CharSeq} object. */ public CharSeq substring(int beginIndex) { return CharSeq.of(back.substring(beginIndex)); } /** * Returns a string that is a substring of this string. The * substring begins at the specified {@code beginIndex} and * extends to the character at index {@code endIndex - 1}. * Thus the length of the substring is {@code endIndex-beginIndex}. * <p> * Examples: * <blockquote><pre> * "hamburger".substring(4, 8) returns "urge" * "smiles".substring(1, 5) returns "mile" * </pre></blockquote> * * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. * @return the specified substring. * @throws IndexOutOfBoundsException if the * {@code beginIndex} is negative, or * {@code endIndex} is larger than the length of * this {@code CharSeq} object, or * {@code beginIndex} is larger than * {@code endIndex}. */ public CharSeq substring(int beginIndex, int endIndex) { return CharSeq.of(back.substring(beginIndex, endIndex)); } @Override public String stringPrefix() { return "CharSeq"; } /** * Returns a string containing the characters in this sequence in the same * order as this sequence. The length of the string will be the length of * this sequence. * * @return a string consisting of exactly this sequence of characters */ @Override public String toString() { return back; } /** * Concatenates the specified string to the end of this string. * <p> * If the length of the argument string is {@code 0}, then this * {@code CharSeq} object is returned. Otherwise, a * {@code CharSeq} object is returned that represents a character * sequence that is the concatenation of the character sequence * represented by this {@code CharSeq} object and the character * sequence represented by the argument string.<p> * Examples: * <blockquote><pre> * "cares".concat("s") returns "caress" * "to".concat("get").concat("her") returns "together" * </pre></blockquote> * * @param str the {@code CharSeq} that is concatenated to the end * of this {@code CharSeq}. * @return a string that represents the concatenation of this object's * characters followed by the string argument's characters. */ public CharSeq concat(CharSeq str) { return CharSeq.of(back.concat(str.back)); } /** * Tells whether or not this string matches the given <a * href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#sum">regular expression</a>. * <p> * An invocation of this method of the form * <i>str</i>{@code .matches(}<i>regex</i>{@code )} yields exactly the * same result as the expression * <blockquote> * {@link Pattern}.{@link Pattern#matches(String, CharSequence) * matches(<i>regex</i>, <i>str</i>)} * </blockquote> * * @param regex the regular expression to which this string is to be matched * @return {@code true} if, and only if, this string matches the * given regular expression * @throws PatternSyntaxException if the regular expression's syntax is invalid * @see Pattern */ public boolean matches(String regex) { return back.matches(regex); } /** * Returns true if and only if this string contains the specified * sequence of char values. * * @param s the sequence to search for * @return true if this string contains {@code s}, false otherwise */ public boolean contains(CharSequence s) { return back.contains(s); } /** * Replaces the first substring of this string that matches the given <a * href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#sum">regular expression</a> with the * given replacement. * <p> * An invocation of this method of the form * <i>str</i>{@code .replaceFirst(}<i>regex</i>{@code ,} <i>repl</i>{@code )} * yields exactly the same result as the expression * <blockquote> * <code> * {@link Pattern}.{@link * Pattern#compile compile}(<i>regex</i>).{@link * Pattern#matcher(CharSequence) matcher}(<i>str</i>).{@link * java.util.regex.Matcher#replaceFirst replaceFirst}(<i>repl</i>) * </code> * </blockquote> * Note that backslashes ({@code \}) and dollar signs ({@code $}) in the * replacement string may cause the results to be different than if it were * being treated as a literal replacement string; see * {@link java.util.regex.Matcher#replaceFirst}. * Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special * meaning of these characters, if desired. * * @param regex the regular expression to which this string is to be matched * @param replacement the string to be substituted for the first match * @return The resulting {@code CharSeq} * @throws PatternSyntaxException if the regular expression's syntax is invalid * @see Pattern */ public CharSeq replaceFirst(String regex, String replacement) { return CharSeq.of(back.replaceFirst(regex, replacement)); } /** * Replaces each substring of this string that matches the given <a * href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#sum">regular expression</a> with the * given replacement. * <p> * An invocation of this method of the form * <i>str</i>{@code .replaceAll(}<i>regex</i>{@code ,} <i>repl</i>{@code )} * yields exactly the same result as the expression * <blockquote> * <code> * {@link Pattern}.{@link * Pattern#compile compile}(<i>regex</i>).{@link * Pattern#matcher(CharSequence) matcher}(<i>str</i>).{@link * java.util.regex.Matcher#replaceAll replaceAll}(<i>repl</i>) * </code> * </blockquote> * Note that backslashes ({@code \}) and dollar signs ({@code $}) in the * replacement string may cause the results to be different than if it were * being treated as a literal replacement string; see * {@link java.util.regex.Matcher#replaceAll Matcher.replaceAll}. * Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special * meaning of these characters, if desired. * * @param regex the regular expression to which this string is to be matched * @param replacement the string to be substituted for each match * @return The resulting {@code CharSeq} * @throws PatternSyntaxException if the regular expression's syntax is invalid * @see Pattern */ public CharSeq replaceAll(String regex, String replacement) { return CharSeq.of(back.replaceAll(regex, replacement)); } /** * Replaces each substring of this string that matches the literal target * sequence with the specified literal replacement sequence. The * replacement proceeds from the beginning of the string to the end, for * example, replacing "aa" with "b" in the string "aaa" will result in * "ba" rather than "ab". * * @param target The sequence of char values to be replaced * @param replacement The replacement sequence of char values * @return The resulting string */ public CharSeq replace(CharSequence target, CharSequence replacement) { return CharSeq.of(back.replace(target, replacement)); } /** * Splits this string around matches of the given * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#sum">regular expression</a>. * <p> * This method works as if by invoking the two-argument {@link #split(String, int)} * method with the given expression and a limit argument of zero. * Trailing empty strings are therefore not included in the resulting {@link Seq}. * <p> * The string {@code "boo:and:foo"}, for example, yields the following results with these expressions: * <blockquote> * <table cellpadding=1 cellspacing=0 summary="Split examples showing regex and result"> * <tr> * <th>Regex</th> * <th>Result</th> * </tr> * <tr> * <td align=center>:</td> * <td>{@code { "boo", "and", "foo" }}</td> * </tr> * <tr> * <td align=center>o</td> * <td>{@code { "b", "", ":and:f" }}</td> * </tr> * </table> * </blockquote> * * @param regex the delimiting regular expression * @return the Seq of strings computed by splitting this string around matches of the given regular expression * @throws PatternSyntaxException if the regular expression's syntax is invalid * @see Pattern */ public Seq<CharSeq> split(String regex) { return split(regex, 0); } /** * Splits this string around matches of the given * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#sum">regular expression</a>. * <p> * The array returned by this method contains each substring of this * string that is terminated by another substring that matches the given * expression or is terminated by the end of the string. The substrings in * the array are in the order in which they occur in this string. If the * expression does not match any part of the input then the resulting array * has just one element, namely this string. * <p> * When there is a positive-width match at the beginning of this * string then an empty leading substring is included at the beginning * of the resulting array. A zero-width match at the beginning however * never produces such empty leading substring. * <p> * The {@code limit} parameter controls the number of times the * pattern is applied and therefore affects the length of the resulting * array. If the limit <i>n</i> is greater than zero then the pattern * will be applied at most <i>n</i> - 1 times, the array's * length will be no greater than <i>n</i>, and the array's last entry * will contain all input beyond the last matched delimiter. If <i>n</i> * is non-positive then the pattern will be applied as many times as * possible and the array can have any length. If <i>n</i> is zero then * the pattern will be applied as many times as possible, the array can * have any length, and trailing empty strings will be discarded. * <p> * The string {@code "boo:and:foo"}, for example, yields the * following results with these parameters: * <blockquote><table cellpadding=1 cellspacing=0 summary="Split example showing regex, limit, and result"> * <tr> * <th>Regex</th> * <th>Limit</th> * <th>Result</th> * </tr> * <tr><td align=center>:</td> * <td align=center>2</td> * <td>{@code { "boo", "and:foo" }}</td></tr> * <tr><td align=center>:</td> * <td align=center>5</td> * <td>{@code { "boo", "and", "foo" }}</td></tr> * <tr><td align=center>:</td> * <td align=center>-2</td> * <td>{@code { "boo", "and", "foo" }}</td></tr> * <tr><td align=center>o</td> * <td align=center>5</td> * <td>{@code { "b", "", ":and:f", "", "" }}</td></tr> * <tr><td align=center>o</td> * <td align=center>-2</td> * <td>{@code { "b", "", ":and:f", "", "" }}</td></tr> * <tr><td align=center>o</td> * <td align=center>0</td> * <td>{@code { "b", "", ":and:f" }}</td></tr> * </table></blockquote> * An invocation of this method of the form * <i>str.</i>{@code split(}<i>regex</i>{@code ,} <i>n</i>{@code )} * yields the same result as the expression * <blockquote> * <code> * {@link Pattern}.{@link * Pattern#compile compile}(<i>regex</i>).{@link * Pattern#split(CharSequence, int) split}(<i>str</i>, <i>n</i>) * </code> * </blockquote> * * @param regex the delimiting regular expression * @param limit the result threshold, as described above * @return the Seq of strings computed by splitting this string around matches of the given regular expression * @throws PatternSyntaxException if the regular expression's syntax is invalid * @see Pattern */ public Seq<CharSeq> split(String regex, int limit) { final Seq<String> split = Array.wrap(back.split(regex, limit)); return split.map(CharSeq::of); } /** * Converts all of the characters in this {@code CharSeq} to lower * case using the rules of the given {@code Locale}. Case mapping is based * on the Unicode Standard version specified by the {@link Character Character} * class. Since case mappings are not always 1:1 char mappings, the resulting * {@code CharSeq} may be a different length than the original {@code CharSeq}. * <p> * Examples of lowercase mappings are in the following table: * <table border="1" summary="Lowercase mapping examples showing language code of locale, upper case, lower case, and description"> * <tr> * <th>Language Code of Locale</th> * <th>Upper Case</th> * <th>Lower Case</th> * <th>Description</th> * </tr> * <tr> * <td>tr (Turkish)</td> * <td>\u0130</td> * <td>\u0069</td> * <td>capital letter I with dot above -> small letter i</td> * </tr> * <tr> * <td>tr (Turkish)</td> * <td>\u0049</td> * <td>\u0131</td> * <td>capital letter I -> small letter dotless i </td> * </tr> * <tr> * <td>(all)</td> * <td>French Fries</td> * <td>french fries</td> * <td>lowercased all chars in String</td> * </tr> * <tr> * <td>(all)</td> * <td><img src="doc-files/capiota.gif" alt="capiota"><img src="doc-files/capchi.gif" alt="capchi"> * <img src="doc-files/captheta.gif" alt="captheta"><img src="doc-files/capupsil.gif" alt="capupsil"> * <img src="doc-files/capsigma.gif" alt="capsigma"></td> * <td><img src="doc-files/iota.gif" alt="iota"><img src="doc-files/chi.gif" alt="chi"> * <img src="doc-files/theta.gif" alt="theta"><img src="doc-files/upsilon.gif" alt="upsilon"> * <img src="doc-files/sigma1.gif" alt="sigma"></td> * <td>lowercased all chars in String</td> * </tr> * </table> * * @param locale use the case transformation rules for this locale * @return the {@code CharSeq}, converted to lowercase. * @see String#toLowerCase() * @see String#toUpperCase() * @see String#toUpperCase(Locale) */ public CharSeq toLowerCase(Locale locale) { return CharSeq.of(back.toLowerCase(locale)); } /** * Converts all of the characters in this {@code CharSeq} to lower * case using the rules of the default locale. This is equivalent to calling * {@code toLowerCase(Locale.getDefault())}. * <p> * <b>Note:</b> This method is locale sensitive, and may produce unexpected * results if used for strings that are intended to be interpreted locale * independently. * Examples are programming language identifiers, protocol keys, and HTML * tags. * For instance, {@code "TITLE".toLowerCase()} in a Turkish locale * returns {@code "t\u005Cu0131tle"}, where '\u005Cu0131' is the * LATIN SMALL LETTER DOTLESS I character. * To obtain correct results for locale insensitive strings, use * {@code toLowerCase(Locale.ROOT)}. * <p> * * @return the {@code CharSeq}, converted to lowercase. * @see String#toLowerCase(Locale) */ public CharSeq toLowerCase() { return CharSeq.of(back.toLowerCase(Locale.getDefault())); } /** * Converts all of the characters in this {@code CharSeq} to upper * case using the rules of the given {@code Locale}. Case mapping is based * on the Unicode Standard version specified by the {@link Character Character} * class. Since case mappings are not always 1:1 char mappings, the resulting * {@code CharSeq} may be a different length than the original {@code CharSeq}. * <p> * Examples of locale-sensitive and 1:M case mappings are in the following table. * * <table border="1" summary="Examples of locale-sensitive and 1:M case mappings. Shows Language code of locale, lower case, upper case, and description."> * <tr> * <th>Language Code of Locale</th> * <th>Lower Case</th> * <th>Upper Case</th> * <th>Description</th> * </tr> * <tr> * <td>tr (Turkish)</td> * <td>\u0069</td> * <td>\u0130</td> * <td>small letter i -> capital letter I with dot above</td> * </tr> * <tr> * <td>tr (Turkish)</td> * <td>\u0131</td> * <td>\u0049</td> * <td>small letter dotless i -> capital letter I</td> * </tr> * <tr> * <td>(all)</td> * <td>\u00df</td> * <td>\u0053 \u0053</td> * <td>small letter sharp s -> two letters: SS</td> * </tr> * <tr> * <td>(all)</td> * <td>Fahrvergnügen</td> * <td>FAHRVERGNÜGEN</td> * <td></td> * </tr> * </table> * * @param locale use the case transformation rules for this locale * @return the {@code CharSeq}, converted to uppercase. * @see String#toUpperCase() * @see String#toLowerCase() * @see String#toLowerCase(Locale) */ public CharSeq toUpperCase(Locale locale) { return CharSeq.of(back.toUpperCase(locale)); } /** * Converts all of the characters in this {@code CharSeq} to upper * case using the rules of the default locale. This method is equivalent to * {@code toUpperCase(Locale.getDefault())}. * <p> * <b>Note:</b> This method is locale sensitive, and may produce unexpected * results if used for strings that are intended to be interpreted locale * independently. * Examples are programming language identifiers, protocol keys, and HTML * tags. * For instance, {@code "title".toUpperCase()} in a Turkish locale * returns {@code "T\u005Cu0130TLE"}, where '\u005Cu0130' is the * LATIN CAPITAL LETTER I WITH DOT ABOVE character. * To obtain correct results for locale insensitive strings, use * {@code toUpperCase(Locale.ROOT)}. * <p> * * @return the {@code CharSeq}, converted to uppercase. * @see String#toUpperCase(Locale) */ public CharSeq toUpperCase() { return CharSeq.of(back.toUpperCase(Locale.getDefault())); } /** * Converts the first character in this {@code CharSeq} to upper * case using the rules of the given {@code Locale}. If the {@code CharSeq} is * empty, it won't have any effect. Case mapping is based * on the Unicode Standard version specified by the {@link Character Character} * class. Since case mappings are not always 1:1 char mappings, the resulting * {@code CharSeq} may be a different length than the original {@code CharSeq}. * <p> * Examples of locale-sensitive and 1:M case mappings are in the following table. * * <table border="1" summary="Examples of locale-sensitive and 1:M case mappings. Shows Language code of locale, lower case, upper case, and description."> * <tr> * <th>Language Code of Locale</th> * <th>Lower Case</th> * <th>Upper Case</th> * <th>Description</th> * </tr> * <tr> * <td>tr (Turkish)</td> * <td>\u0069</td> * <td>\u0130</td> * <td>small letter i -> capital letter I with dot above</td> * </tr> * <tr> * <td>tr (Turkish)</td> * <td>\u0131</td> * <td>\u0049</td> * <td>small letter dotless i -> capital letter I</td> * </tr> * <tr> * <td>(all)</td> * <td>\u00df</td> * <td>\u0053 \u0053</td> * <td>small letter sharp s -> two letters: SS</td> * </tr> * <tr> * <td>(all)</td> * <td>Fahrvergnügen</td> * <td>FAHRVERGNÜGEN</td> * <td></td> * </tr> * </table> * * @param locale use the case transformation rules for this locale * @return the {@code CharSeq}, capitalized. */ public CharSeq capitalize(Locale locale) { if (back.isEmpty()) { return this; } return CharSeq.of(back.substring(0, 1).toUpperCase(locale) + back.substring(1)); } /** * Converts the first character in this {@code CharSeq} to upper * case using the rules of the default locale. If the {@code CharSeq} is * empty, it won't have any effect. This method is equivalent to * {@code capitalize(Locale.getDefault())}. * <p> * <b>Note:</b> This method is locale sensitive, and may produce unexpected * results if used for strings that are intended to be interpreted locale * independently. * Examples are programming language identifiers, protocol keys, and HTML * tags. * For instance, {@code "title".toUpperCase()} in a Turkish locale * returns {@code "T\u005Cu0130TLE"}, where '\u005Cu0130' is the * LATIN CAPITAL LETTER I WITH DOT ABOVE character. * To obtain correct results for locale insensitive strings, use * {@code toUpperCase(Locale.ROOT)}. * <p> * * @return the {@code CharSeq}, capitalized. */ public CharSeq capitalize() { return capitalize(Locale.getDefault()); } /** * Returns a string whose value is this string, with any leading and trailing * whitespace removed. * <p> * If this {@code CharSeq} object represents an empty character * sequence, or the first and last characters of character sequence * represented by this {@code CharSeq} object both have codes * greater than {@code '\u005Cu0020'} (the space character), then a * reference to this {@code CharSeq} object is returned. * <p> * Otherwise, if there is no character with a code greater than * {@code '\u005Cu0020'} in the string, then a * {@code CharSeq} object representing an empty string is * returned. * <p> * Otherwise, let <i>k</i> be the index of the first character in the * string whose code is greater than {@code '\u005Cu0020'}, and let * <i>m</i> be the index of the last character in the string whose code * is greater than {@code '\u005Cu0020'}. A {@code CharSeq} * object is returned, representing the substring of this string that * begins with the character at index <i>k</i> and ends with the * character at index <i>m</i>-that is, the result of * {@code this.substring(k, m + 1)}. * <p> * This method may be used to trim whitespace (as defined above) from * the beginning and end of a string. * * @return A string whose value is this string, with any leading and trailing white * space removed, or this string if it has no leading or * trailing white space. */ public CharSeq trim() { return of(back.trim()); } /** * Converts this string to a new character array. * * @return a newly allocated character array whose length is the length * of this string and whose contents are initialized to contain * the character sequence represented by this string. */ public char[] toCharArray() { return back.toCharArray(); } // -- number conversion /** * Decodes this {@code CharSeq} into a {@code Byte} by calling {@link Byte#decode(String)}. * <p> * We write * * <pre><code> * Byte value = charSeq.decodeByte(); * </code></pre> * * instead of * * <pre><code> * Byte value = Byte.decode(charSeq.mkString()); * </code></pre> * * @return a {@code Byte} object holding the byte value represented by this {@code CharSeq} * @throws NumberFormatException if this {@code CharSeq} does not contain a parsable byte. */ public Byte decodeByte() { return Byte.decode(back); } /** * Decodes this {@code CharSeq} into an {@code Integer} by calling {@link Integer#decode(String)}. * <p> * We write * * <pre><code> * Integer value = charSeq.decodeInteger(); * </code></pre> * * instead of * * <pre><code> * Integer value = Integer.decode(charSeq.mkString()); * </code></pre> * * @return an {@code Integer} object holding the int value represented by this {@code CharSeq} * @throws NumberFormatException if this {@code CharSeq} does not contain a parsable int. */ public Integer decodeInteger() { return Integer.decode(back); } /** * Decodes this {@code CharSeq} into a {@code Long} by calling {@link Long#decode(String)}. * <p> * We write * * <pre><code> * Long value = charSeq.decodeLong(); * </code></pre> * * instead of * * <pre><code> * Long value = Long.decode(charSeq.mkString()); * </code></pre> * * @return a {@code Long} object holding the long value represented by this {@code CharSeq} * @throws NumberFormatException if this {@code CharSeq} does not contain a parsable long. */ public Long decodeLong() { return Long.decode(back); } /** * Decodes this {@code CharSeq} into a {@code Short} by calling {@link Short#decode(String)}. * <p> * We write * * <pre><code> * Short value = charSeq.decodeShort(); * </code></pre> * * instead of * * <pre><code> * Short value = Short.decode(charSeq.mkString()); * </code></pre> * * @return a {@code Short} object holding the short value represented by this {@code CharSeq} * @throws NumberFormatException if this {@code CharSeq} does not contain a parsable short. */ public Short decodeShort() { return Short.decode(back); } /** * Parses this {@code CharSeq} as a boolean by calling {@link Boolean#parseBoolean(String)}. * <p> * We write * * <pre><code> * boolean value = charSeq.parseBoolean(); * </code></pre> * * instead of * * <pre><code> * boolean value = Boolean.parseBoolean(charSeq.mkString()); * </code></pre> * * @return the boolean represented by this {@code CharSeq} */ public boolean parseBoolean() { return Boolean.parseBoolean(back); } /** * Parses this {@code CharSeq} as a signed decimal byte by calling {@link Byte#parseByte(String)}. * <p> * We write * * <pre><code> * byte value = charSeq.parseByte(); * </code></pre> * * instead of * * <pre><code> * byte value = Byte.parseByte(charSeq.mkString()); * </code></pre> * * @return the byte value represented by this {@code CharSeq} in decimal * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable byte. */ public byte parseByte() { return Byte.parseByte(back); } /** * Parses this {@code CharSeq} as a signed byte in the specified radix * by calling {@link Byte#parseByte(String, int)}. * <p> * We write * * <pre><code> * byte value = charSeq.parseByte(radix); * </code></pre> * * instead of * * <pre><code> * byte value = Byte.parseByte(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this {@code CharSeq} * @return the byte value represented by this {@code CharSeq} in the specified radix * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable byte. */ public byte parseByte(int radix) { return Byte.parseByte(back, radix); } /** * Parses this {@code CharSeq} as a double by calling {@link Double#parseDouble(String)}. * <p> * We write * * <pre><code> * double value = charSeq.parseDouble(); * </code></pre> * * instead of * * <pre><code> * double value = Double.parseDouble(charSeq.mkString()); * </code></pre> * * @return the double value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable double. */ public double parseDouble() { return Double.parseDouble(back); } /** * Parses this {@code CharSeq} as a float by calling {@link Float#parseFloat(String)}. * <p> * We write * * <pre><code> * float value = charSeq.parseFloat(); * </code></pre> * * instead of * * <pre><code> * float value = Double.parseFloat(charSeq.mkString()); * </code></pre> * * @return the float value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable float. */ public float parseFloat() { return Float.parseFloat(back); } /** * Parses this {@code CharSeq} as a signed decimal int by calling {@link Integer#parseInt(String)}. * <p> * We write * * <pre><code> * int value = charSeq.parseInt(); * </code></pre> * * instead of * * <pre><code> * int value = Integer.parseInt(charSeq.mkString()); * </code></pre> * * @return the int value represented by this {@code CharSeq} in decimal * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable int. */ public int parseInt() { return Integer.parseInt(back); } /** * Parses this {@code CharSeq} as a signed int in the specified radix * by calling {@link Integer#parseInt(String, int)}. * <p> * We write * * <pre><code> * int value = charSeq.parseInt(radix); * </code></pre> * * instead of * * <pre><code> * int value = Integer.parseInt(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this {@code CharSeq} * @return the int value represented by this {@code CharSeq} in the specified radix * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable int. */ public int parseInt(int radix) { return Integer.parseInt(back, radix); } /** * Parses this {@code CharSeq} as a unsigned decimal int by calling {@link Integer#parseUnsignedInt(String)}. * <p> * We write * * <pre><code> * int value = charSeq.parseUnsignedInt(); * </code></pre> * * instead of * * <pre><code> * int value = Integer.parseUnsignedInt(charSeq.mkString()); * </code></pre> * * @return the unsigned int value represented by this {@code CharSeq} in decimal * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable unsigned int. */ @GwtIncompatible public int parseUnsignedInt() { return Integer.parseUnsignedInt(back); } /** * Parses this {@code CharSeq} as a unsigned int in the specified radix * by calling {@link Integer#parseUnsignedInt(String, int)}. * <p> * We write * * <pre><code> * int value = charSeq.parseUnsignedInt(radix); * </code></pre> * * instead of * * <pre><code> * int value = Integer.parseUnsignedInt(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this {@code CharSeq} * @return the unsigned int value represented by this {@code CharSeq} in the specified radix * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable unsigned int. */ @GwtIncompatible public int parseUnsignedInt(int radix) { return Integer.parseUnsignedInt(back, radix); } /** * Parses this {@code CharSeq} as a signed decimal long by calling {@link Long#parseLong(String)}. * <p> * We write * * <pre><code> * long value = charSeq.parseLong(); * </code></pre> * * instead of * * <pre><code> * long value = Long.parseLong(charSeq.mkString()); * </code></pre> * * @return the long value represented by this {@code CharSeq} in decimal * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable long. */ public long parseLong() { return Long.parseLong(back); } /** * Parses this {@code CharSeq} as a signed long in the specified radix * by calling {@link Long#parseLong(String, int)}. * <p> * We write * * <pre><code> * long value = charSeq.parseLong(radix); * </code></pre> * * instead of * * <pre><code> * long value = Long.parseLong(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this {@code CharSeq} * @return the long value represented by this {@code CharSeq} in the specified radix * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable long. */ public long parseLong(int radix) { return Long.parseLong(back, radix); } /** * Parses this {@code CharSeq} as a unsigned decimal long by calling {@link Long#parseUnsignedLong(String)}. * <p> * We write * * <pre><code> * long value = charSeq.parseUnsignedLong(); * </code></pre> * * instead of * * <pre><code> * long value = Long.parseUnsignedLong(charSeq.mkString()); * </code></pre> * * @return the unsigned long value represented by this {@code CharSeq} in decimal * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable unsigned long. */ @GwtIncompatible public long parseUnsignedLong() { return Long.parseUnsignedLong(back); } /** * Parses this {@code CharSeq} as a unsigned long in the specified radix * by calling {@link Long#parseUnsignedLong(String, int)}. * <p> * We write * * <pre><code> * long value = charSeq.parseUnsignedLong(radix); * </code></pre> * * instead of * * <pre><code> * long value = Long.parseUnsignedLong(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this {@code CharSeq} * @return the unsigned long value represented by this {@code CharSeq} in the specified radix * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable unsigned long. */ @GwtIncompatible public long parseUnsignedLong(int radix) { return Long.parseUnsignedLong(back, radix); } /** * Parses this {@code CharSeq} as a signed decimal short by calling {@link Short#parseShort(String)}. * <p> * We write * * <pre><code> * short value = charSeq.parseShort(); * </code></pre> * * instead of * * <pre><code> * short value = Short.parseShort(charSeq.mkString()); * </code></pre> * * @return the short value represented by this {@code CharSeq} in decimal * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable short. */ public short parseShort() { return Short.parseShort(back); } /** * Parses this {@code CharSeq} as a signed short in the specified radix * by calling {@link Short#parseShort(String, int)}. * <p> * We write * * <pre><code> * short value = charSeq.parseShort(radix); * </code></pre> * * instead of * * <pre><code> * short value = Short.parseShort(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this {@code CharSeq} * @return the short value represented by this {@code CharSeq} in the specified radix * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable short. */ public short parseShort(int radix) { return Short.parseShort(back, radix); } /** * Converts this {@code CharSeq} to a {@code Boolean} by calling {@link Boolean#valueOf(String)}. * <p> * We write * * <pre><code> * Boolean value = charSeq.toBoolean(); * </code></pre> * * instead of * * <pre><code> * Boolean value = Boolean.valueOf(charSeq.mkString()); * </code></pre> * * @return the {@code Boolean} value represented by this {@code CharSeq} */ public Boolean toBoolean() { return Boolean.valueOf(back); } /** * Converts this {@code CharSeq} to a {@code Byte} by calling {@link Byte#valueOf(String)}. * <p> * We write * * <pre><code> * Byte value = charSeq.toByte(); * </code></pre> * * instead of * * <pre><code> * Byte value = Byte.valueOf(charSeq.mkString()); * </code></pre> * * @return a {@code Byte} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable byte. */ public Byte toByte() { return Byte.valueOf(back); } /** * Converts this {@code CharSeq} to a {@code Byte} in the specified radix * by calling {@link Byte#valueOf(String, int)}. * <p> * We write * * <pre><code> * Byte value = charSeq.toByte(radix); * </code></pre> * * instead of * * <pre><code> * Byte value = Byte.valueOf(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this char sequence * @return a {@code Byte} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable byte. */ public Byte toByte(int radix) { return Byte.valueOf(back, radix); } /** * Converts this {@code CharSeq} to a {@code Double} by calling {@link Double#valueOf(String)}. * <p> * We write * * <pre><code> * Double value = charSeq.toDouble(); * </code></pre> * * instead of * * <pre><code> * Double value = Double.valueOf(charSeq.mkString()); * </code></pre> * * @return a {@code Double} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable double. */ public Double toDouble() { return Double.valueOf(back); } /** * Converts this {@code CharSeq} to a {@code Float} by calling {@link Float#valueOf(String)}. * <p> * We write * * <pre><code> * Float value = charSeq.toFloat(); * </code></pre> * * instead of * * <pre><code> * Float value = Float.valueOf(charSeq.mkString()); * </code></pre> * * @return a {@code Float} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable float. */ public Float toFloat() { return Float.valueOf(back); } /** * Converts this {@code CharSeq} to an {@code Integer} by calling {@link Integer#valueOf(String)}. * <p> * We write * * <pre><code> * Integer value = charSeq.toInteger(); * </code></pre> * * instead of * * <pre><code> * Integer value = Integer.valueOf(charSeq.mkString()); * </code></pre> * * @return an {@code Integer} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable int. */ public Integer toInteger() { return Integer.valueOf(back); } /** * Converts this {@code CharSeq} to an {@code Integer} in the specified radix * by calling {@link Integer#valueOf(String, int)}. * <p> * We write * * <pre><code> * Integer value = charSeq.toInteger(radix); * </code></pre> * * instead of * * <pre><code> * Integer value = Integer.valueOf(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this char sequence * @return an {@code Integer} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable int. */ public Integer toInteger(int radix) { return Integer.valueOf(back, radix); } /** * Converts this {@code CharSeq} to a {@code Long} by calling {@link Long#valueOf(String)}. * <p> * We write * * <pre><code> * Long value = charSeq.toLong(); * </code></pre> * * instead of * * <pre><code> * Long value = Long.valueOf(charSeq.mkString()); * </code></pre> * * @return a {@code Long} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable long. */ public Long toLong() { return Long.valueOf(back); } /** * Converts this {@code CharSeq} to a {@code Long} in the specified radix * by calling {@link Long#valueOf(String, int)}. * <p> * We write * * <pre><code> * Long value = charSeq.toLong(radix); * </code></pre> * * instead of * * <pre><code> * Long value = Long.valueOf(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this char sequence * @return a {@code Long} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable long. */ public Long toLong(int radix) { return Long.valueOf(back, radix); } /** * Converts this {@code CharSeq} to a {@code Short} by calling {@link Short#valueOf(String)}. * <p> * We write * * <pre><code> * Short value = charSeq.toShort(); * </code></pre> * * instead of * * <pre><code> * Short value = Short.valueOf(charSeq.mkString()); * </code></pre> * * @return a {@code Short} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable short. */ public Short toShort() { return Short.valueOf(back); } /** * Converts this {@code CharSeq} to a {@code Short} in the specified radix * by calling {@link Short#valueOf(String, int)}. * <p> * We write * * <pre><code> * Short value = charSeq.toShort(radix); * </code></pre> * * instead of * * <pre><code> * Short value = Short.valueOf(charSeq.mkString(), radix); * </code></pre> * * @param radix the radix to be used in interpreting this char sequence * @return a {@code Short} object holding the value represented by this {@code CharSeq} * @throws NumberFormatException If this {@code CharSeq} does not contain a parsable short. */ public Short toShort(int radix) { return Short.valueOf(back, radix); } // -- conversion overrides @Override public Character[] toJavaArray() { return toJavaList().toArray(new Character[0]); } // -- functional interfaces @FunctionalInterface public interface CharUnaryOperator { char apply(char c); } @FunctionalInterface public interface CharFunction<R> { R apply(char c); } } interface CharSeqModule { interface Combinations { static IndexedSeq<CharSeq> apply(CharSeq elements, int k) { if (k == 0) { return Vector.of(CharSeq.empty()); } else { return elements.zipWithIndex().flatMap( t -> apply(elements.drop(t._2 + 1), (k - 1)).map((CharSeq c) -> c.prepend(t._1)) ); } } } }