stream, Object monitor) {
+ Objects.requireNonNull(stream, "stream cannot be null");
+ return new SyncStream<>(stream, monitor);
+ }
+
+ /**
+ * Wraps the given {@link IntStream} to make all
+ *
+ * terminal operations acquire the provided monitor's lock before
+ * execution. Intermediate operations
+ * return streams that are also synchronized on the same object. The created
+ * stream will behave identically
+ * to the provided stream in all other aspects. Use this to synchronize
+ * access to stream's source.
+ *
+ * The returned {@code IntStream}'s {@link IntStream#iterator()
+ * iterator()} and
+ * {@link IntStream#spliterator() spliterator()} methods return regular
+ * non-synchronized iterators and
+ * spliterators respectively. It is the user's responsibility to avoid
+ * concurrency issues:
+ *
+ *
+ * synchronized (stream.getMonitor()) {
+ * PrimitiveIterator.OfInt it = stream.iterator();
+ * ...
+ * }
+ *
+ *
+ * Usage example:
+ *
+ *
+ * Set<Object> s = Collections.synchronizedSet(new HashSet<>());
+ * ...
+ * IntStream stream = SyncStreams.synchronizedStream(s.stream().mapToInt(Object::hashCode), s);
+ * stream = stream.map(i -> i % 67); // Still synchronized
+ * stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
+ *
+ *
+ * @param stream the stream to wrap.
+ * @param monitor the object that the stream will use for synchronization.
+ * When {@code null}, the stream
+ * will synchronize on itself.
+ * @return a {@link SyncIntStream} synchronized on {@code monitor} and
+ * backed by {@code stream}.
+ * @throws NullPointerException if {@code stream == null}.
+ */
+ public static SyncIntStream synchronizedStream(IntStream stream, Object monitor) {
+ Objects.requireNonNull(stream, "stream cannot be null");
+ return new SyncIntStream(stream, monitor);
+ }
+
+ /**
+ * Wraps the given {@link LongStream} to make all
+ *
+ * terminal operations acquire the provided monitor's lock before
+ * execution. Intermediate operations
+ * return streams that are also synchronized on the same object. The created
+ * stream will behave identically
+ * to the provided stream in all other aspects. Use this to synchronize
+ * access to stream's source.
+ *
+ * The returned {@code LongStream}'s {@link LongStream#iterator()
+ * iterator()} and
+ * {@link LongStream#spliterator() spliterator()} methods return regular
+ * non-synchronized iterators and
+ * spliterators respectively. It is the user's responsibility to avoid
+ * concurrency issues:
+ *
+ *
+ * synchronized (stream.getMonitor()) {
+ * PrimitiveIterator.OfLong it = stream.iterator();
+ * ...
+ * }
+ *
+ *
+ * Usage example:
+ *
+ *
+ * Set<Object> s = Collections.synchronizedSet(new HashSet<>());
+ * ...
+ * LongStream stream = SyncStreams.synchronizedStream(s.stream().mapToLong(o -> (long) o.hashCode()), s);
+ * stream = stream.map(i -> i % 67); // Still synchronized
+ * stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
+ *
+ *
+ * @param stream the stream to wrap.
+ * @param monitor the object that the stream will use for synchronization.
+ * When {@code null}, the stream
+ * will synchronize on itself.
+ * @return a {@link SyncLongStream} synchronized on {@code monitor} and
+ * backed by {@code stream}.
+ * @throws NullPointerException if {@code stream == null}.
+ */
+ public static SyncLongStream synchronizedStream(LongStream stream, Object monitor) {
+ Objects.requireNonNull(stream, "stream cannot be null");
+ return new SyncLongStream(stream, monitor);
+ }
+
+ /**
+ * Wraps the given {@link DoubleStream} to make all
+ *
+ * terminal operations acquire the provided monitor's lock before
+ * execution. Intermediate operations
+ * return streams that are also synchronized on the same object. The created
+ * stream will behave identically
+ * to the provided stream in all other aspects. Use this to synchronize
+ * access to stream's source.
+ *
+ * The returned {@code DoubleStream}'s {@link DoubleStream#iterator()
+ * iterator()} and
+ * {@link DoubleStream#spliterator() spliterator()} methods return regular
+ * non-synchronized iterators and
+ * spliterators respectively. It is the user's responsibility to avoid
+ * concurrency issues:
+ *
+ *
+ * synchronized (stream.getMonitor()) {
+ * PrimitiveIterator.OfDouble it = stream.iterator();
+ * ...
+ * }
+ *
+ *
+ * Usage example:
+ *
+ *
+ * Set<Object> s = Collections.synchronizedSet(new HashSet<>());
+ * ...
+ * DoubleStream stream = SyncStreams.synchronizedStream(s.stream().mapToLong(o -> (double) o.hashCode()), s);
+ * stream = stream.map(Math::sin); // Still synchronized
+ * stream.forEach(System.out::println); // Should never throw a ConcurrentModificationException
+ *
+ *
+ * @param stream the stream to wrap.
+ * @param monitor the object that the stream will use for synchronization.
+ * When {@code null}, the stream
+ * will synchronize on itself.
+ * @return a {@link SyncDoubleStream} synchronized on {@code monitor} and
+ * backed by {@code stream}.
+ * @throws NullPointerException if {@code stream == null}.
+ */
+ public static SyncDoubleStream synchronizedStream(DoubleStream stream, Object monitor) {
+ Objects.requireNonNull(stream, "stream cannot be null");
+ return new SyncDoubleStream(stream, monitor);
+ }
+
+ /*
+ * Private constructor
+ */
+ private SyncStreams() {
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/SyntaxException.java b/src/main/java/ru/windcorp/jputil/SyntaxException.java
index e47d123..02cc3c4 100644
--- a/src/main/java/ru/windcorp/jputil/SyntaxException.java
+++ b/src/main/java/ru/windcorp/jputil/SyntaxException.java
@@ -1,44 +1,45 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil;
-
-public class SyntaxException extends Exception {
-
- private static final long serialVersionUID = -4052144233640072750L;
-
- public SyntaxException() {
-
- }
-
- public SyntaxException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-
- public SyntaxException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public SyntaxException(String message) {
- super(message);
- }
-
- public SyntaxException(Throwable cause) {
- super(cause);
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil;
+
+public class SyntaxException extends Exception {
+
+ private static final long serialVersionUID = -4052144233640072750L;
+
+ public SyntaxException() {
+
+ }
+
+ public SyntaxException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public SyntaxException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SyntaxException(String message) {
+ super(message);
+ }
+
+ public SyntaxException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/CharArrayIterator.java b/src/main/java/ru/windcorp/jputil/chars/CharArrayIterator.java
index f65ddf4..be0940d 100644
--- a/src/main/java/ru/windcorp/jputil/chars/CharArrayIterator.java
+++ b/src/main/java/ru/windcorp/jputil/chars/CharArrayIterator.java
@@ -1,128 +1,132 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.text.CharacterIterator;
-
-public class CharArrayIterator implements CharacterIterator {
-
- private final char[] array;
- private int pos;
-
- public CharArrayIterator(char[] array) {
- this.array = array;
- }
-
- public CharArrayIterator(String src) {
- this(src.toCharArray());
- }
-
- @Override
- public char first() {
- pos = 0;
- if (array.length != 0) {
- return array[pos];
- }
- return DONE;
- }
-
- @Override
- public char last() {
- pos = array.length;
- if (array.length != 0) {
- pos -= 1;
- return array[pos];
- }
- return DONE;
- }
-
- @Override
- public char current() {
- if (array.length != 0 && pos < array.length) {
- return array[pos];
- }
- return DONE;
- }
-
- @Override
- public char next() {
- pos += 1;
- if (pos >= array.length) {
- pos = array.length;
- return DONE;
- }
- return current();
- }
-
- @Override
- public char previous() {
- if (pos == 0) {
- return DONE;
- }
- pos -= 1;
- return current();
- }
-
- @Override
- public char setIndex(int position) {
- if (position < 0 || position > array.length) {
- throw new IllegalArgumentException("bad position: " + position);
- }
-
- pos = position;
-
- if (pos != array.length && array.length != 0) {
- return array[pos];
- }
- return DONE;
- }
-
- @Override
- public int getBeginIndex() {
- return 0;
- }
-
- @Override
- public int getEndIndex() {
- return array.length;
- }
-
- @Override
- public int getIndex() {
- return pos;
- }
-
-// @SuppressWarnings("all") Just STFU, this _is_ terrific
-
- // SonarLint: "clone" should not be overridden (java:S2975)
- // And I wouldn't have done that if only CharacterIterator had not required exception safety.
- // SonarLint: "toString()" and "clone()" methods should not return null (java:S2225)
- // The clause is unreachable: CharacterArrayIterator implements Cloneable and superclass is Object.
- @SuppressWarnings({"squid:S2975", "squid:S2225"})
-
- @Override
- public CharArrayIterator clone() {
- try {
- return (CharArrayIterator) super.clone();
- } catch (CloneNotSupportedException cnse) {
- // Impossible
- return null;
- }
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.text.CharacterIterator;
+
+public class CharArrayIterator implements CharacterIterator {
+
+ private final char[] array;
+ private int pos;
+
+ public CharArrayIterator(char[] array) {
+ this.array = array;
+ }
+
+ public CharArrayIterator(String src) {
+ this(src.toCharArray());
+ }
+
+ @Override
+ public char first() {
+ pos = 0;
+ if (array.length != 0) {
+ return array[pos];
+ }
+ return DONE;
+ }
+
+ @Override
+ public char last() {
+ pos = array.length;
+ if (array.length != 0) {
+ pos -= 1;
+ return array[pos];
+ }
+ return DONE;
+ }
+
+ @Override
+ public char current() {
+ if (array.length != 0 && pos < array.length) {
+ return array[pos];
+ }
+ return DONE;
+ }
+
+ @Override
+ public char next() {
+ pos += 1;
+ if (pos >= array.length) {
+ pos = array.length;
+ return DONE;
+ }
+ return current();
+ }
+
+ @Override
+ public char previous() {
+ if (pos == 0) {
+ return DONE;
+ }
+ pos -= 1;
+ return current();
+ }
+
+ @Override
+ public char setIndex(int position) {
+ if (position < 0 || position > array.length) {
+ throw new IllegalArgumentException("bad position: " + position);
+ }
+
+ pos = position;
+
+ if (pos != array.length && array.length != 0) {
+ return array[pos];
+ }
+ return DONE;
+ }
+
+ @Override
+ public int getBeginIndex() {
+ return 0;
+ }
+
+ @Override
+ public int getEndIndex() {
+ return array.length;
+ }
+
+ @Override
+ public int getIndex() {
+ return pos;
+ }
+
+// @SuppressWarnings("all") Just STFU, this _is_ terrific
+
+ // SonarLint: "clone" should not be overridden (java:S2975)
+ // And I wouldn't have done that if only CharacterIterator had not required
+ // exception safety.
+ // SonarLint: "toString()" and "clone()" methods should not return null
+ // (java:S2225)
+ // The clause is unreachable: CharacterArrayIterator implements Cloneable
+ // and superclass is Object.
+ @SuppressWarnings({ "squid:S2975", "squid:S2225" })
+
+ @Override
+ public CharArrayIterator clone() {
+ try {
+ return (CharArrayIterator) super.clone();
+ } catch (CloneNotSupportedException cnse) {
+ // Impossible
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/CharConsumer.java b/src/main/java/ru/windcorp/jputil/chars/CharConsumer.java
index de1d372..7b68bb1 100644
--- a/src/main/java/ru/windcorp/jputil/chars/CharConsumer.java
+++ b/src/main/java/ru/windcorp/jputil/chars/CharConsumer.java
@@ -1,42 +1,43 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.util.function.IntConsumer;
-
-@FunctionalInterface
-public interface CharConsumer {
-
- void accept(char c);
-
- public static CharConsumer andThen(CharConsumer first, CharConsumer second) {
- return c -> {
- first.accept(c);
- second.accept(c);
- };
- }
-
- public static IntConsumer toInt(CharConsumer consumer) {
- return i -> consumer.accept((char) i);
- }
-
- public static CharConsumer toChar(IntConsumer consumer) {
- return consumer::accept;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.util.function.IntConsumer;
+
+@FunctionalInterface
+public interface CharConsumer {
+
+ void accept(char c);
+
+ public static CharConsumer andThen(CharConsumer first, CharConsumer second) {
+ return c -> {
+ first.accept(c);
+ second.accept(c);
+ };
+ }
+
+ public static IntConsumer toInt(CharConsumer consumer) {
+ return i -> consumer.accept((char) i);
+ }
+
+ public static CharConsumer toChar(IntConsumer consumer) {
+ return consumer::accept;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/CharConsumers.java b/src/main/java/ru/windcorp/jputil/chars/CharConsumers.java
index e0613bc..1ac7973 100644
--- a/src/main/java/ru/windcorp/jputil/chars/CharConsumers.java
+++ b/src/main/java/ru/windcorp/jputil/chars/CharConsumers.java
@@ -1,69 +1,70 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.util.Objects;
-
-import ru.windcorp.jputil.ArrayUtil;
-
-/**
- * @author Javapony
- *
- */
-public class CharConsumers {
-
- private CharConsumers() {}
-
- public static CharConsumer fillArray(char[] array, int offset, int length) {
- return new ArrayFiller(array, offset, length);
- }
-
- public static CharConsumer fillArray(char[] array) {
- return fillArray(array, 0, -1);
- }
-
- private static class ArrayFiller implements CharConsumer {
-
- final char[] array;
- int i;
- final int end;
-
- /**
- * @param array
- * @param offset
- * @param length
- */
- ArrayFiller(char[] array, int offset, int length) {
- this.array = Objects.requireNonNull(array, "array");
- this.end = ArrayUtil.checkArrayStartEnd(array, offset, offset + length);
- this.i = offset;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.CharConsumer#accept(char)
- */
- @Override
- public void accept(char c) {
- if (i == end)
- throw new ArrayIndexOutOfBoundsException(end);
- array[i++] = c;
- }
-
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.util.Objects;
+
+import ru.windcorp.jputil.ArrayUtil;
+
+/**
+ * @author Javapony
+ */
+public class CharConsumers {
+
+ private CharConsumers() {
+ }
+
+ public static CharConsumer fillArray(char[] array, int offset, int length) {
+ return new ArrayFiller(array, offset, length);
+ }
+
+ public static CharConsumer fillArray(char[] array) {
+ return fillArray(array, 0, -1);
+ }
+
+ private static class ArrayFiller implements CharConsumer {
+
+ final char[] array;
+ int i;
+ final int end;
+
+ /**
+ * @param array
+ * @param offset
+ * @param length
+ */
+ ArrayFiller(char[] array, int offset, int length) {
+ this.array = Objects.requireNonNull(array, "array");
+ this.end = ArrayUtil.checkArrayStartEnd(array, offset, offset + length);
+ this.i = offset;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.CharConsumer#accept(char)
+ */
+ @Override
+ public void accept(char c) {
+ if (i == end)
+ throw new ArrayIndexOutOfBoundsException(end);
+ array[i++] = c;
+ }
+
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/CharPredicate.java b/src/main/java/ru/windcorp/jputil/chars/CharPredicate.java
index b4be567..03ded02 100644
--- a/src/main/java/ru/windcorp/jputil/chars/CharPredicate.java
+++ b/src/main/java/ru/windcorp/jputil/chars/CharPredicate.java
@@ -1,84 +1,85 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.util.Arrays;
-import java.util.function.IntPredicate;
-
-import ru.windcorp.jputil.ArrayUtil;
-
-@FunctionalInterface
-public interface CharPredicate {
-
- boolean test(char c);
-
- public static CharPredicate and(CharPredicate first, CharPredicate second) {
- return c -> first.test(c) && second.test(c);
- }
-
- public static CharPredicate or(CharPredicate first, CharPredicate second) {
- return c -> first.test(c) || second.test(c);
- }
-
- public static CharPredicate negate(CharPredicate predicate) {
- return c -> !predicate.test(c);
- }
-
- public static IntPredicate toInt(CharPredicate predicate) {
- return i -> predicate.test((char) i);
- }
-
- public static CharPredicate toChar(IntPredicate predicate) {
- return predicate::test;
- }
-
- public static CharPredicate forArray(char... chars) {
- if (chars.length == 0) {
- return c -> false;
- }
-
- if (chars.length == 1) {
- return forChar(chars[0]);
- }
-
- if (chars.length < 16) {
- return c -> ArrayUtil.firstIndexOf(chars, c) >= 0;
- } else {
- final char[] sorted = Arrays.copyOf(chars, chars.length);
- Arrays.sort(sorted);
- return c -> Arrays.binarySearch(chars, c) >= 0;
- }
- }
-
- public static CharPredicate forChar(final char c) {
- return given -> given == c;
- }
-
- public static CharPredicate forRange(final char minInclusive, final char maxExclusive) {
- if (minInclusive > maxExclusive) {
- throw new IllegalArgumentException("min > max: " + minInclusive + " > " + maxExclusive);
- }
-
- if (minInclusive == maxExclusive) {
- return c -> false;
- }
-
- return c -> c >= minInclusive && c < maxExclusive;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.util.Arrays;
+import java.util.function.IntPredicate;
+
+import ru.windcorp.jputil.ArrayUtil;
+
+@FunctionalInterface
+public interface CharPredicate {
+
+ boolean test(char c);
+
+ public static CharPredicate and(CharPredicate first, CharPredicate second) {
+ return c -> first.test(c) && second.test(c);
+ }
+
+ public static CharPredicate or(CharPredicate first, CharPredicate second) {
+ return c -> first.test(c) || second.test(c);
+ }
+
+ public static CharPredicate negate(CharPredicate predicate) {
+ return c -> !predicate.test(c);
+ }
+
+ public static IntPredicate toInt(CharPredicate predicate) {
+ return i -> predicate.test((char) i);
+ }
+
+ public static CharPredicate toChar(IntPredicate predicate) {
+ return predicate::test;
+ }
+
+ public static CharPredicate forArray(char... chars) {
+ if (chars.length == 0) {
+ return c -> false;
+ }
+
+ if (chars.length == 1) {
+ return forChar(chars[0]);
+ }
+
+ if (chars.length < 16) {
+ return c -> ArrayUtil.firstIndexOf(chars, c) >= 0;
+ } else {
+ final char[] sorted = Arrays.copyOf(chars, chars.length);
+ Arrays.sort(sorted);
+ return c -> Arrays.binarySearch(chars, c) >= 0;
+ }
+ }
+
+ public static CharPredicate forChar(final char c) {
+ return given -> given == c;
+ }
+
+ public static CharPredicate forRange(final char minInclusive, final char maxExclusive) {
+ if (minInclusive > maxExclusive) {
+ throw new IllegalArgumentException("min > max: " + minInclusive + " > " + maxExclusive);
+ }
+
+ if (minInclusive == maxExclusive) {
+ return c -> false;
+ }
+
+ return c -> c >= minInclusive && c < maxExclusive;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/CharSupplier.java b/src/main/java/ru/windcorp/jputil/chars/CharSupplier.java
index 580deef..0743d79 100644
--- a/src/main/java/ru/windcorp/jputil/chars/CharSupplier.java
+++ b/src/main/java/ru/windcorp/jputil/chars/CharSupplier.java
@@ -1,35 +1,36 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.util.function.IntSupplier;
-
-@FunctionalInterface
-public interface CharSupplier {
-
- char getAsChar();
-
- public static IntSupplier toInt(CharSupplier consumer) {
- return consumer::getAsChar;
- }
-
- public static CharSupplier toChar(IntSupplier consumer) {
- return () -> (char) consumer.getAsInt();
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.util.function.IntSupplier;
+
+@FunctionalInterface
+public interface CharSupplier {
+
+ char getAsChar();
+
+ public static IntSupplier toInt(CharSupplier consumer) {
+ return consumer::getAsChar;
+ }
+
+ public static CharSupplier toChar(IntSupplier consumer) {
+ return () -> (char) consumer.getAsInt();
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/EscapeException.java b/src/main/java/ru/windcorp/jputil/chars/EscapeException.java
index 2578dad..935f72f 100644
--- a/src/main/java/ru/windcorp/jputil/chars/EscapeException.java
+++ b/src/main/java/ru/windcorp/jputil/chars/EscapeException.java
@@ -1,44 +1,45 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-public class EscapeException extends Exception {
-
- private static final long serialVersionUID = -3647188859290365053L;
-
- public EscapeException() {
- super();
- }
-
- public EscapeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-
- public EscapeException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public EscapeException(String message) {
- super(message);
- }
-
- public EscapeException(Throwable cause) {
- super(cause);
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+public class EscapeException extends Exception {
+
+ private static final long serialVersionUID = -3647188859290365053L;
+
+ public EscapeException() {
+ super();
+ }
+
+ public EscapeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public EscapeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EscapeException(String message) {
+ super(message);
+ }
+
+ public EscapeException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/Escaper.java b/src/main/java/ru/windcorp/jputil/chars/Escaper.java
index 17e9b26..f19f6c7 100644
--- a/src/main/java/ru/windcorp/jputil/chars/Escaper.java
+++ b/src/main/java/ru/windcorp/jputil/chars/Escaper.java
@@ -1,474 +1,514 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.text.CharacterIterator;
-
-import ru.windcorp.jputil.ArrayUtil;
-import ru.windcorp.jputil.chars.reader.CharReader;
-import ru.windcorp.jputil.chars.reader.CharReaders;
-
-public class Escaper {
-
- public static class EscaperBuilder {
- private char escapeChar = '\\';
- private char unicodeEscapeChar = 'u';
- private char[] safes = null;
- private char[] unsafes = null;
-
- private boolean preferUnicode = false;
- private boolean strict = true;
-
- public EscaperBuilder withEscapeChar(char escapeChar) {
- this.escapeChar = escapeChar;
- return this;
- }
-
- public EscaperBuilder withUnicodeEscapeChar(char unicodeEscapeChar) {
- this.unicodeEscapeChar = unicodeEscapeChar;
- return this;
- }
-
- public EscaperBuilder withChars(char[] safes, char[] unsafes) {
- this.safes = safes;
- this.unsafes = unsafes;
- return this;
- }
-
- public EscaperBuilder withChars(String safes, String unsafes) {
- this.safes = safes.toCharArray();
- this.unsafes = unsafes.toCharArray();
- return this;
- }
-
- public EscaperBuilder withChars(char[] chars) {
- this.safes = this.unsafes = chars;
- return this;
- }
-
- public EscaperBuilder withChars(String chars) {
- this.safes = this.unsafes = chars.toCharArray();
- return this;
- }
-
- public EscaperBuilder withSafes(char[] safes) {
- this.safes = safes;
- return this;
- }
-
- public EscaperBuilder withSafes(String safes) {
- this.safes = safes.toCharArray();
- return this;
- }
-
- public EscaperBuilder withUnsafes(char[] unsafes) {
- this.unsafes = unsafes;
- return this;
- }
-
- public EscaperBuilder withUnsafes(String unsafes) {
- this.unsafes = unsafes.toCharArray();
- return this;
- }
-
- public EscaperBuilder preferUnicode(boolean preferUnicode) {
- this.preferUnicode = preferUnicode;
- return this;
- }
-
- public EscaperBuilder strict(boolean strict) {
- this.strict = strict;
- return this;
- }
-
- public Escaper build() {
- return new Escaper(escapeChar, unicodeEscapeChar, safes, unsafes, preferUnicode, strict);
- }
-
- }
-
- public static final Escaper JAVA = new Escaper('\\', 'u', "tbnrf'\"".toCharArray(), "\t\b\n\r\f\'\"".toCharArray(), true, true);
-
- private final char escapeChar;
- private final char unicodeEscapeChar;
- private final char[] safes;
- private final char[] unsafes;
-
- private final boolean preferUnicode;
- private final boolean strict;
-
- protected Escaper(
- char escapeChar, char unicodeEscapeChar,
- char[] safes, char[] unsafes,
- boolean preferUnicode, boolean strict) {
- this.escapeChar = escapeChar;
- this.unicodeEscapeChar = unicodeEscapeChar;
- this.safes = safes;
- this.unsafes = unsafes;
- this.preferUnicode = preferUnicode;
- this.strict = strict;
-
- int duplicate;
- if ((duplicate = ArrayUtil.hasDuplicates(safes)) != -1)
- throw new IllegalArgumentException("Duplicate safe character '" + safes[duplicate] + "'");
-
- if ((duplicate = ArrayUtil.hasDuplicates(unsafes)) != -1)
- throw new IllegalArgumentException("Duplicate unsafe character '" + unsafes[duplicate] + "'");
-
- for (char c : safes) {
- if (c == escapeChar) throw new IllegalArgumentException("Safe characters contain escape chatacter");
- if (c == unicodeEscapeChar) throw new IllegalArgumentException("Safe characters contain Unicode escape chatacter");
- }
-
- for (char c : unsafes) {
- if (c == escapeChar) throw new IllegalArgumentException("Unsafe characters contain escape chatacter (escape character is escaped automatically)");
- if (c == unicodeEscapeChar) throw new IllegalArgumentException("Unsafe characters contain Unicode escape chatacter");
- }
- }
-
- public static EscaperBuilder create() {
- return new EscaperBuilder();
- }
-
- /*
- * Logic - escape
- */
-
- public void escape(CharReader src, int length, CharPredicate until, CharConsumer output) {
- int end;
- if (length < 0) end = Integer.MAX_VALUE;
- else end = src.getPosition() + length;
- while (src.has() &&
- src.getPosition() < end &&
- (until == null || !until.test(src.current())))
- escape(src.consume(), output);
- }
-
- public void escape(char c, CharConsumer output) {
- if (c == escapeChar) {
- output.accept(escapeChar);
- output.accept(escapeChar);
- return;
- }
-
- int index = ArrayUtil.firstIndexOf(unsafes, c);
-
- if (index >= 0) {
- output.accept(escapeChar);
- output.accept(safes[index]);
- } else {
- if (preferUnicode && !isRegular(c)) {
- escapeAsHex(c, output);
- } else {
- output.accept(c);
- }
- }
- }
-
- // SonarLint: Assignments should not be made from within sub-expressions (java:S1121)
- // Seems self-evident enough
- @SuppressWarnings("squid:S1121")
-
- private void escapeAsHex(char c, CharConsumer output) {
- output.accept(escapeChar);
- output.accept(unicodeEscapeChar);
- output.accept(StringUtil.hexDigit(c >>= (4 * 3)));
- output.accept(StringUtil.hexDigit(c >>= (4 * 2)));
- output.accept(StringUtil.hexDigit(c >>= (4 * 1)));
- output.accept(StringUtil.hexDigit(c >> (4 * 0)));
- }
-
- public int getEscapedLength(CharReader src, int length, CharPredicate until) {
- int end;
- if (length < 0) end = Integer.MAX_VALUE;
- else end = src.getPosition() + length;
-
- int result = 0;
-
- while (src.has() &&
- src.getPosition() < end &&
- (until == null || !until.test(src.current()))) {
- result += getEscapedLength(src.consume());
- }
-
- return result;
- }
-
- public int getEscapedLength(char c) {
- if (c == escapeChar || ArrayUtil.firstIndexOf(unsafes, c) >= 0)
- return 2;
- else {
- if (preferUnicode && !isRegular(c))
- return 6;
- else
- return 1;
- }
- }
-
- /*
- * Logic - unescape
- */
-
- public void unescape(CharReader src, int length, CharPredicate until, CharConsumer output) throws EscapeException {
- int end;
- if (length < 0) end = Integer.MAX_VALUE;
- else end = src.getPosition() + length;
- while (src.has() &&
- src.getPosition() < end &&
- (until == null || !until.test(src.current()))) {
- output.accept(unescapeOneSequence(src));
- }
- }
-
- public char unescapeOneSequence(CharReader src) throws EscapeException {
- int resetPos = src.getPosition();
- try {
- if (src.current() == escapeChar) {
- src.next();
-
- if (src.isEnd())
- throw new EscapeException("Incomplete escape sequence at the end");
-
- if (src.current() == escapeChar) {
- src.next();
- return escapeChar;
- }
-
- if (src.current() == unicodeEscapeChar) {
- src.next();
- return (char) (
- hexValue(src.consume()) << (4 * 3) |
- hexValue(src.consume()) << (4 * 2) |
- hexValue(src.consume()) << (4 * 1) |
- hexValue(src.consume()) << (4 * 0)
- );
- }
-
- int index = ArrayUtil.firstIndexOf(safes, src.current());
- if (index >= 0) {
- src.next();
- return unsafes[index];
- }
-
- if (strict)
- throw new EscapeException("Unknown escape sequence \"" + escapeChar + src.current() + "\"");
- else
- return src.consume();
- } else
- return src.consume();
- } catch (EscapeException | RuntimeException e) {
- src.setPosition(resetPos);
- throw e;
- }
- }
-
- public int getUnescapedLength(CharReader src, int length, CharPredicate until) {
- int end;
- if (length < 0) end = Integer.MAX_VALUE;
- else end = src.getPosition() + length;
-
- int result = 0;
-
- while (src.has() &&
- src.getPosition() < end &&
- (until == null || !until.test(src.current()))) {
- skipOneSequence(src);
- result++;
- }
-
- return result;
- }
-
- public void skipOneSequence(CharReader src) {
- if (
- src.current() == escapeChar
- &&
- src.next() == unicodeEscapeChar
- ) {
- src.advance(4);
- }
- src.next();
- }
-
- /*
- * Utility
- */
-
- public void escape(CharReader src, int length, CharConsumer output) {
- escape(src, length, null, output);
- }
-
- public void escape(CharReader src, CharPredicate until, CharConsumer output) {
- escape(src, -1, until, output);
- }
-
- public void escape(CharReader src, CharConsumer output) {
- escape(src, -1, null, output);
- }
-
- public int getEscapedLength(CharReader src, int length) {
- return getEscapedLength(src, length, null);
- }
-
- public int getEscapedLength(CharReader src, CharPredicate until) {
- return getEscapedLength(src, -1, until);
- }
-
- public int getEscapedLength(CharReader src) {
- return getEscapedLength(src, -1, null);
- }
-
- public char[] escape(CharReader src, int length, CharPredicate until) {
- src.mark();
- char[] result = new char[getEscapedLength(src, length, until)];
- src.reset();
- escape(src, length, until, CharConsumers.fillArray(result));
- return result;
- }
-
- public char[] escape(CharReader src, int length) {
- return escape(src, length, (CharPredicate) null);
- }
-
- public char[] escape(CharReader src, CharPredicate until) {
- return escape(src, -1, until);
- }
-
- public char[] escape(CharReader src) {
- return escape(src, -1, (CharPredicate) null);
- }
-
- public void unescape(CharReader src, int length, CharConsumer output) throws EscapeException {
- unescape(src, length, null, output);
- }
-
- public void unescape(CharReader src, CharPredicate until, CharConsumer output) throws EscapeException {
- unescape(src, -1, until, output);
- }
-
- public void unescape(CharReader src, CharConsumer output) throws EscapeException {
- unescape(src, -1, null, output);
- }
-
- public int getUnescapedLength(CharReader src, int length) {
- return getUnescapedLength(src, length, null);
- }
-
- public int getUnescapedLength(CharReader src, CharPredicate until) {
- return getUnescapedLength(src, -1, until);
- }
-
- public int getUnescapedLength(CharReader src) {
- return getUnescapedLength(src, -1, null);
- }
-
- public char[] unescape(CharReader src, int length, CharPredicate until) throws EscapeException {
- src.mark();
- char[] result = new char[getUnescapedLength(src, length, until)];
- src.reset();
- unescape(src, length, until, CharConsumers.fillArray(result));
- return result;
- }
-
- public char[] unescape(CharReader src, int length) throws EscapeException {
- return unescape(src, length, (CharPredicate) null);
- }
-
- public char[] unescape(CharReader src, CharPredicate until) throws EscapeException {
- return unescape(src, -1, until);
- }
-
- public char[] unescape(CharReader src) throws EscapeException {
- return unescape(src, -1, (CharPredicate) null);
- }
-
- @Deprecated()
- public char[] unescape(CharacterIterator src, char until) throws EscapeException {
- int index = src.getIndex();
- CharReader reader = CharReaders.wrap(src);
-
- char[] result = unescape(reader, -1, CharPredicate.forChar(until));
-
- src.setIndex(index + reader.getPosition());
- return result;
- }
-
- public String escape(String src) {
- StringBuilder result = new StringBuilder(src.length());
- escape(CharReaders.wrap(src), (CharConsumer) result::append);
- return result.toString();
- }
-
- public String unescape(String src) throws EscapeException {
- StringBuilder result = new StringBuilder(src.length());
- unescape(CharReaders.wrap(src), (CharConsumer) result::append);
- return result.toString();
- }
-
- /*
- * Misc
- */
-
- private static int hexValue(char c) throws EscapeException {
- if (c < '0') throw thisIsNotAHexDigit(c);
- if (c <= '9') return c - '0';
- if (c < 'A') throw thisIsNotAHexDigit(c);
- if (c <= 'F') return c - 'A';
- if (c < 'a') throw thisIsNotAHexDigit(c);
- if (c <= 'f') return c - 'a';
- if (c == CharReader.DONE) throw new EscapeException("Incomplete Unicode escape sequence at the end");
- throw thisIsNotAHexDigit(c);
- }
-
- private static EscapeException thisIsNotAHexDigit(char c) {
- return new EscapeException("Invalid hex digit '" + c + "', expected [0-9A-Fa-f]");
- }
-
- protected static boolean isRegular(char c) {
- return c >= ' ' && c <= '~';
- }
-
- /*
- * Getters / setters
- */
-
- public char getEscapeChar() {
- return escapeChar;
- }
-
- public char getUnicodeEscapeChar() {
- return unicodeEscapeChar;
- }
-
- public char[] getSafes() {
- return safes;
- }
-
- public char[] getUnsafes() {
- return unsafes;
- }
-
- public boolean isPreferUnicode() {
- return preferUnicode;
- }
-
- public boolean isStrict() {
- return strict;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.text.CharacterIterator;
+
+import ru.windcorp.jputil.ArrayUtil;
+import ru.windcorp.jputil.chars.reader.CharReader;
+import ru.windcorp.jputil.chars.reader.CharReaders;
+
+public class Escaper {
+
+ public static class EscaperBuilder {
+ private char escapeChar = '\\';
+ private char unicodeEscapeChar = 'u';
+ private char[] safes = null;
+ private char[] unsafes = null;
+
+ private boolean preferUnicode = false;
+ private boolean strict = true;
+
+ public EscaperBuilder withEscapeChar(char escapeChar) {
+ this.escapeChar = escapeChar;
+ return this;
+ }
+
+ public EscaperBuilder withUnicodeEscapeChar(char unicodeEscapeChar) {
+ this.unicodeEscapeChar = unicodeEscapeChar;
+ return this;
+ }
+
+ public EscaperBuilder withChars(char[] safes, char[] unsafes) {
+ this.safes = safes;
+ this.unsafes = unsafes;
+ return this;
+ }
+
+ public EscaperBuilder withChars(String safes, String unsafes) {
+ this.safes = safes.toCharArray();
+ this.unsafes = unsafes.toCharArray();
+ return this;
+ }
+
+ public EscaperBuilder withChars(char[] chars) {
+ this.safes = this.unsafes = chars;
+ return this;
+ }
+
+ public EscaperBuilder withChars(String chars) {
+ this.safes = this.unsafes = chars.toCharArray();
+ return this;
+ }
+
+ public EscaperBuilder withSafes(char[] safes) {
+ this.safes = safes;
+ return this;
+ }
+
+ public EscaperBuilder withSafes(String safes) {
+ this.safes = safes.toCharArray();
+ return this;
+ }
+
+ public EscaperBuilder withUnsafes(char[] unsafes) {
+ this.unsafes = unsafes;
+ return this;
+ }
+
+ public EscaperBuilder withUnsafes(String unsafes) {
+ this.unsafes = unsafes.toCharArray();
+ return this;
+ }
+
+ public EscaperBuilder preferUnicode(boolean preferUnicode) {
+ this.preferUnicode = preferUnicode;
+ return this;
+ }
+
+ public EscaperBuilder strict(boolean strict) {
+ this.strict = strict;
+ return this;
+ }
+
+ public Escaper build() {
+ return new Escaper(escapeChar, unicodeEscapeChar, safes, unsafes, preferUnicode, strict);
+ }
+
+ }
+
+ public static final Escaper JAVA = new Escaper(
+ '\\',
+ 'u',
+ "tbnrf'\"".toCharArray(),
+ "\t\b\n\r\f\'\"".toCharArray(),
+ true,
+ true
+ );
+
+ private final char escapeChar;
+ private final char unicodeEscapeChar;
+ private final char[] safes;
+ private final char[] unsafes;
+
+ private final boolean preferUnicode;
+ private final boolean strict;
+
+ protected Escaper(
+ char escapeChar,
+ char unicodeEscapeChar,
+ char[] safes,
+ char[] unsafes,
+ boolean preferUnicode,
+ boolean strict
+ ) {
+ this.escapeChar = escapeChar;
+ this.unicodeEscapeChar = unicodeEscapeChar;
+ this.safes = safes;
+ this.unsafes = unsafes;
+ this.preferUnicode = preferUnicode;
+ this.strict = strict;
+
+ int duplicate;
+ if ((duplicate = ArrayUtil.hasDuplicates(safes)) != -1)
+ throw new IllegalArgumentException("Duplicate safe character '" + safes[duplicate] + "'");
+
+ if ((duplicate = ArrayUtil.hasDuplicates(unsafes)) != -1)
+ throw new IllegalArgumentException("Duplicate unsafe character '" + unsafes[duplicate] + "'");
+
+ for (char c : safes) {
+ if (c == escapeChar)
+ throw new IllegalArgumentException("Safe characters contain escape chatacter");
+ if (c == unicodeEscapeChar)
+ throw new IllegalArgumentException("Safe characters contain Unicode escape chatacter");
+ }
+
+ for (char c : unsafes) {
+ if (c == escapeChar)
+ throw new IllegalArgumentException(
+ "Unsafe characters contain escape chatacter (escape character is escaped automatically)"
+ );
+ if (c == unicodeEscapeChar)
+ throw new IllegalArgumentException("Unsafe characters contain Unicode escape chatacter");
+ }
+ }
+
+ public static EscaperBuilder create() {
+ return new EscaperBuilder();
+ }
+
+ /*
+ * Logic - escape
+ */
+
+ public void escape(CharReader src, int length, CharPredicate until, CharConsumer output) {
+ int end;
+ if (length < 0)
+ end = Integer.MAX_VALUE;
+ else
+ end = src.getPosition() + length;
+ while (
+ src.has() &&
+ src.getPosition() < end &&
+ (until == null || !until.test(src.current()))
+ )
+ escape(src.consume(), output);
+ }
+
+ public void escape(char c, CharConsumer output) {
+ if (c == escapeChar) {
+ output.accept(escapeChar);
+ output.accept(escapeChar);
+ return;
+ }
+
+ int index = ArrayUtil.firstIndexOf(unsafes, c);
+
+ if (index >= 0) {
+ output.accept(escapeChar);
+ output.accept(safes[index]);
+ } else {
+ if (preferUnicode && !isRegular(c)) {
+ escapeAsHex(c, output);
+ } else {
+ output.accept(c);
+ }
+ }
+ }
+
+ // SonarLint: Assignments should not be made from within sub-expressions
+ // (java:S1121)
+ // Seems self-evident enough
+ @SuppressWarnings("squid:S1121")
+
+ private void escapeAsHex(char c, CharConsumer output) {
+ output.accept(escapeChar);
+ output.accept(unicodeEscapeChar);
+ output.accept(StringUtil.hexDigit(c >>= (4 * 3)));
+ output.accept(StringUtil.hexDigit(c >>= (4 * 2)));
+ output.accept(StringUtil.hexDigit(c >>= (4 * 1)));
+ output.accept(StringUtil.hexDigit(c >> (4 * 0)));
+ }
+
+ public int getEscapedLength(CharReader src, int length, CharPredicate until) {
+ int end;
+ if (length < 0)
+ end = Integer.MAX_VALUE;
+ else
+ end = src.getPosition() + length;
+
+ int result = 0;
+
+ while (
+ src.has() &&
+ src.getPosition() < end &&
+ (until == null || !until.test(src.current()))
+ ) {
+ result += getEscapedLength(src.consume());
+ }
+
+ return result;
+ }
+
+ public int getEscapedLength(char c) {
+ if (c == escapeChar || ArrayUtil.firstIndexOf(unsafes, c) >= 0)
+ return 2;
+ else {
+ if (preferUnicode && !isRegular(c))
+ return 6;
+ else
+ return 1;
+ }
+ }
+
+ /*
+ * Logic - unescape
+ */
+
+ public void unescape(CharReader src, int length, CharPredicate until, CharConsumer output) throws EscapeException {
+ int end;
+ if (length < 0)
+ end = Integer.MAX_VALUE;
+ else
+ end = src.getPosition() + length;
+ while (
+ src.has() &&
+ src.getPosition() < end &&
+ (until == null || !until.test(src.current()))
+ ) {
+ output.accept(unescapeOneSequence(src));
+ }
+ }
+
+ public char unescapeOneSequence(CharReader src) throws EscapeException {
+ int resetPos = src.getPosition();
+ try {
+ if (src.current() == escapeChar) {
+ src.next();
+
+ if (src.isEnd())
+ throw new EscapeException("Incomplete escape sequence at the end");
+
+ if (src.current() == escapeChar) {
+ src.next();
+ return escapeChar;
+ }
+
+ if (src.current() == unicodeEscapeChar) {
+ src.next();
+ return (char) (hexValue(src.consume()) << (4 * 3) |
+ hexValue(src.consume()) << (4 * 2) |
+ hexValue(src.consume()) << (4 * 1) |
+ hexValue(src.consume()) << (4 * 0));
+ }
+
+ int index = ArrayUtil.firstIndexOf(safes, src.current());
+ if (index >= 0) {
+ src.next();
+ return unsafes[index];
+ }
+
+ if (strict)
+ throw new EscapeException("Unknown escape sequence \"" + escapeChar + src.current() + "\"");
+ else
+ return src.consume();
+ } else
+ return src.consume();
+ } catch (EscapeException | RuntimeException e) {
+ src.setPosition(resetPos);
+ throw e;
+ }
+ }
+
+ public int getUnescapedLength(CharReader src, int length, CharPredicate until) {
+ int end;
+ if (length < 0)
+ end = Integer.MAX_VALUE;
+ else
+ end = src.getPosition() + length;
+
+ int result = 0;
+
+ while (
+ src.has() &&
+ src.getPosition() < end &&
+ (until == null || !until.test(src.current()))
+ ) {
+ skipOneSequence(src);
+ result++;
+ }
+
+ return result;
+ }
+
+ public void skipOneSequence(CharReader src) {
+ if (
+ src.current() == escapeChar
+ &&
+ src.next() == unicodeEscapeChar
+ ) {
+ src.advance(4);
+ }
+ src.next();
+ }
+
+ /*
+ * Utility
+ */
+
+ public void escape(CharReader src, int length, CharConsumer output) {
+ escape(src, length, null, output);
+ }
+
+ public void escape(CharReader src, CharPredicate until, CharConsumer output) {
+ escape(src, -1, until, output);
+ }
+
+ public void escape(CharReader src, CharConsumer output) {
+ escape(src, -1, null, output);
+ }
+
+ public int getEscapedLength(CharReader src, int length) {
+ return getEscapedLength(src, length, null);
+ }
+
+ public int getEscapedLength(CharReader src, CharPredicate until) {
+ return getEscapedLength(src, -1, until);
+ }
+
+ public int getEscapedLength(CharReader src) {
+ return getEscapedLength(src, -1, null);
+ }
+
+ public char[] escape(CharReader src, int length, CharPredicate until) {
+ src.mark();
+ char[] result = new char[getEscapedLength(src, length, until)];
+ src.reset();
+ escape(src, length, until, CharConsumers.fillArray(result));
+ return result;
+ }
+
+ public char[] escape(CharReader src, int length) {
+ return escape(src, length, (CharPredicate) null);
+ }
+
+ public char[] escape(CharReader src, CharPredicate until) {
+ return escape(src, -1, until);
+ }
+
+ public char[] escape(CharReader src) {
+ return escape(src, -1, (CharPredicate) null);
+ }
+
+ public void unescape(CharReader src, int length, CharConsumer output) throws EscapeException {
+ unescape(src, length, null, output);
+ }
+
+ public void unescape(CharReader src, CharPredicate until, CharConsumer output) throws EscapeException {
+ unescape(src, -1, until, output);
+ }
+
+ public void unescape(CharReader src, CharConsumer output) throws EscapeException {
+ unescape(src, -1, null, output);
+ }
+
+ public int getUnescapedLength(CharReader src, int length) {
+ return getUnescapedLength(src, length, null);
+ }
+
+ public int getUnescapedLength(CharReader src, CharPredicate until) {
+ return getUnescapedLength(src, -1, until);
+ }
+
+ public int getUnescapedLength(CharReader src) {
+ return getUnescapedLength(src, -1, null);
+ }
+
+ public char[] unescape(CharReader src, int length, CharPredicate until) throws EscapeException {
+ src.mark();
+ char[] result = new char[getUnescapedLength(src, length, until)];
+ src.reset();
+ unescape(src, length, until, CharConsumers.fillArray(result));
+ return result;
+ }
+
+ public char[] unescape(CharReader src, int length) throws EscapeException {
+ return unescape(src, length, (CharPredicate) null);
+ }
+
+ public char[] unescape(CharReader src, CharPredicate until) throws EscapeException {
+ return unescape(src, -1, until);
+ }
+
+ public char[] unescape(CharReader src) throws EscapeException {
+ return unescape(src, -1, (CharPredicate) null);
+ }
+
+ @Deprecated()
+ public char[] unescape(CharacterIterator src, char until) throws EscapeException {
+ int index = src.getIndex();
+ CharReader reader = CharReaders.wrap(src);
+
+ char[] result = unescape(reader, -1, CharPredicate.forChar(until));
+
+ src.setIndex(index + reader.getPosition());
+ return result;
+ }
+
+ public String escape(String src) {
+ StringBuilder result = new StringBuilder(src.length());
+ escape(CharReaders.wrap(src), (CharConsumer) result::append);
+ return result.toString();
+ }
+
+ public String unescape(String src) throws EscapeException {
+ StringBuilder result = new StringBuilder(src.length());
+ unescape(CharReaders.wrap(src), (CharConsumer) result::append);
+ return result.toString();
+ }
+
+ /*
+ * Misc
+ */
+
+ private static int hexValue(char c) throws EscapeException {
+ if (c < '0')
+ throw thisIsNotAHexDigit(c);
+ if (c <= '9')
+ return c - '0';
+ if (c < 'A')
+ throw thisIsNotAHexDigit(c);
+ if (c <= 'F')
+ return c - 'A';
+ if (c < 'a')
+ throw thisIsNotAHexDigit(c);
+ if (c <= 'f')
+ return c - 'a';
+ if (c == CharReader.DONE)
+ throw new EscapeException("Incomplete Unicode escape sequence at the end");
+ throw thisIsNotAHexDigit(c);
+ }
+
+ private static EscapeException thisIsNotAHexDigit(char c) {
+ return new EscapeException("Invalid hex digit '" + c + "', expected [0-9A-Fa-f]");
+ }
+
+ protected static boolean isRegular(char c) {
+ return c >= ' ' && c <= '~';
+ }
+
+ /*
+ * Getters / setters
+ */
+
+ public char getEscapeChar() {
+ return escapeChar;
+ }
+
+ public char getUnicodeEscapeChar() {
+ return unicodeEscapeChar;
+ }
+
+ public char[] getSafes() {
+ return safes;
+ }
+
+ public char[] getUnsafes() {
+ return unsafes;
+ }
+
+ public boolean isPreferUnicode() {
+ return preferUnicode;
+ }
+
+ public boolean isStrict() {
+ return strict;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/FancyCharacterIterator.java b/src/main/java/ru/windcorp/jputil/chars/FancyCharacterIterator.java
index b309786..183b672 100644
--- a/src/main/java/ru/windcorp/jputil/chars/FancyCharacterIterator.java
+++ b/src/main/java/ru/windcorp/jputil/chars/FancyCharacterIterator.java
@@ -1,17 +1,21 @@
-/*
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
+
package ru.windcorp.jputil.chars;
import java.text.CharacterIterator;
@@ -26,77 +30,81 @@ public class FancyCharacterIterator implements CharacterIterator {
this.data = data;
}
- @Override
+ @Override
public char first() {
return obj.first();
}
- @Override
+ @Override
public char last() {
return obj.last();
}
- @Override
+ @Override
public char setIndex(int p) {
return obj.setIndex(p);
}
- @Override
+ @Override
public char current() {
return obj.current();
}
- @Override
+ @Override
public char next() {
return obj.next();
}
- @Override
+ @Override
public char previous() {
return obj.previous();
}
- @Override
+ @Override
public int getBeginIndex() {
return obj.getBeginIndex();
}
- @Override
+ @Override
public int getEndIndex() {
return obj.getEndIndex();
}
- @Override
+ @Override
public int getIndex() {
return obj.getIndex();
}
-
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("\"");
sb.append(data);
sb.append("\"\n ");
- for (int i = 0; i < obj.getIndex(); ++i) sb.append(' ');
+ for (int i = 0; i < obj.getIndex(); ++i)
+ sb.append(' ');
sb.append("^ Here.");
return sb.toString();
}
-
-// @SuppressWarnings("all") Just STFU, this _is_ terrific
-
- // SonarLint: "clone" should not be overridden (java:S2975)
- // And I wouldn't have done that if only CharacterIterator had not required exception safety.
- // SonarLint: "toString()" and "clone()" methods should not return null (java:S2225)
- // The clause is unreachable: CharacterArrayIterator implements Cloneable and superclass is Object.
- @SuppressWarnings({"squid:S2975", "squid:S2225"})
-
- @Override
- public FancyCharacterIterator clone() {
- try {
- return (FancyCharacterIterator) super.clone();
- } catch (CloneNotSupportedException cnse) {
- // Impossible
- return null;
- }
- }
-}
\ No newline at end of file
+// @SuppressWarnings("all") Just STFU, this _is_ terrific
+
+ // SonarLint: "clone" should not be overridden (java:S2975)
+ // And I wouldn't have done that if only CharacterIterator had not required
+ // exception safety.
+ // SonarLint: "toString()" and "clone()" methods should not return null
+ // (java:S2225)
+ // The clause is unreachable: CharacterArrayIterator implements Cloneable
+ // and superclass is Object.
+ @SuppressWarnings({ "squid:S2975", "squid:S2225" })
+
+ @Override
+ public FancyCharacterIterator clone() {
+ try {
+ return (FancyCharacterIterator) super.clone();
+ } catch (CloneNotSupportedException cnse) {
+ // Impossible
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/IndentedStringBuilder.java b/src/main/java/ru/windcorp/jputil/chars/IndentedStringBuilder.java
index 2bfe3b6..92ba836 100644
--- a/src/main/java/ru/windcorp/jputil/chars/IndentedStringBuilder.java
+++ b/src/main/java/ru/windcorp/jputil/chars/IndentedStringBuilder.java
@@ -1,135 +1,138 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-public class IndentedStringBuilder {
-
- private final StringBuilder sb = new StringBuilder();
-
- private int indentLevel = 0;
- private boolean indentApplied = false;
-
- private String[] indentCache = new String[16];
- private String indent = "";
- private final char[] indentFill;
-
- public IndentedStringBuilder(char[] indentFill) {
- this.indentFill = indentFill;
- }
-
- public IndentedStringBuilder(String indentFill) {
- this(indentFill.toCharArray());
- }
-
- public IndentedStringBuilder(char indentChar, int length) {
- this(StringUtil.sequence(indentChar, length));
- }
-
- public IndentedStringBuilder() {
- this(new char[] {' '});
- }
-
- @Override
- public String toString() {
- return sb.toString();
- }
-
- public int getIndentLevel() {
- return indentLevel;
- }
-
- public void setIndentLevel(int level) {
- this.indentLevel = level;
- updateIndent();
- }
-
- public char[] getIndentFill() {
- return indentFill;
- }
-
- protected void updateIndent() {
- if (indentLevel < indentCache.length) {
- indent = indentCache[indentLevel];
- if (indent != null) return;
- }
-
- char[] fill = getIndentFill();
- char[] array = new char[fill.length * getIndentLevel()];
- for (int i = 0; i < array.length; i += fill.length)
- System.arraycopy(fill, 0, array, i, fill.length);
- indent = new String(array);
-
- if (indentLevel < indentCache.length) {
- indentCache[indentLevel] = indent;
- }
- }
-
- public IndentedStringBuilder indent() {
- setIndentLevel(getIndentLevel() + 1);
- return this;
- }
-
- public IndentedStringBuilder unindent() {
- setIndentLevel(getIndentLevel() - 1);
- return this;
- }
-
- public IndentedStringBuilder append(Object x) {
- if (x == null) {
- appendRaw("null");
- return this;
- }
-
- String str = x.toString();
- int newLines = StringUtil.count(str, '\n');
-
- if (newLines == 0) {
- appendRaw(str);
- return this;
- }
-
- String[] lines = StringUtil.split(str, '\n', newLines + 1);
- appendRaw(lines[0]);
-
- for (int i = 1; i < lines.length; ++i) {
- newLine();
- appendRaw(lines[i]);
- }
-
- return this;
- }
-
- public IndentedStringBuilder appendRaw(String str) {
- if (str.isEmpty()) return this; // Do not append indent
-
- if (!indentApplied) {
- sb.append(indent);
- indentApplied = true;
- }
-
- sb.append(str);
- return this;
- }
-
- public IndentedStringBuilder newLine() {
- sb.append('\n');
- indentApplied = false;
- return this;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+public class IndentedStringBuilder {
+
+ private final StringBuilder sb = new StringBuilder();
+
+ private int indentLevel = 0;
+ private boolean indentApplied = false;
+
+ private String[] indentCache = new String[16];
+ private String indent = "";
+ private final char[] indentFill;
+
+ public IndentedStringBuilder(char[] indentFill) {
+ this.indentFill = indentFill;
+ }
+
+ public IndentedStringBuilder(String indentFill) {
+ this(indentFill.toCharArray());
+ }
+
+ public IndentedStringBuilder(char indentChar, int length) {
+ this(StringUtil.sequence(indentChar, length));
+ }
+
+ public IndentedStringBuilder() {
+ this(new char[] { ' ' });
+ }
+
+ @Override
+ public String toString() {
+ return sb.toString();
+ }
+
+ public int getIndentLevel() {
+ return indentLevel;
+ }
+
+ public void setIndentLevel(int level) {
+ this.indentLevel = level;
+ updateIndent();
+ }
+
+ public char[] getIndentFill() {
+ return indentFill;
+ }
+
+ protected void updateIndent() {
+ if (indentLevel < indentCache.length) {
+ indent = indentCache[indentLevel];
+ if (indent != null)
+ return;
+ }
+
+ char[] fill = getIndentFill();
+ char[] array = new char[fill.length * getIndentLevel()];
+ for (int i = 0; i < array.length; i += fill.length)
+ System.arraycopy(fill, 0, array, i, fill.length);
+ indent = new String(array);
+
+ if (indentLevel < indentCache.length) {
+ indentCache[indentLevel] = indent;
+ }
+ }
+
+ public IndentedStringBuilder indent() {
+ setIndentLevel(getIndentLevel() + 1);
+ return this;
+ }
+
+ public IndentedStringBuilder unindent() {
+ setIndentLevel(getIndentLevel() - 1);
+ return this;
+ }
+
+ public IndentedStringBuilder append(Object x) {
+ if (x == null) {
+ appendRaw("null");
+ return this;
+ }
+
+ String str = x.toString();
+ int newLines = StringUtil.count(str, '\n');
+
+ if (newLines == 0) {
+ appendRaw(str);
+ return this;
+ }
+
+ String[] lines = StringUtil.split(str, '\n', newLines + 1);
+ appendRaw(lines[0]);
+
+ for (int i = 1; i < lines.length; ++i) {
+ newLine();
+ appendRaw(lines[i]);
+ }
+
+ return this;
+ }
+
+ public IndentedStringBuilder appendRaw(String str) {
+ if (str.isEmpty())
+ return this; // Do not append indent
+
+ if (!indentApplied) {
+ sb.append(indent);
+ indentApplied = true;
+ }
+
+ sb.append(str);
+ return this;
+ }
+
+ public IndentedStringBuilder newLine() {
+ sb.append('\n');
+ indentApplied = false;
+ return this;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java
index 0aaf7a6..f08018a 100644
--- a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java
+++ b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java
@@ -1,940 +1,1032 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Objects;
-import java.util.function.IntFunction;
-
-import ru.windcorp.jputil.ArrayUtil;
-
-public class StringUtil {
-
- private StringUtil() {}
-
- private static final String NULL_PLACEHOLDER = "[null]";
- private static final String EMPTY_PLACEHOLDER = "[empty]";
- private static final String DEFAULT_SEPARATOR = "; ";
-
- public static String arrayToString(
- T[] array,
- String separator,
- String empty,
- String nullPlaceholder,
- String nullArray
- ) {
-
- if (separator == null) {
- throw new IllegalArgumentException(new NullPointerException());
- }
-
- if (array == null) {
- return nullArray;
- }
-
- if (array.length == 0) {
- return empty;
- }
-
- StringBuilder sb = new StringBuilder(array[0] == null ? nullPlaceholder : array[0].toString());
-
- for (int i = 1; i < array.length; ++i) {
- sb.append(separator);
- sb.append(array[i] == null ? nullPlaceholder : array[i].toString());
- }
-
- return sb.toString();
- }
-
- public static String arrayToString(T[] array, String separator) {
- return arrayToString(array, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null array]");
- }
-
- public static String arrayToString(T[] array) {
- return arrayToString(array, DEFAULT_SEPARATOR);
- }
-
- public static String iteratorToString(
- Iterator> iterator,
- String separator,
- String empty,
- String nullPlaceholder,
- String nullIterator
- ) {
-
- if (separator == null) {
- throw new IllegalArgumentException(new NullPointerException());
- }
-
- if (iterator == null) {
- return nullIterator;
- }
-
- if (!iterator.hasNext()) {
- return empty;
- }
-
- Object obj = iterator.next();
- StringBuilder sb = new StringBuilder(obj == null ? nullPlaceholder : obj.toString());
-
- while (iterator.hasNext()) {
- obj = iterator.next();
- sb.append(separator);
- sb.append(obj == null ? nullPlaceholder : obj.toString());
- }
-
- return sb.toString();
- }
-
- public static String iteratorToString(Iterator> iterator, String separator) {
- return iteratorToString(iterator, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null iterator]");
- }
-
- public static String iteratorToString(Iterator> iterator) {
- return iteratorToString(iterator, DEFAULT_SEPARATOR);
- }
-
- public static String iterableToString(
- Iterable> iterable,
- String separator,
- String empty,
- String nullPlaceholder,
- String nullIterable
- ) {
-
- if (separator == null) {
- throw new IllegalArgumentException(new NullPointerException());
- }
-
- if (iterable == null) {
- return nullIterable;
- }
-
- return iteratorToString(iterable.iterator(), separator, empty, nullPlaceholder, nullIterable);
- }
-
- public static String iterableToString(Iterable> iterable, String separator) {
- return iterableToString(iterable, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null iterable]");
- }
-
- public static String iterableToString(Iterable> iterable) {
- return iterableToString(iterable, DEFAULT_SEPARATOR);
- }
-
- public static String supplierToString(
- IntFunction supplier,
- int length,
- String separator,
- String empty,
- String nullPlaceholder,
- String nullSupplier
- ) {
-
- if (separator == null) throw new IllegalArgumentException(new NullPointerException());
- if (supplier == null) return nullSupplier;
- if (length == 0) return empty;
-
- if (length > 0) {
- return supplierToStringExactly(
- supplier,
- length,
- separator,
- nullPlaceholder
- );
- } else {
- return supplierToStringUntilNull(
- supplier,
- separator,
- empty
- );
- }
-
- }
-
- private static String supplierToStringExactly(
- IntFunction supplier,
- int length,
- String separator,
- String nullPlaceholder
- ) {
- T element = supplier.apply(0);
-
- StringBuilder sb = new StringBuilder(element == null ? nullPlaceholder : element.toString());
-
- for (int i = 1; i < length; ++i) {
- sb.append(separator);
- element = supplier.apply(i);
- sb.append(element == null ? nullPlaceholder : element.toString());
- }
-
- return sb.toString();
- }
-
- private static String supplierToStringUntilNull(
- IntFunction supplier,
- String separator,
- String empty
- ) {
- T element = supplier.apply(0);
-
- if (element == null) {
- return empty;
- }
-
- StringBuilder sb = new StringBuilder(element.toString());
-
- int i = 0;
- while ((element = supplier.apply(i++)) != null) {
- sb.append(separator);
- sb.append(element);
- }
-
- return sb.toString();
- }
-
- public static String supplierToString(IntFunction> supplier, int length, String separator) {
- return supplierToString(supplier, length, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null supplier]");
- }
-
- public static String supplierToString(IntFunction> supplier, String separator) {
- return supplierToString(supplier, -1, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null supplier]");
- }
-
- public static String supplierToString(IntFunction> supplier, int length) {
- return supplierToString(supplier, length, DEFAULT_SEPARATOR);
- }
-
- public static String supplierToString(IntFunction> supplier) {
- return supplierToString(supplier, -1, DEFAULT_SEPARATOR);
- }
-
- public static byte[] toJavaByteArray(String str) {
- char[] chars = str.toCharArray();
- byte[] bytes = new byte[chars.length];
-
- for (int i = 0; i < bytes.length; ++i) {
- bytes[i] = (byte) chars[i];
- }
-
- return bytes;
- }
-
- public static int count(String src, char target) {
- int i = 0;
- for (char c : src.toCharArray()) {
- if (c == target) {
- ++i;
- }
- }
-
- return i;
- }
-
- public static String[] split(String src, char separator) {
- return split(src, separator, count(src, separator) + 1);
- }
-
- public static String[] split(String src, char separator, int arrayLength) {
- if (arrayLength < 0) throw illegalArrayLength(arrayLength);
- else if (arrayLength == 0) return new String[0];
- else if (arrayLength == 1) return new String[] { src };
-
- String[] result = new String[arrayLength];
-
- int resultIndex = 0;
- StringBuilder sb = new StringBuilder();
- for (char c : src.toCharArray()) {
- if (c == separator && (resultIndex + 1) < arrayLength) {
- result[resultIndex] = resetStringBuilder(sb);
- ++resultIndex;
- } else {
- sb.append(c);
- }
- }
-
- result[resultIndex] = sb.toString();
-
- return result;
- }
-
- public static int count(String src, char... target) {
- int i = 0;
- for (char c : src.toCharArray()) {
- for (char t : target) {
- if (c == t) {
- ++i;
- break;
- }
- }
- }
-
- return i;
- }
-
- public static String[] split(String src, char... separator) {
- return split(src, count(src, separator) + 1, separator);
- }
-
- public static String[] split(String src, int arrayLength, char... separator) {
- if (arrayLength < 0) throw illegalArrayLength(arrayLength);
- else if (arrayLength == 0) return new String[0];
- else if (arrayLength == 1) return new String[] { src };
-
- String[] result = new String[arrayLength];
-
- int resultIndex = 0;
- StringBuilder sb = new StringBuilder();
-
- charLoop:
- for (char c : src.toCharArray()) {
- if ((resultIndex + 1) < arrayLength) {
- for (char h : separator) {
- if (c == h) {
- result[resultIndex] = resetStringBuilder(sb);
- ++resultIndex;
- continue charLoop;
- }
- }
- }
-
- sb.append(c);
- }
-
- result[resultIndex] = sb.toString();
-
- return result;
- }
-
- public static int count(String src, CharPredicate test) {
- int i = 0;
- for (char c : src.toCharArray()) {
- if (test.test(c)) i++;
- }
-
- return i;
- }
-
- public static String[] split(String src, CharPredicate test) {
- return split(src, count(src, test) + 1, test);
- }
-
- public static String[] split(String src, int arrayLength, CharPredicate test) {
- if (arrayLength < 0) throw illegalArrayLength(arrayLength);
- else if (arrayLength == 0) return new String[0];
- else if (arrayLength == 1) return new String[] { src };
-
- String[] result = new String[arrayLength];
-
- int resultIndex = 0;
- StringBuilder sb = new StringBuilder();
-
- charLoop:
- for (char c : src.toCharArray()) {
- if (
- (resultIndex + 1) < arrayLength
- &&
- test.test(c)
- ) {
- result[resultIndex] = resetStringBuilder(sb);
- ++resultIndex;
- continue charLoop;
- }
-
- sb.append(c);
- }
-
- result[resultIndex] = sb.toString();
-
- return result;
- }
-
- /**
- * Splits {@code src} at index {@code at} discarding the character at that index.
- *
- * Indices {@code 0} and {@code src.length() - 1} produce {@code str} excluding
- * the specified character and {@code ""}.
- *
- * @param src the String to split
- * @param at index to split at
- * @throws IllegalArgumentException if the index is out of bounds for {@code src}
- * @return an array containing the substrings, in order of encounter in {@code src}.
- * Its length is always 2.
- */
- public static String[] splitAt(String src, int at) {
- Objects.requireNonNull(src, "src");
-
- if (at < 0) {
- throw new StringIndexOutOfBoundsException(at);
- } else if (at >= src.length()) {
- throw new StringIndexOutOfBoundsException(at);
- }
-
- if (at == 0) {
- return new String[] {"", src.substring(1)};
- } else if (at == src.length()) {
- return new String[] {src.substring(0, src.length() - 1), ""};
- }
-
- return new String[] {
- src.substring(0, at),
- src.substring(at + 1)
- };
- }
-
- /**
- * Splits {@code src} at indices {@code at} discarding characters at those indices.
- *
- * Indices {@code 0} and {@code src.length() - 1} produce extra zero-length outputs.
- * Duplicate indices produce extra zero-length outputs.
- *
- * Examples:
- *
- * splitAt("a.b.c", new int[] {1, 3}) -> {"a", "b", "c"}
- * splitAt("a..b", new int[] {1, 2}) -> {"a", "", "b"}
- * splitAt(".b.", new int[] {0, 2}) -> {"", "b", ""}
- * splitAt("a.b", new int[] {1, 1, 1}) -> {"a", "", "", "b"}
- *
- * @param src the String to split
- * @param at indices to split at, in any order
- * @throws IllegalArgumentException if some index is out of bounds for {@code src}
- * @return an array containing the substrings, in order of encounter in {@code src}.
- * Its length is always {@code at.length + 1}.
- */
- public static String[] splitAt(String src, int... at) {
- Objects.requireNonNull(src, "src");
- Objects.requireNonNull(at, "at");
-
- if (at.length == 0) return new String[] {src};
- if (at.length == 1) return splitAt(src, at[0]);
-
- int[] indices; // Always sorted
-
- if (ArrayUtil.isSorted(at, true)) {
- indices = at;
- } else {
- indices = at.clone();
- Arrays.sort(indices);
- }
-
- if (indices[0] < 0) {
- throw new StringIndexOutOfBoundsException(indices[0]);
- } else if (indices[indices.length - 1] >= src.length()) {
- throw new StringIndexOutOfBoundsException(indices[indices.length - 1]);
- }
-
- String[] result = new String[at.length + 1];
-
- int start = 0;
- int resultIndex = 0;
- for (int index : indices) {
- int end = index;
-
- String substring;
-
- if (end <= start) {
- // Duplicate or successive index
- substring = "";
- } else {
- substring = src.substring(start, end);
- }
-
- result[resultIndex] = substring;
- resultIndex++;
- start = end + 1;
- }
-
- result[resultIndex] = src.substring(start);
-
- return result;
- }
-
- private static IllegalArgumentException illegalArrayLength(int length) {
- return new IllegalArgumentException("arrayLength must be non-negative (" + length + ")");
- }
-
- public static String remove(String src, char... remove) {
- char[] result = new char[src.length() - count(src, remove)];
-
- char current;
- int resultIndex = 0;
-
- mainLoop:
- for (int srcIndex = 0; srcIndex < src.length(); ++srcIndex) {
- current = src.charAt(srcIndex);
-
- for (char c : remove) {
- if (current == c) {
- continue mainLoop;
- }
- }
-
- result[resultIndex++] = current;
- }
-
- return new String(result);
- }
-
- public static String resetStringBuilder(StringBuilder sb) {
- String result = sb.toString();
- sb.setLength(0);
- sb.ensureCapacity(10);
- return result;
- }
-
- public static String readToString(InputStream is, Charset encoding, int bufferSize) throws IOException {
- char[] buffer = new char[bufferSize];
- StringBuilder result = new StringBuilder();
-
- Reader reader = new InputStreamReader(is, encoding);
- while (true) {
- int readChars = reader.read(buffer, 0, buffer.length);
-
- if (readChars == -1) {
- break;
- }
-
- result.append(buffer, 0, readChars);
- }
-
- return result.toString();
- }
-
- public static boolean equalsPart(char[] a, char[] b, int beginPos, int endPos) {
- if (beginPos < 0) {
- throw new IllegalArgumentException("beginPos must be non-negative (" + beginPos + ")");
- }
-
- if (endPos < beginPos) {
- throw new IllegalArgumentException("endPos must be greater than or equal to beginPos (endPos="
- + endPos + ", beginPos=" + beginPos + ")");
- }
-
- if (endPos >= Math.min(a.length, b.length)) {
- return false; // At least one of the arrays does not contain at least one of the required elements
- }
-
- for (int i = beginPos; i < endPos; ++i) {
- if (a[i] != b[i]) {
- return false;
- }
- }
-
- return true;
- }
-
- // Java 8 is for pussies
- public static char[] join(char[]... srcs) {
- int tmp = 0;
- for (int i = 0; i < srcs.length; ++i) {
- tmp += srcs[i].length;
- }
-
- char[] result = new char[tmp];
- tmp = 0;
- for (int i = 0; i < srcs.length; ++i) {
- System.arraycopy(srcs[i], 0, result, tmp, srcs[i].length);
- tmp += srcs[i].length;
- }
-
- return result;
- }
-
- /**
- * Finds and returns the index of the specified appearance of the specified character
- * in the given array. The search starts at index 0.
- * Examples:
- *
- *
- * src |
- * target |
- * skip |
- * returns |
- *
- * a.b.c | '.' | 0 | 1 |
- * a.b.c | '.' | 1 | 3 |
- * a.b.c | '.' | 2 | -1 |
- * a.b.c | 'd' | any | -1 |
- *
- * @param src - the array to search in.
- * @param target - the character to search for.
- * @param skip - the amount of target
characters to be skipped.
- * @return The index of the skip+1
th target
character or -1, if none found.
- * @see StringUtil#indexFromEnd(char[], char, int)
- */
- public static int indexFromBeginning(char[] src, char target, int skip) {
- for (int i = 0; i < src.length; ++i) {
- if (src[i] == target) {
- if (skip == 0) {
- return i;
- }
-
- --skip;
- }
- }
- return -1;
- }
-
- /**
- * Finds and returns the index of the specified appearance of the specified character
- * in the given array. The search starts at index src.length - 1
.
- * Examples:
- *
- *
- * src |
- * target |
- * skip |
- * returns |
- *
- * a.b.c | '.' | 0 | 3 |
- * a.b.c | '.' | 1 | 1 |
- * a.b.c | '.' | 2 | -1 |
- * a.b.c | 'd' | any | -1 |
- *
- * @param src - the array to search in.
- * @param target - the character to search for.
- * @param skip - the amount of target
characters to be skipped.
- * @return The index of the skip+1
th target
character
- * from the end of the array or -1, if none found.
- * @see StringUtil#indexFromBeginning(char[], char, int)
- */
- public static int indexFromEnd(char[] src, char target, int skip) {
- for (int i = src.length - 1; i >= 0; --i) {
- if (src[i] == target) {
- if (skip == 0) {
- return i;
- }
-
- --skip;
- }
- }
-
- return -1;
- }
-
- public static String padToLeft(String src, int length, char c) {
- if (length <= 0) {
- throw new IllegalArgumentException("length must be positive (" + length + ")");
- }
-
- if (length <= src.length()) {
- return src;
- }
-
- char[] result = new char[length];
-
- int i = 0;
- for (; i < src.length(); ++i) {
- result[i] = src.charAt(i);
- }
-
- for (; i < length; ++i) {
- result[i] = c;
- }
-
- return new String(result);
- }
-
- public static String padToLeft(String src, int length) {
- return padToLeft(src, length, ' ');
- }
-
- public static String padToRight(String src, int length, char c) {
- if (length <= 0) {
- throw new IllegalArgumentException("length must be positive (" + length + ")");
- }
-
- if (length <= src.length()) {
- return src;
- }
-
- char[] result = new char[length];
-
- int i = 0;
- int srcLength = src.length();
-
- for (; i < length - srcLength; ++i) {
- result[i] = c;
- }
-
- for (; i < length; ++i) {
- result[i] = src.charAt(i - (length - srcLength));
- }
-
- return new String(result);
- }
-
- public static String padToRight(String src, int length) {
- return padToRight(src, length, ' ');
- }
-
- public static String center(String src, int length) {
- return center(src, length, ' ');
- }
-
- public static String center(String src, int length, char filler) {
- if (length <= 0) {
- throw new IllegalArgumentException("length must be positive (" + length + ")");
- }
-
- if (src == null || length <= src.length()) {
- return src;
- }
-
- char[] result = new char[length];
-
- int leftPaddingLength = (length - src.length()) / 2;
-
- Arrays.fill(result, 0, leftPaddingLength, filler);
-
- for (int i = 0; i < src.length(); ++i) {
- result[i + leftPaddingLength] = src.charAt(i);
- }
-
- Arrays.fill(result, leftPaddingLength + src.length(), result.length, filler);
-
- return new String(result);
- }
-
- public static int countWords(String src) {
- int i = 0;
- boolean isWord = false;
-
- for (char c : src.toCharArray()) {
- if (Character.isWhitespace(c)) {
- if (isWord) {
- isWord = false;
- i++;
- }
- } else {
- isWord = true;
- }
- }
-
- if (isWord) {
- i++;
- }
-
- return i;
- }
-
- public static String[] splitWords(String src) {
- String[] result = new String[countWords(src)];
-
- int i = 0;
- StringBuilder sb = new StringBuilder();
- for (char c : src.toCharArray()) {
- if (Character.isWhitespace(c)) {
- if (sb.length() != 0) {
- result[i++] = resetStringBuilder(sb);
- }
- } else {
- sb.append(c);
- }
- }
-
- if (sb.length() != 0) {
- result[i] = resetStringBuilder(sb);
- }
-
- return result;
- }
-
- public static char[] sequence(char c, int length) {
- char[] result = new char[length];
- Arrays.fill(result, c);
- return result;
- }
-
- public static String stripPrefix(String string, String prefix) {
- if (prefix != null && string.startsWith(prefix)) {
- return string.substring(prefix.length());
- }
-
- return string;
- }
-
- public static String stripSuffix(String string, String suffix) {
- if (suffix != null && string.endsWith(suffix)) {
- return string.substring(suffix.length());
- }
-
- return string;
- }
-
- @SafeVarargs
- public static Collection allCombinations(Iterable... parts) {
- StringBuilder sb = new StringBuilder();
- Collection result = new ArrayList<>();
- buildCombinations(sb, result, parts, 0);
- return result;
- }
-
- private static void buildCombinations(StringBuilder sb, Collection result, Iterable[] parts,
- int index) {
- if (index >= parts.length) {
- result.add(sb.toString());
- } else {
- int startLength = sb.length();
- for (String part : parts[index]) {
- sb.append(part);
- buildCombinations(sb, result, parts, index + 1);
- sb.setLength(startLength);
- }
- }
- }
-
- @SafeVarargs
- public static String[] allCombinations(String[]... parts) {
- StringBuilder sb = new StringBuilder();
-
- int length = 1;
- for (String[] array : parts) length *= array.length;
- String[] result = new String[length];
-
- buildCombinations(sb, result, new int[] {0}, parts, 0);
- return result;
- }
-
- private static void buildCombinations(StringBuilder sb, String[] result, int[] resultIndex, String[][] parts,
- int index) {
- if (index >= parts.length) {
- result[resultIndex[0]++] = sb.toString();
- } else {
- int startLength = sb.length();
- for (String part : parts[index]) {
- sb.append(part);
- buildCombinations(sb, result, resultIndex, parts, index + 1);
- sb.setLength(startLength);
- }
- }
- }
-
- public static String toUnsignedHexString(byte b) {
- int unsigned = b;
- if (b < 0) {
- unsigned += 0x100;
- }
-
- char[] chars = new char[2];
-
- chars[0] = Character.forDigit(unsigned >>> 4, 0x10);
- chars[1] = Character.forDigit(unsigned & 0x0F, 0x10);
-
- return new String(chars);
- }
-
- public static String toUnsignedHexString(byte[] bytes, String separator, int size) {
- StringBuilder sb = new StringBuilder();
-
- for (int i = 0; i < bytes.length; ++i) {
- sb.append(toUnsignedHexString(bytes[i]));
- if (i < bytes.length - 1 && ((i + 1) % size == 0)) {
- sb.append(separator);
- }
- }
-
- return sb.toString();
- }
-
- public static String toUnsignedHexString(byte[] bytes) {
- return toUnsignedHexString(bytes, ", ", 1);
- }
-
- public static char[] toFullHex(byte x) {
- return toFullHex(x, Byte.BYTES);
- }
-
- public static char[] toFullHex(short x) {
- return toFullHex(x, Short.BYTES);
- }
-
- public static char[] toFullHex(int x) {
- return toFullHex(x, Integer.BYTES);
- }
-
- public static char[] toFullHex(long x) {
- return toFullHex(x, Long.BYTES);
- }
-
- private static char[] toFullHex(long x, int bytes) {
- final int digits = bytes * 2;
-
- char[] result = new char[digits + 2];
- result[0] = '0';
- result[1] = 'x';
-
- for (int digit = 0; digit < digits; ++digit) {
- result[(digits - digit - 1) + 2] =
- hexDigit(x, digit);
- }
-
- return result;
- }
-
- private static char hexDigit(long value, int digit) {
- return hexDigit(
- (int) (value >>> (4 * digit))
- & 0xF
- );
- }
-
- public static char hexDigit(int value) {
- if (value < 0xA) return (char) ('0' + value);
- else return (char) ('A' - 0xA + value);
- }
-
- public static String replaceAll(String source, String substring, String replacement) {
- Objects.requireNonNull(source, "source");
- Objects.requireNonNull(substring, "substring");
-
- if (substring.isEmpty()) {
- throw new IllegalArgumentException("substring is empty");
- }
-
- if (!source.contains(substring)) { // also passes if source is empty
- return source;
- }
-
- if (substring.equals(replacement)) { // null-safe
- return source;
- }
-
- StringBuilder sb = new StringBuilder(2 * source.length());
-
- for (int i = 0; i < source.length() - substring.length() + 1; ++i) {
- if (source.startsWith(substring, i)) {
- if (replacement != null) {
- sb.append(replacement);
- }
- } else {
- sb.append(source.charAt(i));
- }
- }
-
- return sb.toString();
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.function.IntFunction;
+
+import ru.windcorp.jputil.ArrayUtil;
+
+public class StringUtil {
+
+ private StringUtil() {
+ }
+
+ private static final String NULL_PLACEHOLDER = "[null]";
+ private static final String EMPTY_PLACEHOLDER = "[empty]";
+ private static final String DEFAULT_SEPARATOR = "; ";
+
+ public static String arrayToString(
+ T[] array,
+ String separator,
+ String empty,
+ String nullPlaceholder,
+ String nullArray
+ ) {
+
+ if (separator == null) {
+ throw new IllegalArgumentException(new NullPointerException());
+ }
+
+ if (array == null) {
+ return nullArray;
+ }
+
+ if (array.length == 0) {
+ return empty;
+ }
+
+ StringBuilder sb = new StringBuilder(array[0] == null ? nullPlaceholder : array[0].toString());
+
+ for (int i = 1; i < array.length; ++i) {
+ sb.append(separator);
+ sb.append(array[i] == null ? nullPlaceholder : array[i].toString());
+ }
+
+ return sb.toString();
+ }
+
+ public static String arrayToString(T[] array, String separator) {
+ return arrayToString(array, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null array]");
+ }
+
+ public static String arrayToString(T[] array) {
+ return arrayToString(array, DEFAULT_SEPARATOR);
+ }
+
+ public static String iteratorToString(
+ Iterator> iterator,
+ String separator,
+ String empty,
+ String nullPlaceholder,
+ String nullIterator
+ ) {
+
+ if (separator == null) {
+ throw new IllegalArgumentException(new NullPointerException());
+ }
+
+ if (iterator == null) {
+ return nullIterator;
+ }
+
+ if (!iterator.hasNext()) {
+ return empty;
+ }
+
+ Object obj = iterator.next();
+ StringBuilder sb = new StringBuilder(obj == null ? nullPlaceholder : obj.toString());
+
+ while (iterator.hasNext()) {
+ obj = iterator.next();
+ sb.append(separator);
+ sb.append(obj == null ? nullPlaceholder : obj.toString());
+ }
+
+ return sb.toString();
+ }
+
+ public static String iteratorToString(Iterator> iterator, String separator) {
+ return iteratorToString(iterator, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null iterator]");
+ }
+
+ public static String iteratorToString(Iterator> iterator) {
+ return iteratorToString(iterator, DEFAULT_SEPARATOR);
+ }
+
+ public static String iterableToString(
+ Iterable> iterable,
+ String separator,
+ String empty,
+ String nullPlaceholder,
+ String nullIterable
+ ) {
+
+ if (separator == null) {
+ throw new IllegalArgumentException(new NullPointerException());
+ }
+
+ if (iterable == null) {
+ return nullIterable;
+ }
+
+ return iteratorToString(iterable.iterator(), separator, empty, nullPlaceholder, nullIterable);
+ }
+
+ public static String iterableToString(Iterable> iterable, String separator) {
+ return iterableToString(iterable, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null iterable]");
+ }
+
+ public static String iterableToString(Iterable> iterable) {
+ return iterableToString(iterable, DEFAULT_SEPARATOR);
+ }
+
+ public static String supplierToString(
+ IntFunction supplier,
+ int length,
+ String separator,
+ String empty,
+ String nullPlaceholder,
+ String nullSupplier
+ ) {
+
+ if (separator == null)
+ throw new IllegalArgumentException(new NullPointerException());
+ if (supplier == null)
+ return nullSupplier;
+ if (length == 0)
+ return empty;
+
+ if (length > 0) {
+ return supplierToStringExactly(
+ supplier,
+ length,
+ separator,
+ nullPlaceholder
+ );
+ } else {
+ return supplierToStringUntilNull(
+ supplier,
+ separator,
+ empty
+ );
+ }
+
+ }
+
+ private static String supplierToStringExactly(
+ IntFunction supplier,
+ int length,
+ String separator,
+ String nullPlaceholder
+ ) {
+ T element = supplier.apply(0);
+
+ StringBuilder sb = new StringBuilder(element == null ? nullPlaceholder : element.toString());
+
+ for (int i = 1; i < length; ++i) {
+ sb.append(separator);
+ element = supplier.apply(i);
+ sb.append(element == null ? nullPlaceholder : element.toString());
+ }
+
+ return sb.toString();
+ }
+
+ private static String supplierToStringUntilNull(
+ IntFunction supplier,
+ String separator,
+ String empty
+ ) {
+ T element = supplier.apply(0);
+
+ if (element == null) {
+ return empty;
+ }
+
+ StringBuilder sb = new StringBuilder(element.toString());
+
+ int i = 0;
+ while ((element = supplier.apply(i++)) != null) {
+ sb.append(separator);
+ sb.append(element);
+ }
+
+ return sb.toString();
+ }
+
+ public static String supplierToString(IntFunction> supplier, int length, String separator) {
+ return supplierToString(supplier, length, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null supplier]");
+ }
+
+ public static String supplierToString(IntFunction> supplier, String separator) {
+ return supplierToString(supplier, -1, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null supplier]");
+ }
+
+ public static String supplierToString(IntFunction> supplier, int length) {
+ return supplierToString(supplier, length, DEFAULT_SEPARATOR);
+ }
+
+ public static String supplierToString(IntFunction> supplier) {
+ return supplierToString(supplier, -1, DEFAULT_SEPARATOR);
+ }
+
+ public static byte[] toJavaByteArray(String str) {
+ char[] chars = str.toCharArray();
+ byte[] bytes = new byte[chars.length];
+
+ for (int i = 0; i < bytes.length; ++i) {
+ bytes[i] = (byte) chars[i];
+ }
+
+ return bytes;
+ }
+
+ public static int count(String src, char target) {
+ int i = 0;
+ for (char c : src.toCharArray()) {
+ if (c == target) {
+ ++i;
+ }
+ }
+
+ return i;
+ }
+
+ public static String[] split(String src, char separator) {
+ return split(src, separator, count(src, separator) + 1);
+ }
+
+ public static String[] split(String src, char separator, int arrayLength) {
+ if (arrayLength < 0)
+ throw illegalArrayLength(arrayLength);
+ else if (arrayLength == 0)
+ return new String[0];
+ else if (arrayLength == 1)
+ return new String[] { src };
+
+ String[] result = new String[arrayLength];
+
+ int resultIndex = 0;
+ StringBuilder sb = new StringBuilder();
+ for (char c : src.toCharArray()) {
+ if (c == separator && (resultIndex + 1) < arrayLength) {
+ result[resultIndex] = resetStringBuilder(sb);
+ ++resultIndex;
+ } else {
+ sb.append(c);
+ }
+ }
+
+ result[resultIndex] = sb.toString();
+
+ return result;
+ }
+
+ public static int count(String src, char... target) {
+ int i = 0;
+ for (char c : src.toCharArray()) {
+ for (char t : target) {
+ if (c == t) {
+ ++i;
+ break;
+ }
+ }
+ }
+
+ return i;
+ }
+
+ public static String[] split(String src, char... separator) {
+ return split(src, count(src, separator) + 1, separator);
+ }
+
+ public static String[] split(String src, int arrayLength, char... separator) {
+ if (arrayLength < 0)
+ throw illegalArrayLength(arrayLength);
+ else if (arrayLength == 0)
+ return new String[0];
+ else if (arrayLength == 1)
+ return new String[] { src };
+
+ String[] result = new String[arrayLength];
+
+ int resultIndex = 0;
+ StringBuilder sb = new StringBuilder();
+
+ charLoop: for (char c : src.toCharArray()) {
+ if ((resultIndex + 1) < arrayLength) {
+ for (char h : separator) {
+ if (c == h) {
+ result[resultIndex] = resetStringBuilder(sb);
+ ++resultIndex;
+ continue charLoop;
+ }
+ }
+ }
+
+ sb.append(c);
+ }
+
+ result[resultIndex] = sb.toString();
+
+ return result;
+ }
+
+ public static int count(String src, CharPredicate test) {
+ int i = 0;
+ for (char c : src.toCharArray()) {
+ if (test.test(c))
+ i++;
+ }
+
+ return i;
+ }
+
+ public static String[] split(String src, CharPredicate test) {
+ return split(src, count(src, test) + 1, test);
+ }
+
+ public static String[] split(String src, int arrayLength, CharPredicate test) {
+ if (arrayLength < 0)
+ throw illegalArrayLength(arrayLength);
+ else if (arrayLength == 0)
+ return new String[0];
+ else if (arrayLength == 1)
+ return new String[] { src };
+
+ String[] result = new String[arrayLength];
+
+ int resultIndex = 0;
+ StringBuilder sb = new StringBuilder();
+
+ charLoop: for (char c : src.toCharArray()) {
+ if (
+ (resultIndex + 1) < arrayLength
+ &&
+ test.test(c)
+ ) {
+ result[resultIndex] = resetStringBuilder(sb);
+ ++resultIndex;
+ continue charLoop;
+ }
+
+ sb.append(c);
+ }
+
+ result[resultIndex] = sb.toString();
+
+ return result;
+ }
+
+ /**
+ * Splits {@code src} at index {@code at} discarding the character at that
+ * index.
+ *
+ * Indices {@code 0} and {@code src.length() - 1} produce {@code str}
+ * excluding
+ * the specified character and {@code ""}.
+ *
+ *
+ * @param src the String to split
+ * @param at index to split at
+ * @throws IllegalArgumentException if the index is out of bounds for
+ * {@code src}
+ * @return an array containing the substrings, in order of encounter in
+ * {@code src}.
+ * Its length is always 2.
+ */
+ public static String[] splitAt(String src, int at) {
+ Objects.requireNonNull(src, "src");
+
+ if (at < 0) {
+ throw new StringIndexOutOfBoundsException(at);
+ } else if (at >= src.length()) {
+ throw new StringIndexOutOfBoundsException(at);
+ }
+
+ if (at == 0) {
+ return new String[] { "", src.substring(1) };
+ } else if (at == src.length()) {
+ return new String[] { src.substring(0, src.length() - 1), "" };
+ }
+
+ return new String[] {
+ src.substring(0, at),
+ src.substring(at + 1)
+ };
+ }
+
+ /**
+ * Splits {@code src} at indices {@code at} discarding characters at those
+ * indices.
+ *
+ * Indices {@code 0} and {@code src.length() - 1} produce extra zero-length
+ * outputs.
+ * Duplicate indices produce extra zero-length outputs.
+ *
+ * Examples:
+ *
+ *
+ * splitAt("a.b.c", 1, 3) -> {"a", "b", "c"}
+ * splitAt("a..b", 1, 2) -> {"a", "", "b"}
+ * splitAt(".b.", 0, 2) -> {"", "b", ""}
+ * splitAt("a.b", 1, 1, 1) -> {"a", "", "", "b"}
+ *
+ *
+ * @param src the String to split
+ * @param at indices to split at, in any order
+ * @throws IllegalArgumentException if some index is out of bounds for
+ * {@code src}
+ * @return an array containing the substrings, in order of encounter in
+ * {@code src}.
+ * Its length is always {@code at.length + 1}.
+ */
+ public static String[] splitAt(String src, int... at) {
+ Objects.requireNonNull(src, "src");
+ Objects.requireNonNull(at, "at");
+
+ if (at.length == 0)
+ return new String[] { src };
+ if (at.length == 1)
+ return splitAt(src, at[0]);
+
+ int[] indices; // Always sorted
+
+ if (ArrayUtil.isSorted(at, true)) {
+ indices = at;
+ } else {
+ indices = at.clone();
+ Arrays.sort(indices);
+ }
+
+ if (indices[0] < 0) {
+ throw new StringIndexOutOfBoundsException(indices[0]);
+ } else if (indices[indices.length - 1] >= src.length()) {
+ throw new StringIndexOutOfBoundsException(indices[indices.length - 1]);
+ }
+
+ String[] result = new String[at.length + 1];
+
+ int start = 0;
+ int resultIndex = 0;
+ for (int index : indices) {
+ int end = index;
+
+ String substring;
+
+ if (end <= start) {
+ // Duplicate or successive index
+ substring = "";
+ } else {
+ substring = src.substring(start, end);
+ }
+
+ result[resultIndex] = substring;
+ resultIndex++;
+ start = end + 1;
+ }
+
+ result[resultIndex] = src.substring(start);
+
+ return result;
+ }
+
+ private static IllegalArgumentException illegalArrayLength(int length) {
+ return new IllegalArgumentException("arrayLength must be non-negative (" + length + ")");
+ }
+
+ public static String remove(String src, char... remove) {
+ char[] result = new char[src.length() - count(src, remove)];
+
+ char current;
+ int resultIndex = 0;
+
+ mainLoop: for (int srcIndex = 0; srcIndex < src.length(); ++srcIndex) {
+ current = src.charAt(srcIndex);
+
+ for (char c : remove) {
+ if (current == c) {
+ continue mainLoop;
+ }
+ }
+
+ result[resultIndex++] = current;
+ }
+
+ return new String(result);
+ }
+
+ public static String resetStringBuilder(StringBuilder sb) {
+ String result = sb.toString();
+ sb.setLength(0);
+ sb.ensureCapacity(10);
+ return result;
+ }
+
+ public static String readToString(InputStream is, Charset encoding, int bufferSize) throws IOException {
+ char[] buffer = new char[bufferSize];
+ StringBuilder result = new StringBuilder();
+
+ Reader reader = new InputStreamReader(is, encoding);
+ while (true) {
+ int readChars = reader.read(buffer, 0, buffer.length);
+
+ if (readChars == -1) {
+ break;
+ }
+
+ result.append(buffer, 0, readChars);
+ }
+
+ return result.toString();
+ }
+
+ public static boolean equalsPart(char[] a, char[] b, int beginPos, int endPos) {
+ if (beginPos < 0) {
+ throw new IllegalArgumentException("beginPos must be non-negative (" + beginPos + ")");
+ }
+
+ if (endPos < beginPos) {
+ throw new IllegalArgumentException(
+ "endPos must be greater than or equal to beginPos (endPos="
+ + endPos + ", beginPos=" + beginPos + ")"
+ );
+ }
+
+ if (endPos >= Math.min(a.length, b.length)) {
+ return false; // At least one of the arrays does not contain at
+ // least one of the required elements
+ }
+
+ for (int i = beginPos; i < endPos; ++i) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Java 8 is for pussies
+ public static char[] join(char[]... srcs) {
+ int tmp = 0;
+ for (int i = 0; i < srcs.length; ++i) {
+ tmp += srcs[i].length;
+ }
+
+ char[] result = new char[tmp];
+ tmp = 0;
+ for (int i = 0; i < srcs.length; ++i) {
+ System.arraycopy(srcs[i], 0, result, tmp, srcs[i].length);
+ tmp += srcs[i].length;
+ }
+
+ return result;
+ }
+
+ /**
+ * Finds and returns the index of the specified appearance of the specified
+ * character
+ * in the given array. The search starts at index 0.
+ *
+ * Examples:
+ *
+ *
+ *
+ * src |
+ * target |
+ * skip |
+ * returns |
+ *
+ *
+ * a.b.c |
+ * '.' |
+ * 0 |
+ * 1 |
+ *
+ *
+ * a.b.c |
+ * '.' |
+ * 1 |
+ * 3 |
+ *
+ *
+ * a.b.c |
+ * '.' |
+ * 2 |
+ * -1 |
+ *
+ *
+ * a.b.c |
+ * 'd' |
+ * any |
+ * -1 |
+ *
+ *
+ *
+ * @param src - the array to search in.
+ * @param target - the character to search for.
+ * @param skip - the amount of target
characters to be
+ * skipped.
+ * @return The index of the skip+1
th target
+ * character or -1, if none found.
+ * @see StringUtil#indexFromEnd(char[], char, int)
+ */
+ public static int indexFromBeginning(char[] src, char target, int skip) {
+ for (int i = 0; i < src.length; ++i) {
+ if (src[i] == target) {
+ if (skip == 0) {
+ return i;
+ }
+
+ --skip;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Finds and returns the index of the specified appearance of the specified
+ * character
+ * in the given array. The search starts at index
+ * src.length - 1
.
+ *
+ * Examples:
+ *
+ *
+ *
+ * src |
+ * target |
+ * skip |
+ * returns |
+ *
+ *
+ * a.b.c |
+ * '.' |
+ * 0 |
+ * 3 |
+ *
+ *
+ * a.b.c |
+ * '.' |
+ * 1 |
+ * 1 |
+ *
+ *
+ * a.b.c |
+ * '.' |
+ * 2 |
+ * -1 |
+ *
+ *
+ * a.b.c |
+ * 'd' |
+ * any |
+ * -1 |
+ *
+ *
+ *
+ * @param src - the array to search in.
+ * @param target - the character to search for.
+ * @param skip - the amount of target
characters to be
+ * skipped.
+ * @return The index of the skip+1
th
+ * target
character
+ * from the end of the array or -1, if none found.
+ * @see StringUtil#indexFromBeginning(char[], char, int)
+ */
+ public static int indexFromEnd(char[] src, char target, int skip) {
+ for (int i = src.length - 1; i >= 0; --i) {
+ if (src[i] == target) {
+ if (skip == 0) {
+ return i;
+ }
+
+ --skip;
+ }
+ }
+
+ return -1;
+ }
+
+ public static String padToLeft(String src, int length, char c) {
+ if (length <= 0) {
+ throw new IllegalArgumentException("length must be positive (" + length + ")");
+ }
+
+ if (length <= src.length()) {
+ return src;
+ }
+
+ char[] result = new char[length];
+
+ int i = 0;
+ for (; i < src.length(); ++i) {
+ result[i] = src.charAt(i);
+ }
+
+ for (; i < length; ++i) {
+ result[i] = c;
+ }
+
+ return new String(result);
+ }
+
+ public static String padToLeft(String src, int length) {
+ return padToLeft(src, length, ' ');
+ }
+
+ public static String padToRight(String src, int length, char c) {
+ if (length <= 0) {
+ throw new IllegalArgumentException("length must be positive (" + length + ")");
+ }
+
+ if (length <= src.length()) {
+ return src;
+ }
+
+ char[] result = new char[length];
+
+ int i = 0;
+ int srcLength = src.length();
+
+ for (; i < length - srcLength; ++i) {
+ result[i] = c;
+ }
+
+ for (; i < length; ++i) {
+ result[i] = src.charAt(i - (length - srcLength));
+ }
+
+ return new String(result);
+ }
+
+ public static String padToRight(String src, int length) {
+ return padToRight(src, length, ' ');
+ }
+
+ public static String center(String src, int length) {
+ return center(src, length, ' ');
+ }
+
+ public static String center(String src, int length, char filler) {
+ if (length <= 0) {
+ throw new IllegalArgumentException("length must be positive (" + length + ")");
+ }
+
+ if (src == null || length <= src.length()) {
+ return src;
+ }
+
+ char[] result = new char[length];
+
+ int leftPaddingLength = (length - src.length()) / 2;
+
+ Arrays.fill(result, 0, leftPaddingLength, filler);
+
+ for (int i = 0; i < src.length(); ++i) {
+ result[i + leftPaddingLength] = src.charAt(i);
+ }
+
+ Arrays.fill(result, leftPaddingLength + src.length(), result.length, filler);
+
+ return new String(result);
+ }
+
+ public static int countWords(String src) {
+ int i = 0;
+ boolean isWord = false;
+
+ for (char c : src.toCharArray()) {
+ if (Character.isWhitespace(c)) {
+ if (isWord) {
+ isWord = false;
+ i++;
+ }
+ } else {
+ isWord = true;
+ }
+ }
+
+ if (isWord) {
+ i++;
+ }
+
+ return i;
+ }
+
+ public static String[] splitWords(String src) {
+ String[] result = new String[countWords(src)];
+
+ int i = 0;
+ StringBuilder sb = new StringBuilder();
+ for (char c : src.toCharArray()) {
+ if (Character.isWhitespace(c)) {
+ if (sb.length() != 0) {
+ result[i++] = resetStringBuilder(sb);
+ }
+ } else {
+ sb.append(c);
+ }
+ }
+
+ if (sb.length() != 0) {
+ result[i] = resetStringBuilder(sb);
+ }
+
+ return result;
+ }
+
+ public static char[] sequence(char c, int length) {
+ char[] result = new char[length];
+ Arrays.fill(result, c);
+ return result;
+ }
+
+ public static String stripPrefix(String string, String prefix) {
+ if (prefix != null && string.startsWith(prefix)) {
+ return string.substring(prefix.length());
+ }
+
+ return string;
+ }
+
+ public static String stripSuffix(String string, String suffix) {
+ if (suffix != null && string.endsWith(suffix)) {
+ return string.substring(suffix.length());
+ }
+
+ return string;
+ }
+
+ @SafeVarargs
+ public static Collection allCombinations(Iterable... parts) {
+ StringBuilder sb = new StringBuilder();
+ Collection result = new ArrayList<>();
+ buildCombinations(sb, result, parts, 0);
+ return result;
+ }
+
+ private static void buildCombinations(
+ StringBuilder sb,
+ Collection result,
+ Iterable[] parts,
+ int index
+ ) {
+ if (index >= parts.length) {
+ result.add(sb.toString());
+ } else {
+ int startLength = sb.length();
+ for (String part : parts[index]) {
+ sb.append(part);
+ buildCombinations(sb, result, parts, index + 1);
+ sb.setLength(startLength);
+ }
+ }
+ }
+
+ @SafeVarargs
+ public static String[] allCombinations(String[]... parts) {
+ StringBuilder sb = new StringBuilder();
+
+ int length = 1;
+ for (String[] array : parts)
+ length *= array.length;
+ String[] result = new String[length];
+
+ buildCombinations(sb, result, new int[] { 0 }, parts, 0);
+ return result;
+ }
+
+ private static void buildCombinations(
+ StringBuilder sb,
+ String[] result,
+ int[] resultIndex,
+ String[][] parts,
+ int index
+ ) {
+ if (index >= parts.length) {
+ result[resultIndex[0]++] = sb.toString();
+ } else {
+ int startLength = sb.length();
+ for (String part : parts[index]) {
+ sb.append(part);
+ buildCombinations(sb, result, resultIndex, parts, index + 1);
+ sb.setLength(startLength);
+ }
+ }
+ }
+
+ public static String toUnsignedHexString(byte b) {
+ int unsigned = b;
+ if (b < 0) {
+ unsigned += 0x100;
+ }
+
+ char[] chars = new char[2];
+
+ chars[0] = Character.forDigit(unsigned >>> 4, 0x10);
+ chars[1] = Character.forDigit(unsigned & 0x0F, 0x10);
+
+ return new String(chars);
+ }
+
+ public static String toUnsignedHexString(byte[] bytes, String separator, int size) {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < bytes.length; ++i) {
+ sb.append(toUnsignedHexString(bytes[i]));
+ if (i < bytes.length - 1 && ((i + 1) % size == 0)) {
+ sb.append(separator);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public static String toUnsignedHexString(byte[] bytes) {
+ return toUnsignedHexString(bytes, ", ", 1);
+ }
+
+ public static char[] toFullHex(byte x) {
+ return toFullHex(x, Byte.BYTES);
+ }
+
+ public static char[] toFullHex(short x) {
+ return toFullHex(x, Short.BYTES);
+ }
+
+ public static char[] toFullHex(int x) {
+ return toFullHex(x, Integer.BYTES);
+ }
+
+ public static char[] toFullHex(long x) {
+ return toFullHex(x, Long.BYTES);
+ }
+
+ private static char[] toFullHex(long x, int bytes) {
+ final int digits = bytes * 2;
+
+ char[] result = new char[digits + 2];
+ result[0] = '0';
+ result[1] = 'x';
+
+ for (int digit = 0; digit < digits; ++digit) {
+ result[(digits - digit - 1) + 2] = hexDigit(x, digit);
+ }
+
+ return result;
+ }
+
+ private static char hexDigit(long value, int digit) {
+ return hexDigit(
+ (int) (value >>> (4 * digit))
+ & 0xF
+ );
+ }
+
+ public static char hexDigit(int value) {
+ if (value < 0xA)
+ return (char) ('0' + value);
+ else
+ return (char) ('A' - 0xA + value);
+ }
+
+ public static String replaceAll(String source, String substring, String replacement) {
+ Objects.requireNonNull(source, "source");
+ Objects.requireNonNull(substring, "substring");
+
+ if (substring.isEmpty()) {
+ throw new IllegalArgumentException("substring is empty");
+ }
+
+ if (!source.contains(substring)) { // also passes if source is empty
+ return source;
+ }
+
+ if (substring.equals(replacement)) { // null-safe
+ return source;
+ }
+
+ StringBuilder sb = new StringBuilder(2 * source.length());
+
+ for (int i = 0; i < source.length() - substring.length() + 1; ++i) {
+ if (source.startsWith(substring, i)) {
+ if (replacement != null) {
+ sb.append(replacement);
+ }
+ } else {
+ sb.append(source.charAt(i));
+ }
+ }
+
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/UncheckedEscapeException.java b/src/main/java/ru/windcorp/jputil/chars/UncheckedEscapeException.java
index 03c7a5f..33717ee 100644
--- a/src/main/java/ru/windcorp/jputil/chars/UncheckedEscapeException.java
+++ b/src/main/java/ru/windcorp/jputil/chars/UncheckedEscapeException.java
@@ -1,37 +1,38 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-public class UncheckedEscapeException extends RuntimeException {
-
- private static final long serialVersionUID = 5392628641744570926L;
-
- public UncheckedEscapeException(String message, EscapeException cause) {
- super(message, cause);
- }
-
- public UncheckedEscapeException(EscapeException cause) {
- super(cause);
- }
-
- @Override
- public synchronized EscapeException getCause() {
- return (EscapeException) super.getCause();
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+public class UncheckedEscapeException extends RuntimeException {
+
+ private static final long serialVersionUID = 5392628641744570926L;
+
+ public UncheckedEscapeException(String message, EscapeException cause) {
+ super(message, cause);
+ }
+
+ public UncheckedEscapeException(EscapeException cause) {
+ super(cause);
+ }
+
+ @Override
+ public synchronized EscapeException getCause() {
+ return (EscapeException) super.getCause();
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/WordReader.java b/src/main/java/ru/windcorp/jputil/chars/WordReader.java
index 8636734..22d6969 100644
--- a/src/main/java/ru/windcorp/jputil/chars/WordReader.java
+++ b/src/main/java/ru/windcorp/jputil/chars/WordReader.java
@@ -1,139 +1,143 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.CharBuffer;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-public class WordReader implements Iterator {
-
- private final Reader reader;
-
- private char[] wordBuffer = new char[1024];
- private final CharBuffer inputBuffer;
-
- private String next = null;
- private boolean isExhausted = false;
-
- private IOException lastException = null;
-
- public WordReader(Reader src, int bufferSize) {
- this.reader = src;
- this.inputBuffer = CharBuffer.allocate(bufferSize);
- }
-
- public WordReader(Reader src) {
- this(src, 2048);
- }
-
- public WordReader(char[] array, int offset, int length) {
- this.reader = null;
- this.inputBuffer = CharBuffer.wrap(Arrays.copyOfRange(array, offset, length + offset));
- }
-
- public WordReader(char[] array) {
- this.reader = null;
- this.inputBuffer = CharBuffer.wrap(array);
- }
-
- public WordReader(String str) {
- this(str.toCharArray());
- }
-
- @Override
- public String next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
-
- String result = next;
- next = null;
- return result;
- }
-
- @Override
- public boolean hasNext() {
- if (next != null) {
- return true;
- }
-
- if (isExhausted) {
- return false;
- }
-
- int length = 0;
- char c;
- while (true) {
- c = nextChar();
-
- if (isExhausted) break;
-
- if (Character.isWhitespace(c)) {
- if (length == 0) continue;
- else break;
- }
-
- if (wordBuffer.length == length) {
- char[] newBuf = new char[wordBuffer.length * 2];
- System.arraycopy(wordBuffer, 0, newBuf, 0, wordBuffer.length);
- wordBuffer = newBuf;
- }
-
- wordBuffer[length++] = c;
- }
-
- if (length == 0) {
- return false;
- }
-
- next = new String(wordBuffer, 0, length);
- return true;
- }
-
- private char nextChar() {
- if (!inputBuffer.hasRemaining()) {
- if (reader == null) {
- isExhausted = true;
- return 0;
- }
-
- inputBuffer.rewind();
- try {
- if (reader.read(inputBuffer) == -1) {
- isExhausted = true;
- }
- } catch (IOException e) {
- lastException = e;
- isExhausted = true;
- return 0;
- }
-
- }
-
- return inputBuffer.get();
- }
-
- public IOException getLastException() {
- return lastException;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.CharBuffer;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class WordReader implements Iterator {
+
+ private final Reader reader;
+
+ private char[] wordBuffer = new char[1024];
+ private final CharBuffer inputBuffer;
+
+ private String next = null;
+ private boolean isExhausted = false;
+
+ private IOException lastException = null;
+
+ public WordReader(Reader src, int bufferSize) {
+ this.reader = src;
+ this.inputBuffer = CharBuffer.allocate(bufferSize);
+ }
+
+ public WordReader(Reader src) {
+ this(src, 2048);
+ }
+
+ public WordReader(char[] array, int offset, int length) {
+ this.reader = null;
+ this.inputBuffer = CharBuffer.wrap(Arrays.copyOfRange(array, offset, length + offset));
+ }
+
+ public WordReader(char[] array) {
+ this.reader = null;
+ this.inputBuffer = CharBuffer.wrap(array);
+ }
+
+ public WordReader(String str) {
+ this(str.toCharArray());
+ }
+
+ @Override
+ public String next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ String result = next;
+ next = null;
+ return result;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (next != null) {
+ return true;
+ }
+
+ if (isExhausted) {
+ return false;
+ }
+
+ int length = 0;
+ char c;
+ while (true) {
+ c = nextChar();
+
+ if (isExhausted)
+ break;
+
+ if (Character.isWhitespace(c)) {
+ if (length == 0)
+ continue;
+ else
+ break;
+ }
+
+ if (wordBuffer.length == length) {
+ char[] newBuf = new char[wordBuffer.length * 2];
+ System.arraycopy(wordBuffer, 0, newBuf, 0, wordBuffer.length);
+ wordBuffer = newBuf;
+ }
+
+ wordBuffer[length++] = c;
+ }
+
+ if (length == 0) {
+ return false;
+ }
+
+ next = new String(wordBuffer, 0, length);
+ return true;
+ }
+
+ private char nextChar() {
+ if (!inputBuffer.hasRemaining()) {
+ if (reader == null) {
+ isExhausted = true;
+ return 0;
+ }
+
+ inputBuffer.rewind();
+ try {
+ if (reader.read(inputBuffer) == -1) {
+ isExhausted = true;
+ }
+ } catch (IOException e) {
+ lastException = e;
+ isExhausted = true;
+ return 0;
+ }
+
+ }
+
+ return inputBuffer.get();
+ }
+
+ public IOException getLastException() {
+ return lastException;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/reader/AbstractCharReader.java b/src/main/java/ru/windcorp/jputil/chars/reader/AbstractCharReader.java
index 1d48158..d31e092 100644
--- a/src/main/java/ru/windcorp/jputil/chars/reader/AbstractCharReader.java
+++ b/src/main/java/ru/windcorp/jputil/chars/reader/AbstractCharReader.java
@@ -1,104 +1,108 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars.reader;
-
-/**
- * @author Javapony
- *
- */
-public abstract class AbstractCharReader implements CharReader {
-
- protected static final int DEFAULT_MARK_STACK_SIZE = 8;
-
- /**
- * Current position of this CharReader. The reader maps its input to positions starting from 0.
- * Positions that are negative or lower than 0 are invalid. {@link #current()}
- * will throw an exception if position is invalid.
- */
- protected int position = 0;
-
- private int[] marks = new int[DEFAULT_MARK_STACK_SIZE];
- private int nextMark = 0;
-
- protected static int closestGreaterPowerOf2(int x) {
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- return x + 1;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#getPosition()
- */
- @Override
- public int getPosition() {
- return position;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#setPosition(int)
- */
- @Override
- public int setPosition(int position) {
- this.position = position;
- return position;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#mark()
- */
- @Override
- public int mark() {
- ensureMarksCapacity();
- marks[nextMark++] = position;
- return position;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#forget()
- */
- @Override
- public int forget() {
- return marks[--nextMark];
- }
-
- private void ensureMarksCapacity() {
- if (nextMark < marks.length) return;
- int[] newMarks = new int[closestGreaterPowerOf2(nextMark)];
- System.arraycopy(marks, 0, newMarks, 0, nextMark);
- marks = newMarks;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("\"");
-
- mark();
- position = 0;
- sb.append(getChars());
- reset();
-
- sb.append("\"\n ");
- for (int i = 0; i < position; ++i) sb.append(' ');
- sb.append("^ (pos " + position + ")");
- return sb.toString();
- }
-
-}
\ No newline at end of file
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars.reader;
+
+/**
+ * @author Javapony
+ */
+public abstract class AbstractCharReader implements CharReader {
+
+ protected static final int DEFAULT_MARK_STACK_SIZE = 8;
+
+ /**
+ * Current position of this CharReader. The reader maps its input to
+ * positions starting from 0.
+ * Positions that are negative or lower than 0 are invalid.
+ * {@link #current()}
+ * will throw an exception if position is invalid.
+ */
+ protected int position = 0;
+
+ private int[] marks = new int[DEFAULT_MARK_STACK_SIZE];
+ private int nextMark = 0;
+
+ protected static int closestGreaterPowerOf2(int x) {
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return x + 1;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#getPosition()
+ */
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#setPosition(int)
+ */
+ @Override
+ public int setPosition(int position) {
+ this.position = position;
+ return position;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#mark()
+ */
+ @Override
+ public int mark() {
+ ensureMarksCapacity();
+ marks[nextMark++] = position;
+ return position;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#forget()
+ */
+ @Override
+ public int forget() {
+ return marks[--nextMark];
+ }
+
+ private void ensureMarksCapacity() {
+ if (nextMark < marks.length)
+ return;
+ int[] newMarks = new int[closestGreaterPowerOf2(nextMark)];
+ System.arraycopy(marks, 0, newMarks, 0, nextMark);
+ marks = newMarks;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("\"");
+
+ mark();
+ position = 0;
+ sb.append(getChars());
+ reset();
+
+ sb.append("\"\n ");
+ for (int i = 0; i < position; ++i)
+ sb.append(' ');
+ sb.append("^ (pos " + position + ")");
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/reader/ArrayCharReader.java b/src/main/java/ru/windcorp/jputil/chars/reader/ArrayCharReader.java
index 13a32d2..f12f590 100644
--- a/src/main/java/ru/windcorp/jputil/chars/reader/ArrayCharReader.java
+++ b/src/main/java/ru/windcorp/jputil/chars/reader/ArrayCharReader.java
@@ -1,59 +1,60 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars.reader;
-
-import java.util.Objects;
-
-import ru.windcorp.jputil.ArrayUtil;
-
-/**
- * @author Javapony
- *
- */
-public class ArrayCharReader extends AbstractCharReader {
-
- private final char[] array;
- private final int offset;
- private final int length;
-
- public ArrayCharReader(char[] array, int offset, int length) {
- this.array = Objects.requireNonNull(array, "array");
- this.length = ArrayUtil.checkArrayOffsetLength(array, offset, length);
- this.offset = offset;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#current()
- */
- @Override
- public char current() {
- if (position >= length) return DONE;
- if (position < 0)
- throw new IllegalStateException("Position " + position + " is invalid");
- return array[position + offset];
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#remaining()
- */
- @Override
- public int remaining() {
- return length - position;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars.reader;
+
+import java.util.Objects;
+
+import ru.windcorp.jputil.ArrayUtil;
+
+/**
+ * @author Javapony
+ */
+public class ArrayCharReader extends AbstractCharReader {
+
+ private final char[] array;
+ private final int offset;
+ private final int length;
+
+ public ArrayCharReader(char[] array, int offset, int length) {
+ this.array = Objects.requireNonNull(array, "array");
+ this.length = ArrayUtil.checkArrayOffsetLength(array, offset, length);
+ this.offset = offset;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#current()
+ */
+ @Override
+ public char current() {
+ if (position >= length)
+ return DONE;
+ if (position < 0)
+ throw new IllegalStateException("Position " + position + " is invalid");
+ return array[position + offset];
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#remaining()
+ */
+ @Override
+ public int remaining() {
+ return length - position;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/reader/BufferedCharReader.java b/src/main/java/ru/windcorp/jputil/chars/reader/BufferedCharReader.java
index 5c6d5bd..83ae6b2 100644
--- a/src/main/java/ru/windcorp/jputil/chars/reader/BufferedCharReader.java
+++ b/src/main/java/ru/windcorp/jputil/chars/reader/BufferedCharReader.java
@@ -1,122 +1,128 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars.reader;
-
-/**
- * @author Javapony
- *
- */
-public abstract class BufferedCharReader extends AbstractCharReader {
-
- protected static final int DEFAULT_BUFFER_SIZE = 256;
- /**
- * Buffer to store data acquired with {@link #pullChars(char[], int, int)}.
- * Contains characters for positions [0; bufferNextIndex)
.
- */
- private char[] buffer = new char[DEFAULT_BUFFER_SIZE];
-
- /**
- * The index of the next character.
- */
- private int bufferNextIndex = 0;
-
- /**
- * Whether this reader has been buffered completely.
- */
- private boolean exhausted = false;
-
- /**
- * Acquires the next character.
- * @return the character or {@link #DONE} if the end of the reader has been reached
- */
- protected abstract char pullChar();
-
- /**
- * Acquires next characters and stores them in the array.
- *
- * @param buffer the output array
- * @param offset index of the first character
- * @param length maximum amount of characters to be pulled
- * @return the amount of characters actually pulled
- */
- protected int pullChars(char[] buffer, int offset, int length) {
- for (int i = 0; i < length; ++i) {
- if ((buffer[offset + i] = pullChar()) == DONE) {
- return i;
- }
- }
-
- return length;
- }
-
- private int pullChars(int offset, int length) {
- if (exhausted || length == 0) return 0;
-
- int pulled = pullChars(buffer, offset, length);
- if (pulled != length) {
- exhausted = true;
- }
-
- return pulled;
- }
-
- @Override
- public char current() {
- if (getPosition() < 0) {
- throw new IllegalStateException("Position " + getPosition() + " is invalid");
- }
-
- if (getPosition() >= bufferNextIndex) {
- if (exhausted) return DONE;
-
- ensureBufferCapacity();
-
- int needToPull = getPosition() - bufferNextIndex + 1;
- assert needToPull <= buffer.length : "buffer size not ensured!";
-
- int pulled = pullChars(bufferNextIndex, needToPull);
- bufferNextIndex += pulled;
-
- if (exhausted) return DONE;
- }
-
- // TODO test the shit out of current()
-
- return buffer[getPosition()];
- }
-
- private void ensureBufferCapacity() {
- if (getPosition() < buffer.length) return;
- char[] newBuffer = new char[closestGreaterPowerOf2(getPosition())];
- System.arraycopy(buffer, 0, newBuffer, 0, bufferNextIndex);
- buffer = newBuffer;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#remaining()
- */
- @Override
- public int remaining() {
- if (exhausted) {
- return Math.max(bufferNextIndex - getPosition(), 0);
- }
-
- return super.remaining();
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars.reader;
+
+/**
+ * @author Javapony
+ */
+public abstract class BufferedCharReader extends AbstractCharReader {
+
+ protected static final int DEFAULT_BUFFER_SIZE = 256;
+ /**
+ * Buffer to store data acquired with {@link #pullChars(char[], int, int)}.
+ * Contains characters for positions [0; bufferNextIndex)
.
+ */
+ private char[] buffer = new char[DEFAULT_BUFFER_SIZE];
+
+ /**
+ * The index of the next character.
+ */
+ private int bufferNextIndex = 0;
+
+ /**
+ * Whether this reader has been buffered completely.
+ */
+ private boolean exhausted = false;
+
+ /**
+ * Acquires the next character.
+ *
+ * @return the character or {@link #DONE} if the end of the reader has been
+ * reached
+ */
+ protected abstract char pullChar();
+
+ /**
+ * Acquires next characters and stores them in the array.
+ *
+ * @param buffer the output array
+ * @param offset index of the first character
+ * @param length maximum amount of characters to be pulled
+ * @return the amount of characters actually pulled
+ */
+ protected int pullChars(char[] buffer, int offset, int length) {
+ for (int i = 0; i < length; ++i) {
+ if ((buffer[offset + i] = pullChar()) == DONE) {
+ return i;
+ }
+ }
+
+ return length;
+ }
+
+ private int pullChars(int offset, int length) {
+ if (exhausted || length == 0)
+ return 0;
+
+ int pulled = pullChars(buffer, offset, length);
+ if (pulled != length) {
+ exhausted = true;
+ }
+
+ return pulled;
+ }
+
+ @Override
+ public char current() {
+ if (getPosition() < 0) {
+ throw new IllegalStateException("Position " + getPosition() + " is invalid");
+ }
+
+ if (getPosition() >= bufferNextIndex) {
+ if (exhausted)
+ return DONE;
+
+ ensureBufferCapacity();
+
+ int needToPull = getPosition() - bufferNextIndex + 1;
+ assert needToPull <= buffer.length : "buffer size not ensured!";
+
+ int pulled = pullChars(bufferNextIndex, needToPull);
+ bufferNextIndex += pulled;
+
+ if (exhausted)
+ return DONE;
+ }
+
+ // TODO test the shit out of current()
+
+ return buffer[getPosition()];
+ }
+
+ private void ensureBufferCapacity() {
+ if (getPosition() < buffer.length)
+ return;
+ char[] newBuffer = new char[closestGreaterPowerOf2(getPosition())];
+ System.arraycopy(buffer, 0, newBuffer, 0, bufferNextIndex);
+ buffer = newBuffer;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#remaining()
+ */
+ @Override
+ public int remaining() {
+ if (exhausted) {
+ return Math.max(bufferNextIndex - getPosition(), 0);
+ }
+
+ return super.remaining();
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/reader/CharReader.java b/src/main/java/ru/windcorp/jputil/chars/reader/CharReader.java
index c6dd90a..147daf3 100644
--- a/src/main/java/ru/windcorp/jputil/chars/reader/CharReader.java
+++ b/src/main/java/ru/windcorp/jputil/chars/reader/CharReader.java
@@ -1,270 +1,284 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars.reader;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import ru.windcorp.jputil.chars.CharPredicate;
-import ru.windcorp.jputil.chars.EscapeException;
-import ru.windcorp.jputil.chars.Escaper;
-
-/**
- * @author Javapony
- *
- */
-
-// SonarLint: Constants should not be defined in interfaces (java:S1214)
-// DONE is an essential part of the interface
-@SuppressWarnings("squid:S1214")
-
-public interface CharReader {
-
- char DONE = '\uFFFF';
-
- char current();
- int getPosition();
- int setPosition(int position);
-
- default char next() {
- return advance(1);
- }
-
- default char previous() {
- return rollBack(1);
- }
-
- default char consume() {
- char c = current();
- advance(1);
- return c;
- }
-
- default char advance(int forward) {
- setPosition(getPosition() + forward);
- return current();
- }
-
- default char rollBack(int backward) {
- return advance(-backward);
- }
-
- default boolean isEnd() {
- return current() == DONE;
- }
-
- default boolean has() {
- return current() != DONE;
- }
-
- default boolean is(char c) {
- return current() == c;
- }
-
- default int getChars(char[] output, int offset, int length) {
- for (int i = 0; i < length; ++i) {
- if ((output[offset + i] = current()) == DONE) {
- return i;
- }
- next();
- }
-
- return length;
- }
-
- default int getChars(char[] output) {
- return getChars(output, 0, output.length);
- }
-
- default char[] getChars(int length) {
- char[] result = new char[length];
- int from = getChars(result);
- if (from != length) Arrays.fill(result, from, length, DONE);
- return result;
- }
-
- default char[] getChars() {
- return getChars(remaining());
- }
-
- default String getString(int length) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < length && !isEnd(); ++i) sb.append(consume());
- return sb.toString();
- }
-
- default String getString() {
- return getString(Integer.MAX_VALUE);
- }
-
- default boolean match(CharSequence seq) {
- for (int i = 0; i < seq.length(); ++i) {
- if (isEnd()) return false;
- if (current() != seq.charAt(i)) return false;
- next();
- }
-
- return true;
- }
-
- default boolean matchOrReset(CharSequence seq) {
- mark();
- if (match(seq)) {
- forget();
- return true;
- } else {
- reset();
- return false;
- }
- }
-
- default boolean match(char[] array) {
- for (int i = 0; i < array.length; ++i) {
- if (isEnd()) return false;
- if (current() != array[i]) return false;
- next();
- }
-
- return true;
- }
-
- default boolean matchOrReset(char[] array) {
- mark();
- if (match(array)) {
- forget();
- return true;
- } else {
- reset();
- return false;
- }
- }
-
- default int skip(CharPredicate condition) {
- int i = 0;
-
- while (has() && condition.test(current())) {
- i++;
- next();
- }
-
- return i;
- }
-
- default int skipWhitespace() {
- return skip(Character::isWhitespace);
- }
-
- /**
- * Skips to the end of the current line. Both "\n"
, "\r"
- * and "\r\n"
are considered line separators.
- * @return the amount of characters in the skipped line
- */
- default int skipLine() {
- int i = 0;
-
- while (!isEnd()) {
- if (current() == '\r') {
- if (next() == '\n') {
- next();
- }
- break;
- } else if (current() == '\n') {
- next();
- break;
- }
-
- i++;
- next();
- }
-
- return i;
- }
-
- default char[] readWhile(CharPredicate condition) {
- return readUntil(CharPredicate.negate(condition));
- }
-
- default char[] readUntil(CharPredicate condition) {
- mark();
- int length = 0;
- while (!isEnd() && !condition.test(current())) {
- length++;
- next();
- }
- reset();
-
- char[] result = new char[length];
- for (int i = 0; i < length; ++i) result[i] = consume();
- return result;
- }
-
- default char[] readWord() {
- skipWhitespace();
- return readUntil(Character::isWhitespace);
- }
-
- default char[] readWord(Escaper escaper, char quotes) throws EscapeException {
- skipWhitespace();
-
- if (current() == quotes) {
- return escaper.unescape(this, quotes);
- } else {
- return readWord();
- }
- }
-
- default char[] readLine() {
- mark();
- int length = skipLine();
- reset();
-
- char[] result = new char[length];
- for (int i = 0; i < result.length; ++i) result[i] = consume();
- return result;
- }
-
- default int remaining() {
- mark();
- int result = 0;
-
- while (consume() != DONE) result++;
-
- reset();
- return result;
- }
-
- int mark();
- int forget();
-
- default int reset() {
- return setPosition(forget());
- }
-
- default IOException getLastException() {
- return null;
- }
-
- default void resetLastException() {
- // Do nothing
- }
-
- default boolean hasErrored() {
- return getLastException() != null;
- }
-
-}
\ No newline at end of file
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars.reader;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import ru.windcorp.jputil.chars.CharPredicate;
+import ru.windcorp.jputil.chars.EscapeException;
+import ru.windcorp.jputil.chars.Escaper;
+
+/**
+ * @author Javapony
+ */
+
+// SonarLint: Constants should not be defined in interfaces (java:S1214)
+// DONE is an essential part of the interface
+@SuppressWarnings("squid:S1214")
+
+public interface CharReader {
+
+ char DONE = '\uFFFF';
+
+ char current();
+
+ int getPosition();
+
+ int setPosition(int position);
+
+ default char next() {
+ return advance(1);
+ }
+
+ default char previous() {
+ return rollBack(1);
+ }
+
+ default char consume() {
+ char c = current();
+ advance(1);
+ return c;
+ }
+
+ default char advance(int forward) {
+ setPosition(getPosition() + forward);
+ return current();
+ }
+
+ default char rollBack(int backward) {
+ return advance(-backward);
+ }
+
+ default boolean isEnd() {
+ return current() == DONE;
+ }
+
+ default boolean has() {
+ return current() != DONE;
+ }
+
+ default boolean is(char c) {
+ return current() == c;
+ }
+
+ default int getChars(char[] output, int offset, int length) {
+ for (int i = 0; i < length; ++i) {
+ if ((output[offset + i] = current()) == DONE) {
+ return i;
+ }
+ next();
+ }
+
+ return length;
+ }
+
+ default int getChars(char[] output) {
+ return getChars(output, 0, output.length);
+ }
+
+ default char[] getChars(int length) {
+ char[] result = new char[length];
+ int from = getChars(result);
+ if (from != length)
+ Arrays.fill(result, from, length, DONE);
+ return result;
+ }
+
+ default char[] getChars() {
+ return getChars(remaining());
+ }
+
+ default String getString(int length) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < length && !isEnd(); ++i)
+ sb.append(consume());
+ return sb.toString();
+ }
+
+ default String getString() {
+ return getString(Integer.MAX_VALUE);
+ }
+
+ default boolean match(CharSequence seq) {
+ for (int i = 0; i < seq.length(); ++i) {
+ if (isEnd())
+ return false;
+ if (current() != seq.charAt(i))
+ return false;
+ next();
+ }
+
+ return true;
+ }
+
+ default boolean matchOrReset(CharSequence seq) {
+ mark();
+ if (match(seq)) {
+ forget();
+ return true;
+ } else {
+ reset();
+ return false;
+ }
+ }
+
+ default boolean match(char[] array) {
+ for (int i = 0; i < array.length; ++i) {
+ if (isEnd())
+ return false;
+ if (current() != array[i])
+ return false;
+ next();
+ }
+
+ return true;
+ }
+
+ default boolean matchOrReset(char[] array) {
+ mark();
+ if (match(array)) {
+ forget();
+ return true;
+ } else {
+ reset();
+ return false;
+ }
+ }
+
+ default int skip(CharPredicate condition) {
+ int i = 0;
+
+ while (has() && condition.test(current())) {
+ i++;
+ next();
+ }
+
+ return i;
+ }
+
+ default int skipWhitespace() {
+ return skip(Character::isWhitespace);
+ }
+
+ /**
+ * Skips to the end of the current line. Both "\n"
,
+ * "\r"
+ * and "\r\n"
are considered line separators.
+ *
+ * @return the amount of characters in the skipped line
+ */
+ default int skipLine() {
+ int i = 0;
+
+ while (!isEnd()) {
+ if (current() == '\r') {
+ if (next() == '\n') {
+ next();
+ }
+ break;
+ } else if (current() == '\n') {
+ next();
+ break;
+ }
+
+ i++;
+ next();
+ }
+
+ return i;
+ }
+
+ default char[] readWhile(CharPredicate condition) {
+ return readUntil(CharPredicate.negate(condition));
+ }
+
+ default char[] readUntil(CharPredicate condition) {
+ mark();
+ int length = 0;
+ while (!isEnd() && !condition.test(current())) {
+ length++;
+ next();
+ }
+ reset();
+
+ char[] result = new char[length];
+ for (int i = 0; i < length; ++i)
+ result[i] = consume();
+ return result;
+ }
+
+ default char[] readWord() {
+ skipWhitespace();
+ return readUntil(Character::isWhitespace);
+ }
+
+ default char[] readWord(Escaper escaper, char quotes) throws EscapeException {
+ skipWhitespace();
+
+ if (current() == quotes) {
+ return escaper.unescape(this, quotes);
+ } else {
+ return readWord();
+ }
+ }
+
+ default char[] readLine() {
+ mark();
+ int length = skipLine();
+ reset();
+
+ char[] result = new char[length];
+ for (int i = 0; i < result.length; ++i)
+ result[i] = consume();
+ return result;
+ }
+
+ default int remaining() {
+ mark();
+ int result = 0;
+
+ while (consume() != DONE)
+ result++;
+
+ reset();
+ return result;
+ }
+
+ int mark();
+
+ int forget();
+
+ default int reset() {
+ return setPosition(forget());
+ }
+
+ default IOException getLastException() {
+ return null;
+ }
+
+ default void resetLastException() {
+ // Do nothing
+ }
+
+ default boolean hasErrored() {
+ return getLastException() != null;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/reader/CharReaders.java b/src/main/java/ru/windcorp/jputil/chars/reader/CharReaders.java
index 9cd02e0..a181dbf 100644
--- a/src/main/java/ru/windcorp/jputil/chars/reader/CharReaders.java
+++ b/src/main/java/ru/windcorp/jputil/chars/reader/CharReaders.java
@@ -1,112 +1,113 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars.reader;
-
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.text.CharacterIterator;
-import java.util.function.IntSupplier;
-
-import ru.windcorp.jputil.chars.CharSupplier;
-
-/**
- * @author Javapony
- *
- */
-public class CharReaders {
-
- private CharReaders() {}
-
- public static CharReader wrap(char[] array, int offset, int length) {
- return new ArrayCharReader(array, offset, length);
- }
-
- public static CharReader wrap(char[] array) {
- return wrap(array, 0, array.length);
- }
-
- public static CharReader wrap(String str, int offset, int length) {
- return new StringCharReader(str, offset, length);
- }
-
- public static CharReader wrap(String str) {
- return wrap(str, 0, str.length());
- }
-
- public static CharReader wrap(CharSupplier supplier) {
- return new BufferedCharReader() {
- @Override
- protected char pullChar() {
- try {
- return supplier.getAsChar();
- } catch (Exception e) {
- return DONE;
- }
- }
- };
- }
-
- public static CharReader wrap(IntSupplier supplier) {
- return new BufferedCharReader() {
- @Override
- protected char pullChar() {
- try {
- int i = supplier.getAsInt();
- if (i < 0 || i > Character.MAX_VALUE) {
- return DONE;
- } else {
- return (char) i;
- }
- } catch (Exception e) {
- return DONE;
- }
- }
- };
- }
-
- public static CharReader wrap(CharacterIterator it) {
- return new BufferedCharReader() {
- @Override
- protected char pullChar() {
- char result = it.current();
- it.next();
- return result;
- }
- };
- }
-
- public static CharReader wrap(Reader reader) {
- return new ReaderCharReader(reader);
- }
-
- public static CharReader wrap(InputStream is, Charset charset) {
- return wrap(new InputStreamReader(is, charset));
- }
-
- public static CharReader wrapDefaultCS(InputStream is) {
- return wrap(new InputStreamReader(is));
- }
-
- public static CharReader wrapUTF8(InputStream is) {
- return wrap(new InputStreamReader(is, StandardCharsets.UTF_8));
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars.reader;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.text.CharacterIterator;
+import java.util.function.IntSupplier;
+
+import ru.windcorp.jputil.chars.CharSupplier;
+
+/**
+ * @author Javapony
+ */
+public class CharReaders {
+
+ private CharReaders() {
+ }
+
+ public static CharReader wrap(char[] array, int offset, int length) {
+ return new ArrayCharReader(array, offset, length);
+ }
+
+ public static CharReader wrap(char[] array) {
+ return wrap(array, 0, array.length);
+ }
+
+ public static CharReader wrap(String str, int offset, int length) {
+ return new StringCharReader(str, offset, length);
+ }
+
+ public static CharReader wrap(String str) {
+ return wrap(str, 0, str.length());
+ }
+
+ public static CharReader wrap(CharSupplier supplier) {
+ return new BufferedCharReader() {
+ @Override
+ protected char pullChar() {
+ try {
+ return supplier.getAsChar();
+ } catch (Exception e) {
+ return DONE;
+ }
+ }
+ };
+ }
+
+ public static CharReader wrap(IntSupplier supplier) {
+ return new BufferedCharReader() {
+ @Override
+ protected char pullChar() {
+ try {
+ int i = supplier.getAsInt();
+ if (i < 0 || i > Character.MAX_VALUE) {
+ return DONE;
+ } else {
+ return (char) i;
+ }
+ } catch (Exception e) {
+ return DONE;
+ }
+ }
+ };
+ }
+
+ public static CharReader wrap(CharacterIterator it) {
+ return new BufferedCharReader() {
+ @Override
+ protected char pullChar() {
+ char result = it.current();
+ it.next();
+ return result;
+ }
+ };
+ }
+
+ public static CharReader wrap(Reader reader) {
+ return new ReaderCharReader(reader);
+ }
+
+ public static CharReader wrap(InputStream is, Charset charset) {
+ return wrap(new InputStreamReader(is, charset));
+ }
+
+ public static CharReader wrapDefaultCS(InputStream is) {
+ return wrap(new InputStreamReader(is));
+ }
+
+ public static CharReader wrapUTF8(InputStream is) {
+ return wrap(new InputStreamReader(is, StandardCharsets.UTF_8));
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/reader/ReaderCharReader.java b/src/main/java/ru/windcorp/jputil/chars/reader/ReaderCharReader.java
index 0a2435f..51a88db 100644
--- a/src/main/java/ru/windcorp/jputil/chars/reader/ReaderCharReader.java
+++ b/src/main/java/ru/windcorp/jputil/chars/reader/ReaderCharReader.java
@@ -1,70 +1,71 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars.reader;
-
-import java.io.IOException;
-import java.io.Reader;
-
-/**
- * @author Javapony
- *
- */
-public class ReaderCharReader extends BufferedCharReader {
-
- private final Reader src;
- private IOException lastException = null;
-
- public ReaderCharReader(Reader src) {
- this.src = src;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.BufferedCharReader#pullChar()
- */
- @Override
- protected char pullChar() {
- try {
- return (char) src.read(); // Handles DONE correctly
- } catch (IOException e) {
- lastException = e;
- return DONE;
- }
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.BufferedCharReader#pullChars(char[], int, int)
- */
- @Override
- protected int pullChars(char[] buffer, int offset, int length) {
- try {
- return src.read(buffer, offset, length);
- } catch (IOException e) {
- lastException = e;
- return 0;
- }
- }
-
- /**
- * @return the exception
- */
- @Override
- public IOException getLastException() {
- return lastException;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars.reader;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * @author Javapony
+ */
+public class ReaderCharReader extends BufferedCharReader {
+
+ private final Reader src;
+ private IOException lastException = null;
+
+ public ReaderCharReader(Reader src) {
+ this.src = src;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.BufferedCharReader#pullChar()
+ */
+ @Override
+ protected char pullChar() {
+ try {
+ return (char) src.read(); // Handles DONE correctly
+ } catch (IOException e) {
+ lastException = e;
+ return DONE;
+ }
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.BufferedCharReader#pullChars(char[],
+ * int, int)
+ */
+ @Override
+ protected int pullChars(char[] buffer, int offset, int length) {
+ try {
+ return src.read(buffer, offset, length);
+ } catch (IOException e) {
+ lastException = e;
+ return 0;
+ }
+ }
+
+ /**
+ * @return the exception
+ */
+ @Override
+ public IOException getLastException() {
+ return lastException;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/chars/reader/StringCharReader.java b/src/main/java/ru/windcorp/jputil/chars/reader/StringCharReader.java
index c082321..5836d15 100644
--- a/src/main/java/ru/windcorp/jputil/chars/reader/StringCharReader.java
+++ b/src/main/java/ru/windcorp/jputil/chars/reader/StringCharReader.java
@@ -1,65 +1,68 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.chars.reader;
-
-import java.util.Objects;
-
-/**
- * @author Javapony
- *
- */
-public class StringCharReader extends AbstractCharReader {
-
- private final String str;
- private final int offset;
- private final int length;
-
- public StringCharReader(String str, int offset, int length) {
- this.str = Objects.requireNonNull(str, "str");
-
- if (length < 0)
- length = str.length();
-
- int end = offset + length;
- if (end > str.length() || offset < 0)
- throw new IllegalArgumentException("String contains [0; " + str.length() + "), requested [" + offset + "; " + end + ")");
-
- this.offset = offset;
- this.length = length;
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#current()
- */
- @Override
- public char current() {
- if (position >= length) return DONE;
- if (position < 0)
- throw new IllegalStateException("Position " + position + " is invalid");
- return str.charAt(position + offset);
- }
-
- /**
- * @see ru.windcorp.jputil.chars.reader.CharReader#remaining()
- */
- @Override
- public int remaining() {
- return length - position;
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.chars.reader;
+
+import java.util.Objects;
+
+/**
+ * @author Javapony
+ */
+public class StringCharReader extends AbstractCharReader {
+
+ private final String str;
+ private final int offset;
+ private final int length;
+
+ public StringCharReader(String str, int offset, int length) {
+ this.str = Objects.requireNonNull(str, "str");
+
+ if (length < 0)
+ length = str.length();
+
+ int end = offset + length;
+ if (end > str.length() || offset < 0)
+ throw new IllegalArgumentException(
+ "String contains [0; " + str.length() + "), requested [" + offset + "; " + end + ")"
+ );
+
+ this.offset = offset;
+ this.length = length;
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#current()
+ */
+ @Override
+ public char current() {
+ if (position >= length)
+ return DONE;
+ if (position < 0)
+ throw new IllegalStateException("Position " + position + " is invalid");
+ return str.charAt(position + offset);
+ }
+
+ /**
+ * @see ru.windcorp.jputil.chars.reader.CharReader#remaining()
+ */
+ @Override
+ public int remaining() {
+ return length - position;
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java b/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java
index 060b06a..a157032 100644
--- a/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java
+++ b/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java
@@ -1,8 +1,26 @@
-package ru.windcorp.jputil.functions;
-
-@FunctionalInterface
-public interface FloatSupplier {
-
- float getAsFloat();
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.functions;
+
+@FunctionalInterface
+public interface FloatSupplier {
+
+ float getAsFloat();
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/functions/ThrowingBiConsumer.java b/src/main/java/ru/windcorp/jputil/functions/ThrowingBiConsumer.java
index 4e1c26e..be7cb8f 100644
--- a/src/main/java/ru/windcorp/jputil/functions/ThrowingBiConsumer.java
+++ b/src/main/java/ru/windcorp/jputil/functions/ThrowingBiConsumer.java
@@ -1,72 +1,76 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.functions;
-
-import java.util.function.BiConsumer;
-
-@FunctionalInterface
-public interface ThrowingBiConsumer {
-
- @FunctionalInterface
- public static interface BiConsumerHandler {
- void handle(T t, U u, E e);
- }
-
- void accept(T t, U u) throws E;
-
- @SuppressWarnings("unchecked")
- default BiConsumer withHandler(BiConsumerHandler super T, ? super U, ? super E> handler) {
- return (t, u) -> {
- try {
- accept(t, u);
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- handler.handle(t, u, (E) e);
- }
- };
- }
-
- public static ThrowingBiConsumer concat(
- ThrowingBiConsumer super T, ? super U, ? extends E> first,
- ThrowingBiConsumer super T, ? super U, ? extends E> second) {
- return (t, u) -> {
- first.accept(t, u);
- second.accept(t, u);
- };
- }
-
- public static ThrowingBiConsumer concat(
- BiConsumer super T, ? super U> first,
- ThrowingBiConsumer super T, ? super U, E> second) {
- return (t, u) -> {
- first.accept(t, u);
- second.accept(t, u);
- };
- }
-
- public static ThrowingBiConsumer concat(
- ThrowingBiConsumer super T, ? super U, E> first,
- BiConsumer super T, ? super U> second) {
- return (t, u) -> {
- first.accept(t, u);
- second.accept(t, u);
- };
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.functions;
+
+import java.util.function.BiConsumer;
+
+@FunctionalInterface
+public interface ThrowingBiConsumer {
+
+ @FunctionalInterface
+ public static interface BiConsumerHandler {
+ void handle(T t, U u, E e);
+ }
+
+ void accept(T t, U u) throws E;
+
+ @SuppressWarnings("unchecked")
+ default BiConsumer withHandler(BiConsumerHandler super T, ? super U, ? super E> handler) {
+ return (t, u) -> {
+ try {
+ accept(t, u);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ handler.handle(t, u, (E) e);
+ }
+ };
+ }
+
+ public static ThrowingBiConsumer concat(
+ ThrowingBiConsumer super T, ? super U, ? extends E> first,
+ ThrowingBiConsumer super T, ? super U, ? extends E> second
+ ) {
+ return (t, u) -> {
+ first.accept(t, u);
+ second.accept(t, u);
+ };
+ }
+
+ public static ThrowingBiConsumer concat(
+ BiConsumer super T, ? super U> first,
+ ThrowingBiConsumer super T, ? super U, E> second
+ ) {
+ return (t, u) -> {
+ first.accept(t, u);
+ second.accept(t, u);
+ };
+ }
+
+ public static ThrowingBiConsumer concat(
+ ThrowingBiConsumer super T, ? super U, E> first,
+ BiConsumer super T, ? super U> second
+ ) {
+ return (t, u) -> {
+ first.accept(t, u);
+ second.accept(t, u);
+ };
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/functions/ThrowingConsumer.java b/src/main/java/ru/windcorp/jputil/functions/ThrowingConsumer.java
index dcd1931..14449ca 100644
--- a/src/main/java/ru/windcorp/jputil/functions/ThrowingConsumer.java
+++ b/src/main/java/ru/windcorp/jputil/functions/ThrowingConsumer.java
@@ -1,62 +1,72 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.functions;
-
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-@FunctionalInterface
-public interface ThrowingConsumer {
-
- void accept(T t) throws E;
-
- @SuppressWarnings("unchecked")
- default Consumer withHandler(BiConsumer super T, ? super E> handler) {
- return t -> {
- try {
- accept(t);
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- handler.accept(t, (E) e);
- }
- };
- }
-
- public static ThrowingConsumer concat(ThrowingConsumer super T, ? extends E> first, ThrowingConsumer super T, ? extends E> second) {
- return t -> {
- first.accept(t);
- second.accept(t);
- };
- }
-
- public static ThrowingConsumer concat(Consumer super T> first, ThrowingConsumer super T, ? extends E> second) {
- return t -> {
- first.accept(t);
- second.accept(t);
- };
- }
-
- public static ThrowingConsumer concat(ThrowingConsumer super T, ? extends E> first, Consumer super T> second) {
- return t -> {
- first.accept(t);
- second.accept(t);
- };
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.functions;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+@FunctionalInterface
+public interface ThrowingConsumer {
+
+ void accept(T t) throws E;
+
+ @SuppressWarnings("unchecked")
+ default Consumer withHandler(BiConsumer super T, ? super E> handler) {
+ return t -> {
+ try {
+ accept(t);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ handler.accept(t, (E) e);
+ }
+ };
+ }
+
+ public static ThrowingConsumer concat(
+ ThrowingConsumer super T, ? extends E> first,
+ ThrowingConsumer super T, ? extends E> second
+ ) {
+ return t -> {
+ first.accept(t);
+ second.accept(t);
+ };
+ }
+
+ public static ThrowingConsumer concat(
+ Consumer super T> first,
+ ThrowingConsumer super T, ? extends E> second
+ ) {
+ return t -> {
+ first.accept(t);
+ second.accept(t);
+ };
+ }
+
+ public static ThrowingConsumer concat(
+ ThrowingConsumer super T, ? extends E> first,
+ Consumer super T> second
+ ) {
+ return t -> {
+ first.accept(t);
+ second.accept(t);
+ };
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/functions/ThrowingFunction.java b/src/main/java/ru/windcorp/jputil/functions/ThrowingFunction.java
index faffb44..afdd078 100644
--- a/src/main/java/ru/windcorp/jputil/functions/ThrowingFunction.java
+++ b/src/main/java/ru/windcorp/jputil/functions/ThrowingFunction.java
@@ -1,73 +1,81 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.functions;
-
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-@FunctionalInterface
-public interface ThrowingFunction {
-
- R apply(T t) throws E;
-
- @SuppressWarnings("unchecked")
- default Function withHandler(BiConsumer super T, ? super E> handler, Function super T, ? extends R> value) {
- return t -> {
- try {
- return apply(t);
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- if (handler != null) handler.accept(t, (E) e);
- return value == null ? null : value.apply(t);
- }
- };
- }
-
- default Function withHandler(BiConsumer super T, ? super E> handler, Supplier extends R> value) {
- return withHandler(handler, t -> value.get());
- }
-
- default Function withHandler(BiConsumer super T, ? super E> handler, R value) {
- return withHandler(handler, t -> value);
- }
-
- default Function withHandler(BiConsumer super T, ? super E> handler) {
- return withHandler(handler, (Function) null);
- }
-
- public static ThrowingFunction compose(
- ThrowingFunction super T, I, ? extends E> first,
- ThrowingFunction super I, ? extends R, ? extends E> second) {
- return t -> second.apply(first.apply(t));
- }
-
- public static ThrowingFunction compose(
- Function super T, I> first,
- ThrowingFunction super I, ? extends R, E> second) {
- return t -> second.apply(first.apply(t));
- }
-
- public static ThrowingFunction compose(
- ThrowingFunction super T, I, E> first,
- Function super I, ? extends R> second) {
- return t -> second.apply(first.apply(t));
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.functions;
+
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+@FunctionalInterface
+public interface ThrowingFunction {
+
+ R apply(T t) throws E;
+
+ @SuppressWarnings("unchecked")
+ default Function withHandler(
+ BiConsumer super T, ? super E> handler,
+ Function super T, ? extends R> value
+ ) {
+ return t -> {
+ try {
+ return apply(t);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ if (handler != null)
+ handler.accept(t, (E) e);
+ return value == null ? null : value.apply(t);
+ }
+ };
+ }
+
+ default Function withHandler(BiConsumer super T, ? super E> handler, Supplier extends R> value) {
+ return withHandler(handler, t -> value.get());
+ }
+
+ default Function withHandler(BiConsumer super T, ? super E> handler, R value) {
+ return withHandler(handler, t -> value);
+ }
+
+ default Function withHandler(BiConsumer super T, ? super E> handler) {
+ return withHandler(handler, (Function) null);
+ }
+
+ public static ThrowingFunction compose(
+ ThrowingFunction super T, I, ? extends E> first,
+ ThrowingFunction super I, ? extends R, ? extends E> second
+ ) {
+ return t -> second.apply(first.apply(t));
+ }
+
+ public static ThrowingFunction compose(
+ Function super T, I> first,
+ ThrowingFunction super I, ? extends R, E> second
+ ) {
+ return t -> second.apply(first.apply(t));
+ }
+
+ public static ThrowingFunction compose(
+ ThrowingFunction super T, I, E> first,
+ Function super I, ? extends R> second
+ ) {
+ return t -> second.apply(first.apply(t));
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/functions/ThrowingRunnable.java b/src/main/java/ru/windcorp/jputil/functions/ThrowingRunnable.java
index 0684122..f27429b 100644
--- a/src/main/java/ru/windcorp/jputil/functions/ThrowingRunnable.java
+++ b/src/main/java/ru/windcorp/jputil/functions/ThrowingRunnable.java
@@ -1,64 +1,65 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.functions;
-
-import java.util.function.Consumer;
-
-@FunctionalInterface
-public interface ThrowingRunnable {
-
- void run() throws E;
-
- @SuppressWarnings("unchecked")
- default Runnable withHandler(Consumer super E> handler) {
- return () -> {
- try {
- run();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- handler.accept((E) e);
- }
- };
- }
-
- public static ThrowingRunnable concat(
- ThrowingRunnable extends E> first,
- ThrowingRunnable extends E> second
- ) {
- return () -> {
- first.run();
- second.run();
- };
- }
-
- public static ThrowingRunnable concat(Runnable first, ThrowingRunnable second) {
- return () -> {
- first.run();
- second.run();
- };
- }
-
- public static ThrowingRunnable concat(ThrowingRunnable first, Runnable second) {
- return () -> {
- first.run();
- second.run();
- };
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.functions;
+
+import java.util.function.Consumer;
+
+@FunctionalInterface
+public interface ThrowingRunnable {
+
+ void run() throws E;
+
+ @SuppressWarnings("unchecked")
+ default Runnable withHandler(Consumer super E> handler) {
+ return () -> {
+ try {
+ run();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ handler.accept((E) e);
+ }
+ };
+ }
+
+ public static ThrowingRunnable concat(
+ ThrowingRunnable extends E> first,
+ ThrowingRunnable extends E> second
+ ) {
+ return () -> {
+ first.run();
+ second.run();
+ };
+ }
+
+ public static ThrowingRunnable concat(Runnable first, ThrowingRunnable second) {
+ return () -> {
+ first.run();
+ second.run();
+ };
+ }
+
+ public static ThrowingRunnable concat(ThrowingRunnable first, Runnable second) {
+ return () -> {
+ first.run();
+ second.run();
+ };
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/functions/ThrowingSupplier.java b/src/main/java/ru/windcorp/jputil/functions/ThrowingSupplier.java
index 32327e5..84ad690 100644
--- a/src/main/java/ru/windcorp/jputil/functions/ThrowingSupplier.java
+++ b/src/main/java/ru/windcorp/jputil/functions/ThrowingSupplier.java
@@ -1,50 +1,52 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.functions;
-
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-@FunctionalInterface
-public interface ThrowingSupplier {
-
- T get() throws E;
-
- @SuppressWarnings("unchecked")
- default Supplier withHandler(Consumer super E> handler, Supplier extends T> value) {
- return () -> {
- try {
- return get();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- if (handler != null) handler.accept((E) e);
- return value == null ? null : value.get();
- }
- };
- }
-
- default Supplier withHandler(Consumer super E> handler, T value) {
- return withHandler(handler, () -> value);
- }
-
- default Supplier withHandler(Consumer super E> handler) {
- return withHandler(handler, (Supplier) null);
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.functions;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+@FunctionalInterface
+public interface ThrowingSupplier {
+
+ T get() throws E;
+
+ @SuppressWarnings("unchecked")
+ default Supplier withHandler(Consumer super E> handler, Supplier extends T> value) {
+ return () -> {
+ try {
+ return get();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ if (handler != null)
+ handler.accept((E) e);
+ return value == null ? null : value.get();
+ }
+ };
+ }
+
+ default Supplier withHandler(Consumer super E> handler, T value) {
+ return withHandler(handler, () -> value);
+ }
+
+ default Supplier withHandler(Consumer super E> handler) {
+ return withHandler(handler, (Supplier) null);
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/iterators/ArrayIterator.java b/src/main/java/ru/windcorp/jputil/iterators/ArrayIterator.java
index dc0cdcd..0b8fab2 100644
--- a/src/main/java/ru/windcorp/jputil/iterators/ArrayIterator.java
+++ b/src/main/java/ru/windcorp/jputil/iterators/ArrayIterator.java
@@ -1,47 +1,48 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.iterators;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-public class ArrayIterator implements Iterator {
-
- private final E[] array;
- private int next;
-
- @SafeVarargs
- public ArrayIterator(E... array) {
- this.array = array;
- }
-
- @Override
- public boolean hasNext() {
- return next < array.length;
- }
-
- @Override
- public E next() {
- try {
- return array[next++];
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new NoSuchElementException();
- }
- }
-
-}
+/*
+ * JPUtil
+ * Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package ru.windcorp.jputil.iterators;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class ArrayIterator implements Iterator {
+
+ private final E[] array;
+ private int next;
+
+ @SafeVarargs
+ public ArrayIterator(E... array) {
+ this.array = array;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next < array.length;
+ }
+
+ @Override
+ public E next() {
+ try {
+ return array[next++];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new NoSuchElementException();
+ }
+ }
+
+}
diff --git a/src/main/java/ru/windcorp/jputil/iterators/FunctionIterator.java b/src/main/java/ru/windcorp/jputil/iterators/FunctionIterator.java
index a8d63ae..abc5b44 100644
--- a/src/main/java/ru/windcorp/jputil/iterators/FunctionIterator.java
+++ b/src/main/java/ru/windcorp/jputil/iterators/FunctionIterator.java
@@ -1,61 +1,61 @@
-/*******************************************************************************
- * JPUtil
- * Copyright (C) 2019 Javapony/OLEGSHA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *******************************************************************************/
-package ru.windcorp.jputil.iterators;
-
-import java.util.Iterator;
-import java.util.function.Function;
-
-/**
- * @author Javapony
- *
- */
-public class FunctionIterator implements Iterator {
-
- private final Iterator parent;
- private final Function function;
-
- public FunctionIterator(Iterator