Added some packages from JPUtil (https://github.com/OLEGSHA/JPUtil)
- ru.windcorp.common.util.ThrowingRunnable replaced with ru.windcorp.jputil.functions.ThrowingRunnable
This commit is contained in:
parent
a9896fa3e1
commit
65eaae68a8
525
src/main/java/ru/windcorp/jputil/ArrayUtil.java
Normal file
525
src/main/java/ru/windcorp/jputil/ArrayUtil.java
Normal file
@ -0,0 +1,525 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ArrayUtil {
|
||||
|
||||
private ArrayUtil() {}
|
||||
|
||||
public static int firstIndexOf(byte[] array, byte element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(byte[] array, byte element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(byte[] array, byte element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(byte[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
byte a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(short[] array, short element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(short[] array, short element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(short[] array, short element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(short[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
short a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(int[] array, int element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(int[] array, int element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(int[] array, int element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(int[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
int a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(long[] array, long element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(long[] array, long element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(long[] array, long element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(long[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
long a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(float[] array, float element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(float[] array, float element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(float[] array, float element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(float[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
float a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(double[] array, double element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(double[] array, double element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(double[] array, double element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(double[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
double a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(boolean[] array, boolean element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(boolean[] array, boolean element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(boolean[] array, boolean element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(char[] array, char element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(char[] array, char element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(char[] array, char element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(char[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
char a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOf(Object[] array, Object element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOf(Object[] array, Object element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurences(Object[] array, Object element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (array[i] == element) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasDuplicates(Object[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
Object a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (array[j] == a) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int firstIndexOfEqual(Object[] array, Object element) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (Objects.equals(array[i], element)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int lastIndexOfEqual(Object[] array, Object element) {
|
||||
for (int i = array.length - 1; i >= 0; --i) {
|
||||
if (Objects.equals(array[i], element)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int occurencesOfEqual(Object[] array, Object element) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
if (Objects.equals(array[i], element)) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int hasEquals(Object[] array) {
|
||||
for (int i = 0; i < array.length; ++i) {
|
||||
Object a = array[i];
|
||||
for (int j = i + 1; j < array.length; ++j) {
|
||||
if (Objects.equals(array[j], a)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static long sum(byte[] array, int start, int length) {
|
||||
long s = 0;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s += array[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static long sum(short[] array, int start, int length) {
|
||||
long s = 0;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s += array[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static long sum(int[] array, int start, int length) {
|
||||
long s = 0;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s += array[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static long sum(long[] array, int start, int length) {
|
||||
long s = 0;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s += array[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static BigInteger longSum(long[] array, int start, int length) {
|
||||
BigInteger s = BigInteger.ZERO;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s = s.add(BigInteger.valueOf(array[i]));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static float sum(float[] array, int start, int length) {
|
||||
float s = 0;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s += array[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static double sum(double[] array, int start, int length) {
|
||||
double s = 0;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s += array[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static long sum(char[] array, int start, int length) {
|
||||
long s = 0;
|
||||
length += start;
|
||||
for (int i = start; i < length; ++i) {
|
||||
s += array[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static int checkArrayOffsetLength(Object array, int offset, int length) {
|
||||
int arrayLength = Array.getLength(array);
|
||||
|
||||
if (length < 0)
|
||||
length = arrayLength;
|
||||
|
||||
int end = offset + length;
|
||||
if (end > arrayLength || offset < 0)
|
||||
throw new IllegalArgumentException("Array contains [0; " + arrayLength + "), requested [" + offset + "; " + end + ")");
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public static int checkArrayStartEnd(Object array, int start, int end) {
|
||||
int arrayLength = Array.getLength(array);
|
||||
|
||||
if (end < 0)
|
||||
end = arrayLength;
|
||||
|
||||
if (start > end)
|
||||
throw new IllegalArgumentException("Start > end: " + start + " > " + end);
|
||||
|
||||
if (end > arrayLength || start < 0)
|
||||
throw new IllegalArgumentException("Array contains [0; " + arrayLength + "), requested [" + start + "; " + end + ")");
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
}
|
104
src/main/java/ru/windcorp/jputil/CSVWriter.java
Normal file
104
src/main/java/ru/windcorp/jputil/CSVWriter.java
Normal file
@ -0,0 +1,104 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
public class CSVWriter {
|
||||
|
||||
private String columnSeparator = ";";
|
||||
private String rowSeparator = "\n";
|
||||
|
||||
private boolean shouldAddSeparator = false;
|
||||
|
||||
private final PrintWriter parent;
|
||||
|
||||
public CSVWriter(PrintWriter output) {
|
||||
this.parent = output;
|
||||
}
|
||||
|
||||
public CSVWriter(Writer output) {
|
||||
this(new PrintWriter(output));
|
||||
}
|
||||
|
||||
public CSVWriter(OutputStream output) {
|
||||
this(new PrintWriter(output));
|
||||
}
|
||||
|
||||
public PrintWriter getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public String getColumnSeparator() {
|
||||
return columnSeparator;
|
||||
}
|
||||
|
||||
public CSVWriter setColumnSeparator(String columnSeparator) {
|
||||
this.columnSeparator = columnSeparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRowSeparator() {
|
||||
return rowSeparator;
|
||||
}
|
||||
|
||||
public CSVWriter setRowSeparator(String rowSeparator) {
|
||||
this.rowSeparator = rowSeparator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void print(Object object) {
|
||||
skip();
|
||||
getParent().print(String.valueOf(object));
|
||||
}
|
||||
|
||||
public void skip() {
|
||||
if (shouldAddSeparator) {
|
||||
getParent().print(getColumnSeparator());
|
||||
} else {
|
||||
shouldAddSeparator = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void skip(int amount) {
|
||||
for (int i = 0; i < amount; ++i) {
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
||||
public void endRow() {
|
||||
getParent().print(getRowSeparator());
|
||||
shouldAddSeparator = false;
|
||||
}
|
||||
|
||||
public void endRow(Object object) {
|
||||
print(object);
|
||||
endRow();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
getParent().flush();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
getParent().close();
|
||||
}
|
||||
|
||||
}
|
60
src/main/java/ru/windcorp/jputil/PrimitiveUtil.java
Normal file
60
src/main/java/ru/windcorp/jputil/PrimitiveUtil.java
Normal file
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PrimitiveUtil {
|
||||
|
||||
private PrimitiveUtil() {}
|
||||
|
||||
private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED = new HashMap<>();
|
||||
private static final Map<Class<?>, Object> PRIMITIVE_TO_NULL = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (Class<?> boxed : new Class<?>[] {
|
||||
Boolean.class, Byte.class, Short.class, Character.class,
|
||||
Integer.class, Long.class, Float.class, Double.class
|
||||
}) {
|
||||
try {
|
||||
PRIMITIVE_TO_BOXED.put((Class<?>) boxed.getField("TYPE").get(null), boxed);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
PRIMITIVE_TO_NULL.put(Boolean.TYPE, Boolean.FALSE);
|
||||
PRIMITIVE_TO_NULL.put(Byte.TYPE, Byte.valueOf((byte) 0));
|
||||
PRIMITIVE_TO_NULL.put(Short.TYPE, Short.valueOf((short) 0));
|
||||
PRIMITIVE_TO_NULL.put(Integer.TYPE, Integer.valueOf(0));
|
||||
PRIMITIVE_TO_NULL.put(Long.TYPE, Long.valueOf(0));
|
||||
PRIMITIVE_TO_NULL.put(Float.TYPE, Float.valueOf(Float.NaN));
|
||||
PRIMITIVE_TO_NULL.put(Double.TYPE, Double.valueOf(Double.NaN));
|
||||
PRIMITIVE_TO_NULL.put(Character.TYPE, Character.valueOf('\u0000'));
|
||||
}
|
||||
|
||||
public static Class<?> getBoxedClass(Class<?> primitiveClass) {
|
||||
return PRIMITIVE_TO_BOXED.getOrDefault(primitiveClass, primitiveClass);
|
||||
}
|
||||
|
||||
public static Object getPrimitiveNull(Class<?> primitiveClass) {
|
||||
return PRIMITIVE_TO_NULL.get(primitiveClass);
|
||||
}
|
||||
|
||||
}
|
1210
src/main/java/ru/windcorp/jputil/SyncStreams.java
Normal file
1210
src/main/java/ru/windcorp/jputil/SyncStreams.java
Normal file
File diff suppressed because it is too large
Load Diff
44
src/main/java/ru/windcorp/jputil/SyntaxException.java
Normal file
44
src/main/java/ru/windcorp/jputil/SyntaxException.java
Normal file
@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
128
src/main/java/ru/windcorp/jputil/chars/CharArrayIterator.java
Normal file
128
src/main/java/ru/windcorp/jputil/chars/CharArrayIterator.java
Normal file
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
42
src/main/java/ru/windcorp/jputil/chars/CharConsumer.java
Normal file
42
src/main/java/ru/windcorp/jputil/chars/CharConsumer.java
Normal file
@ -0,0 +1,42 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
69
src/main/java/ru/windcorp/jputil/chars/CharConsumers.java
Normal file
69
src/main/java/ru/windcorp/jputil/chars/CharConsumers.java
Normal file
@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
84
src/main/java/ru/windcorp/jputil/chars/CharPredicate.java
Normal file
84
src/main/java/ru/windcorp/jputil/chars/CharPredicate.java
Normal file
@ -0,0 +1,84 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
35
src/main/java/ru/windcorp/jputil/chars/CharSupplier.java
Normal file
35
src/main/java/ru/windcorp/jputil/chars/CharSupplier.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
44
src/main/java/ru/windcorp/jputil/chars/EscapeException.java
Normal file
44
src/main/java/ru/windcorp/jputil/chars/EscapeException.java
Normal file
@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
474
src/main/java/ru/windcorp/jputil/chars/Escaper.java
Normal file
474
src/main/java/ru/windcorp/jputil/chars/Escaper.java
Normal file
@ -0,0 +1,474 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package ru.windcorp.jputil.chars;
|
||||
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
|
||||
public class FancyCharacterIterator implements CharacterIterator {
|
||||
private final StringCharacterIterator obj;
|
||||
private final String data;
|
||||
|
||||
public FancyCharacterIterator(String data) {
|
||||
this.obj = new StringCharacterIterator(data);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char first() {
|
||||
return obj.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char last() {
|
||||
return obj.last();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char setIndex(int p) {
|
||||
return obj.setIndex(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char current() {
|
||||
return obj.current();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char next() {
|
||||
return obj.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char previous() {
|
||||
return obj.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBeginIndex() {
|
||||
return obj.getBeginIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndIndex() {
|
||||
return obj.getEndIndex();
|
||||
}
|
||||
|
||||
@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(' ');
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
778
src/main/java/ru/windcorp/jputil/chars/StringUtil.java
Normal file
778
src/main/java/ru/windcorp/jputil/chars/StringUtil.java
Normal file
@ -0,0 +1,778 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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.function.IntFunction;
|
||||
|
||||
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 <T> 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 <T> String arrayToString(T[] array, String separator) {
|
||||
return arrayToString(array, separator, EMPTY_PLACEHOLDER, NULL_PLACEHOLDER, "[null array]");
|
||||
}
|
||||
|
||||
public static <T> 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 <T> String supplierToString(
|
||||
IntFunction<T> 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 <T> String supplierToStringExactly(
|
||||
IntFunction<T> 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 <T> String supplierToStringUntilNull(
|
||||
IntFunction<T> 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;
|
||||
}
|
||||
|
||||
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.<p>
|
||||
* Examples:<p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th align="center"><code>src</code></th>
|
||||
* <th align="center"><code>target</code></th>
|
||||
* <th align="center"><code>skip</code></th>
|
||||
* <th align="center">returns</th>
|
||||
* </tr>
|
||||
* <tr align="center"><td><code>a<u>.</u>b.c</code></td><td><code>'.'</code></td><td><code>0</code></td><td><code>1</code></td></tr>
|
||||
* <tr align="center"><td><code>a.b<u>.</u>c</code></td><td><code>'.'</code></td><td><code>1</code></td><td><code>3</code></td></tr>
|
||||
* <tr align="center"><td><code>a.b.c</code></td><td><code>'.'</code></td><td><code>2</code></td><td><code>-1</code></td></tr>
|
||||
* <tr align="center"><td><code>a.b.c</code></td><td><code>'d'</code></td><td><i>any</i></td><td><code>-1</code></td></tr>
|
||||
* </table>
|
||||
* @param src - the array to search in.
|
||||
* @param target - the character to search for.
|
||||
* @param skip - the amount of <code>target</code> characters to be skipped.
|
||||
* @return The index of the <code>skip+1</code>th <code>target</code> 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 <code>src.length - 1</code>.<p>
|
||||
* Examples:<p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th align="center"><code>src</code></th>
|
||||
* <th align="center"><code>target</code></th>
|
||||
* <th align="center"><code>skip</code></th>
|
||||
* <th align="center">returns</th>
|
||||
* </tr>
|
||||
* <tr align="center"><td><code>a.b<u>.</u>c</code></td><td><code>'.'</code></td><td><code>0</code></td><td><code>3</code></td></tr>
|
||||
* <tr align="center"><td><code>a<u>.</u>b.c</code></td><td><code>'.'</code></td><td><code>1</code></td><td><code>1</code></td></tr>
|
||||
* <tr align="center"><td><code>a.b.c</code></td><td><code>'.'</code></td><td><code>2</code></td><td><code>-1</code></td></tr>
|
||||
* <tr align="center"><td><code>a.b.c</code></td><td><code>'d'</code></td><td><i>any</i></td><td><code>-1</code></td></tr>
|
||||
* </table>
|
||||
* @param src - the array to search in.
|
||||
* @param target - the character to search for.
|
||||
* @param skip - the amount of <code>target</code> characters to be skipped.
|
||||
* @return The index of the <code>skip+1</code>th <code>target</code>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 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<String> allCombinations(Iterable<String>... parts) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Collection<String> result = new ArrayList<>();
|
||||
buildCombinations(sb, result, parts, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void buildCombinations(StringBuilder sb, Collection<String> result, Iterable<String>[] 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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
139
src/main/java/ru/windcorp/jputil/chars/WordReader.java
Normal file
139
src/main/java/ru/windcorp/jputil/chars/WordReader.java
Normal file
@ -0,0 +1,139 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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<String> {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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 <code>[0; bufferNextIndex)</code>.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
270
src/main/java/ru/windcorp/jputil/chars/reader/CharReader.java
Normal file
270
src/main/java/ru/windcorp/jputil/chars/reader/CharReader.java
Normal file
@ -0,0 +1,270 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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 <code>"\n"</code>, <code>"\r"</code>
|
||||
* and <code>"\r\n"</code> 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;
|
||||
}
|
||||
|
||||
}
|
112
src/main/java/ru/windcorp/jputil/chars/reader/CharReaders.java
Normal file
112
src/main/java/ru/windcorp/jputil/chars/reader/CharReaders.java
Normal file
@ -0,0 +1,112 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.functions;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingBiConsumer<T, U, E extends Exception> {
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface BiConsumerHandler<T, U, E extends Exception> {
|
||||
void handle(T t, U u, E e);
|
||||
}
|
||||
|
||||
void accept(T t, U u) throws E;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default BiConsumer<T, U> 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 <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> 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 <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> 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 <T, U, E extends Exception> ThrowingBiConsumer<T, U, E> 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);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.functions;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingConsumer<T, E extends Exception> {
|
||||
|
||||
void accept(T t) throws E;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default Consumer<T> 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 <T, E extends Exception> ThrowingConsumer<T, E> concat(ThrowingConsumer<? super T, ? extends E> first, ThrowingConsumer<? super T, ? extends E> second) {
|
||||
return t -> {
|
||||
first.accept(t);
|
||||
second.accept(t);
|
||||
};
|
||||
}
|
||||
|
||||
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(Consumer<? super T> first, ThrowingConsumer<? super T, ? extends E> second) {
|
||||
return t -> {
|
||||
first.accept(t);
|
||||
second.accept(t);
|
||||
};
|
||||
}
|
||||
|
||||
public static <T, E extends Exception> ThrowingConsumer<T, E> concat(ThrowingConsumer<? super T, ? extends E> first, Consumer<? super T> second) {
|
||||
return t -> {
|
||||
first.accept(t);
|
||||
second.accept(t);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.functions;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingFunction<T, R, E extends Exception> {
|
||||
|
||||
R apply(T t) throws E;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default Function<T, R> 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<T, R> withHandler(BiConsumer<? super T, ? super E> handler, Supplier<? extends R> value) {
|
||||
return withHandler(handler, t -> value.get());
|
||||
}
|
||||
|
||||
default Function<T, R> withHandler(BiConsumer<? super T, ? super E> handler, R value) {
|
||||
return withHandler(handler, t -> value);
|
||||
}
|
||||
|
||||
default Function<T, R> withHandler(BiConsumer<? super T, ? super E> handler) {
|
||||
return withHandler(handler, (Function<T, R>) null);
|
||||
}
|
||||
|
||||
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> 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 <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
|
||||
Function<? super T, I> first,
|
||||
ThrowingFunction<? super I, ? extends R, E> second) {
|
||||
return t -> second.apply(first.apply(t));
|
||||
}
|
||||
|
||||
public static <T, R, I, E extends Exception> ThrowingFunction<T, R, E> compose(
|
||||
ThrowingFunction<? super T, I, E> first,
|
||||
Function<? super I, ? extends R> second) {
|
||||
return t -> second.apply(first.apply(t));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.functions;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingRunnable<E extends Exception> {
|
||||
|
||||
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 <E extends Exception> ThrowingRunnable<E> concat(
|
||||
ThrowingRunnable<? extends E> first,
|
||||
ThrowingRunnable<? extends E> second
|
||||
) {
|
||||
return () -> {
|
||||
first.run();
|
||||
second.run();
|
||||
};
|
||||
}
|
||||
|
||||
public static <E extends Exception> ThrowingRunnable<E> concat(Runnable first, ThrowingRunnable<E> second) {
|
||||
return () -> {
|
||||
first.run();
|
||||
second.run();
|
||||
};
|
||||
}
|
||||
|
||||
public static <E extends Exception> ThrowingRunnable<E> concat(ThrowingRunnable<E> first, Runnable second) {
|
||||
return () -> {
|
||||
first.run();
|
||||
second.run();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*******************************************************************************
|
||||
* Progressia
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
* 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
|
||||
@ -15,49 +15,36 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.progressia.common.util;
|
||||
package ru.windcorp.jputil.functions;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingRunnable<T extends Throwable> {
|
||||
public interface ThrowingSupplier<T, E extends Exception> {
|
||||
|
||||
void run() throws T;
|
||||
T get() throws E;
|
||||
|
||||
default Runnable withCatcher(
|
||||
Consumer<T> catcher,
|
||||
Class<T> throwableClass
|
||||
) {
|
||||
@SuppressWarnings("unchecked")
|
||||
default Supplier<T> withHandler(Consumer<? super E> handler, Supplier<? extends T> value) {
|
||||
return () -> {
|
||||
|
||||
try {
|
||||
ThrowingRunnable.this.run();
|
||||
} catch (Throwable t) {
|
||||
if (t.getClass() == throwableClass) {
|
||||
catcher.accept(throwableClass.cast(t));
|
||||
}
|
||||
|
||||
Throwables.throwIfUnchecked(t);
|
||||
|
||||
// This should never happen
|
||||
throw new AssertionError("This should not have been thrown", t);
|
||||
return get();
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
if (handler != null) handler.accept((E) e);
|
||||
return value == null ? null : value.get();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
default Runnable withCatcher(
|
||||
Consumer<Throwable> catcher
|
||||
) {
|
||||
return () -> {
|
||||
try {
|
||||
ThrowingRunnable.this.run();
|
||||
} catch (Throwable t) {
|
||||
catcher.accept(t);
|
||||
}
|
||||
};
|
||||
default Supplier<T> withHandler(Consumer<? super E> handler, T value) {
|
||||
return withHandler(handler, () -> value);
|
||||
}
|
||||
|
||||
default Supplier<T> withHandler(Consumer<? super E> handler) {
|
||||
return withHandler(handler, (Supplier<T>) null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.iterators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class ArrayIterator<E> implements Iterator<E> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.iterators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author Javapony
|
||||
*
|
||||
*/
|
||||
public class FunctionIterator<T, E> implements Iterator<E> {
|
||||
|
||||
private final Iterator<T> parent;
|
||||
private final Function<T, E> function;
|
||||
|
||||
public FunctionIterator(Iterator<T> parent, Function<T, E> function) {
|
||||
this.parent = parent;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Iterator#hasNext()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return parent.hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Iterator#next()
|
||||
*/
|
||||
@Override
|
||||
public E next() {
|
||||
return function.apply(parent.next());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
@Override
|
||||
public void remove() {
|
||||
parent.remove();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.iterators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class PeekingIterator<E> implements Iterator<E> {
|
||||
|
||||
private final Iterator<? extends E> source;
|
||||
private E next = null;
|
||||
|
||||
public PeekingIterator(Iterator<? extends E> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null || source.hasNext();
|
||||
}
|
||||
|
||||
public E peek() {
|
||||
if (next == null) {
|
||||
if (source.hasNext()) {
|
||||
next = source.next();
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
// SonarLint: "Iterator.next()" methods should throw "NoSuchElementException" (java:S2272)
|
||||
// peek() throws NoSuchElementException as expected
|
||||
@SuppressWarnings("squid:S2272")
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
E element = peek();
|
||||
next = null;
|
||||
return element;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.iterators;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class RangeIterator<E> implements Iterator<E> {
|
||||
|
||||
private final Iterator<E> parent;
|
||||
private final int from;
|
||||
private final int amount;
|
||||
|
||||
private int nextIndex = 0;
|
||||
|
||||
public RangeIterator(Iterator<E> iterator, int from, int amount) {
|
||||
this.parent = iterator;
|
||||
this.from = from;
|
||||
this.amount = amount < 0 ? Integer.MAX_VALUE : amount;
|
||||
}
|
||||
|
||||
public RangeIterator(Iterator<E> iterator, int from) {
|
||||
this(iterator, from, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
update();
|
||||
return nextIndex < from + amount && parent.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
update();
|
||||
if (nextIndex >= from + amount) {
|
||||
throw new NoSuchElementException("RangeIterator about to retrieve element " + nextIndex
|
||||
+ " which exceeds upper boundary " + (from + amount));
|
||||
}
|
||||
|
||||
E result = parent.next();
|
||||
nextIndex++;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void update() {
|
||||
while (nextIndex < from && parent.hasNext()) {
|
||||
parent.next();
|
||||
nextIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
70
src/main/java/ru/windcorp/jputil/iterators/Reiterator.java
Normal file
70
src/main/java/ru/windcorp/jputil/iterators/Reiterator.java
Normal file
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.iterators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class Reiterator<E> implements Iterable<E> {
|
||||
|
||||
private class ReiteratorIterator implements Iterator<E> {
|
||||
|
||||
int index = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
synchronized (source) {
|
||||
if (index >= data.size()) {
|
||||
if (!source.hasNext()) {
|
||||
return false;
|
||||
} else {
|
||||
data.add(source.next());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
E result;
|
||||
synchronized (source) {
|
||||
if (!hasNext()) throw new NoSuchElementException();
|
||||
result = data.get(index);
|
||||
}
|
||||
index++;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Iterator<E> source;
|
||||
private final ArrayList<E> data = new ArrayList<>();
|
||||
|
||||
public Reiterator(Iterator<E> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new ReiteratorIterator();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
public abstract class AbstractSelectorOperator implements SelectorOperator {
|
||||
|
||||
private final String[] names;
|
||||
|
||||
public AbstractSelectorOperator(String[] names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesName(String name) {
|
||||
for (String n : names) {
|
||||
if (n.equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import ru.windcorp.jputil.SyntaxException;
|
||||
import ru.windcorp.jputil.chars.StringUtil;
|
||||
|
||||
public abstract class NamedParameterizedSelector<T> extends NamedSelector<T> {
|
||||
|
||||
private final char separator;
|
||||
private String givenName;
|
||||
|
||||
public NamedParameterizedSelector(char separator, String... names) {
|
||||
super(names);
|
||||
this.separator = separator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Selector<T> derive(String name) throws SyntaxException {
|
||||
String[] parts = StringUtil.split(name, separator, 2);
|
||||
|
||||
if (parts[1] == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!matchesName(parts[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NamedParameterizedSelector<T> selector = deriveImpl(parts[1]);
|
||||
selector.givenName = name;
|
||||
return selector;
|
||||
}
|
||||
|
||||
protected abstract NamedParameterizedSelector<T> deriveImpl(String param) throws SyntaxException;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return givenName;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import ru.windcorp.jputil.SyntaxException;
|
||||
|
||||
public abstract class NamedSelector<T> implements Selector<T> {
|
||||
|
||||
private final String[] names;
|
||||
|
||||
public NamedSelector(String... names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
public boolean matchesName(String name) {
|
||||
for (String n : names) {
|
||||
if (n.equalsIgnoreCase(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Selector<T> derive(String name) throws SyntaxException {
|
||||
return matchesName(name) ? this : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return names[0];
|
||||
}
|
||||
|
||||
}
|
36
src/main/java/ru/windcorp/jputil/selectors/OperatorAnd.java
Normal file
36
src/main/java/ru/windcorp/jputil/selectors/OperatorAnd.java
Normal file
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OperatorAnd extends AbstractSelectorOperator {
|
||||
|
||||
public OperatorAnd(String... names) {
|
||||
super(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void process(Deque<Predicate<T>> stack) {
|
||||
Predicate<T> arg2 = stack.pop();
|
||||
Predicate<T> arg1 = stack.pop();
|
||||
stack.push(obj -> arg1.test(obj) && arg2.test(obj));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OperatorExclude extends AbstractSelectorOperator {
|
||||
|
||||
public OperatorExclude(String... names) {
|
||||
super(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void process(Deque<Predicate<T>> stack) {
|
||||
Predicate<T> arg2 = stack.pop();
|
||||
Predicate<T> arg1 = stack.pop();
|
||||
stack.push(obj -> arg1.test(obj) && !arg2.test(obj));
|
||||
}
|
||||
|
||||
}
|
34
src/main/java/ru/windcorp/jputil/selectors/OperatorNot.java
Normal file
34
src/main/java/ru/windcorp/jputil/selectors/OperatorNot.java
Normal file
@ -0,0 +1,34 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OperatorNot extends AbstractSelectorOperator {
|
||||
|
||||
public OperatorNot(String... names) {
|
||||
super(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void process(Deque<Predicate<T>> stack) {
|
||||
stack.push(stack.pop().negate());
|
||||
}
|
||||
|
||||
}
|
36
src/main/java/ru/windcorp/jputil/selectors/OperatorOr.java
Normal file
36
src/main/java/ru/windcorp/jputil/selectors/OperatorOr.java
Normal file
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OperatorOr extends AbstractSelectorOperator {
|
||||
|
||||
public OperatorOr(String... names) {
|
||||
super(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void process(Deque<Predicate<T>> stack) {
|
||||
Predicate<T> arg2 = stack.pop();
|
||||
Predicate<T> arg1 = stack.pop();
|
||||
stack.push(obj -> arg1.test(obj) || arg2.test(obj));
|
||||
}
|
||||
|
||||
}
|
36
src/main/java/ru/windcorp/jputil/selectors/OperatorXor.java
Normal file
36
src/main/java/ru/windcorp/jputil/selectors/OperatorXor.java
Normal file
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OperatorXor extends AbstractSelectorOperator {
|
||||
|
||||
public OperatorXor(String... names) {
|
||||
super(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void process(Deque<Predicate<T>> stack) {
|
||||
Predicate<T> arg2 = stack.pop();
|
||||
Predicate<T> arg1 = stack.pop();
|
||||
stack.push(obj -> arg1.test(obj) != arg2.test(obj));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PredicateWrapper<T> extends NamedSelector<T> {
|
||||
|
||||
private final Predicate<? super T> predicate;
|
||||
|
||||
public PredicateWrapper(String name, Predicate<? super T> predicate) {
|
||||
super(name);
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(T obj) {
|
||||
return predicate.test(obj);
|
||||
}
|
||||
|
||||
}
|
28
src/main/java/ru/windcorp/jputil/selectors/Selector.java
Normal file
28
src/main/java/ru/windcorp/jputil/selectors/Selector.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import ru.windcorp.jputil.SyntaxException;
|
||||
|
||||
public interface Selector<T> extends Predicate<T> {
|
||||
|
||||
public Selector<T> derive(String name) throws SyntaxException;
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface SelectorOperator {
|
||||
|
||||
public <T> void process(Deque<Predicate<T>> stack);
|
||||
|
||||
public boolean matchesName(String name);
|
||||
|
||||
}
|
176
src/main/java/ru/windcorp/jputil/selectors/SelectorSystem.java
Normal file
176
src/main/java/ru/windcorp/jputil/selectors/SelectorSystem.java
Normal file
@ -0,0 +1,176 @@
|
||||
/*******************************************************************************
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.jputil.selectors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import ru.windcorp.jputil.SyntaxException;
|
||||
import ru.windcorp.jputil.iterators.PeekingIterator;
|
||||
|
||||
public class SelectorSystem<T> {
|
||||
|
||||
public static final char EXPRESSION_OPEN = '(';
|
||||
public static final char EXPRESSION_CLOSE = ')';
|
||||
|
||||
private final Collection<Selector<T>> selectors =
|
||||
Collections.synchronizedCollection(new ArrayList<Selector<T>>());
|
||||
|
||||
private final Collection<SelectorOperator> operators =
|
||||
Collections.synchronizedCollection(new ArrayList<SelectorOperator>());
|
||||
|
||||
private String stackPrefix = null;
|
||||
|
||||
public Collection<Selector<T>> getSelectors() {
|
||||
return this.selectors;
|
||||
}
|
||||
|
||||
public Collection<SelectorOperator> getSelectorOperators() {
|
||||
return this.operators;
|
||||
}
|
||||
|
||||
public String getStackPrefix() {
|
||||
return stackPrefix;
|
||||
}
|
||||
|
||||
public SelectorSystem<T> setStackPrefix(String stackPrefix) {
|
||||
this.stackPrefix = stackPrefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectorSystem<T> add(Selector<T> selector) {
|
||||
getSelectors().add(selector);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectorSystem<T> add(SelectorOperator operator) {
|
||||
getSelectorOperators().add(operator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Predicate<T> parse(Iterator<String> tokens) throws SyntaxException {
|
||||
PeekingIterator<String> peeker = new PeekingIterator<>(tokens);
|
||||
|
||||
if (getStackPrefix() != null && peeker.hasNext() && getStackPrefix().equals(peeker.peek())) {
|
||||
peeker.next();
|
||||
return parseStack(peeker);
|
||||
}
|
||||
|
||||
Deque<Predicate<T>> stack = new LinkedList<>();
|
||||
|
||||
synchronized (getSelectorOperators()) {
|
||||
synchronized (getSelectors()) {
|
||||
|
||||
while (peeker.hasNext()) {
|
||||
parseToken(stack, peeker);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return compress(stack);
|
||||
}
|
||||
|
||||
private void parseToken(Deque<Predicate<T>> stack, Iterator<String> tokens) throws SyntaxException {
|
||||
|
||||
if (!tokens.hasNext()) {
|
||||
throw new SyntaxException("Not enough tokens");
|
||||
}
|
||||
String token = tokens.next();
|
||||
|
||||
for (SelectorOperator operator : getSelectorOperators()) {
|
||||
if (operator.matchesName(token.toLowerCase())) {
|
||||
parseToken(stack, tokens);
|
||||
operator.process(stack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Selector<T> tmp;
|
||||
for (Selector<T> selector : getSelectors()) {
|
||||
if ((tmp = selector.derive(token)) != null) {
|
||||
stack.push(tmp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new SyntaxException("Unknown token \"" + token + "\"");
|
||||
}
|
||||
|
||||
public Predicate<T> parseStack(Iterator<String> tokens) throws SyntaxException {
|
||||
Deque<Predicate<T>> stack = new LinkedList<>();
|
||||
|
||||
String token;
|
||||
|
||||
synchronized (getSelectorOperators()) {
|
||||
synchronized (getSelectors()) {
|
||||
|
||||
tokenCycle:
|
||||
while (tokens.hasNext()) {
|
||||
token = tokens.next();
|
||||
|
||||
for (SelectorOperator operator : getSelectorOperators()) {
|
||||
if (operator.matchesName(token.toLowerCase())) {
|
||||
operator.process(stack);
|
||||
continue tokenCycle;
|
||||
}
|
||||
}
|
||||
|
||||
for (Selector<T> selector : getSelectors()) {
|
||||
Selector<T> tmp;
|
||||
if ((tmp = selector.derive(token)) != null) {
|
||||
stack.push(tmp);
|
||||
continue tokenCycle;
|
||||
}
|
||||
}
|
||||
|
||||
throw new SyntaxException("Unknown token \"" + token + "\"");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return compress(stack);
|
||||
}
|
||||
|
||||
private Predicate<T> compress(Deque<Predicate<T>> stack) throws SyntaxException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new SyntaxException("Stack is empty");
|
||||
}
|
||||
|
||||
if (stack.size() == 1) {
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
return obj -> {
|
||||
for (Predicate<? super T> predicate : stack) {
|
||||
if (predicate.test(obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import ru.windcorp.progressia.common.util.ThrowingRunnable;
|
||||
import ru.windcorp.jputil.functions.ThrowingRunnable;
|
||||
|
||||
public class RenderTaskQueue {
|
||||
|
||||
@ -46,9 +46,9 @@ public class RenderTaskQueue {
|
||||
private static final Object WAIT_AND_INVOKE_MONITOR = new Object();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Throwable> void waitAndInvoke(
|
||||
ThrowingRunnable<T> task
|
||||
) throws InterruptedException, T {
|
||||
public static <E extends Exception> void waitAndInvoke(
|
||||
ThrowingRunnable<E> task
|
||||
) throws InterruptedException, E {
|
||||
|
||||
if (GraphicsInterface.isRenderThread()) {
|
||||
task.run();
|
||||
@ -91,7 +91,7 @@ public class RenderTaskQueue {
|
||||
throw (Error) thrown;
|
||||
}
|
||||
|
||||
throw (T) thrown; // Guaranteed
|
||||
throw (E) thrown; // Guaranteed
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user