diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..59f02e8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+
+# uid.tools
+
+pom.xml:
+
+1. in repositories section:
+
+
+ ua.net.uid.releases
+ https://git.uid.net.ua/maven/releases/
+
+
+2. in dependencies section:
+
+
+ ua.net.uid
+ uid.tools
+ 1.0.0
+
diff --git a/nb-configuration.xml b/nb-configuration.xml
new file mode 100644
index 0000000..9910669
--- /dev/null
+++ b/nb-configuration.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ apache20
+ JDK_1.8
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6279da6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,103 @@
+
+
+ 4.0.0
+ ua.net.uid
+ uid.tools
+ 1.0.0-SNAPSHOT
+ jar
+
+
+
+ ASL
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.5.1
+ test
+
+
+
+ com.h2database
+ h2
+ [1.4.193, 1.5.0)
+ test
+
+
+
+
+
+
+ .
+
+ LICENSE
+ NOTICE.txt
+ RELEASE.txt
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 1.8
+ 1.8
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
+
+ maven-failsafe-plugin
+ 2.22.2
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.5.3
+
+ true
+ @{project.version}
+ release
+
+
+
+
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+ scm:git:https://git.uid.net.ua/git/uid/uid.tools.git
+ scm:git:https://git.uid.net.ua/git/uid/uid.tools.git
+ https://git.uid.net.ua/git/uid/uid.tools.git
+ HEAD
+
+
+
+
+ uid-releases
+ Release repository
+ https://git.uid.net.ua/maven/releases/
+
+
+ uid-snapshots
+ Snapshots repository
+ https://git.uid.net.ua/maven/snapshots/
+
+
+
\ No newline at end of file
diff --git a/src/main/java/ua/net/uid/utils/Function.java b/src/main/java/ua/net/uid/utils/Function.java
new file mode 100644
index 0000000..804999c
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/Function.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils;
+
+/**
+ *
+ * @author nightfall
+ * @param
+ * @param
+ */
+@FunctionalInterface
+public interface Function {
+ R call(T param) throws Exception;
+}
diff --git a/src/main/java/ua/net/uid/utils/Getter.java b/src/main/java/ua/net/uid/utils/Getter.java
new file mode 100644
index 0000000..af3d800
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/Getter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils;
+
+/**
+ *
+ * @author nightfall
+ * @param argument type
+ * @param result type
+ */
+public interface Getter {
+ V get(K key);
+}
diff --git a/src/main/java/ua/net/uid/utils/Procedure.java b/src/main/java/ua/net/uid/utils/Procedure.java
new file mode 100644
index 0000000..4752c9e
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/Procedure.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils;
+
+/**
+ *
+ * @author nightfall
+ * @param argument type
+ */
+@FunctionalInterface
+public interface Procedure {
+ void call(T param) throws Exception;
+}
diff --git a/src/main/java/ua/net/uid/utils/Setter.java b/src/main/java/ua/net/uid/utils/Setter.java
new file mode 100644
index 0000000..a426d3f
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/Setter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils;
+
+/**
+ *
+ * @author nightfall
+ * @param
+ * @param
+ */
+public interface Setter {
+ void set(K name, V value);
+}
diff --git a/src/main/java/ua/net/uid/utils/concurrent/ReadyFuture.java b/src/main/java/ua/net/uid/utils/concurrent/ReadyFuture.java
new file mode 100644
index 0000000..e3b3878
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/concurrent/ReadyFuture.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.concurrent;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+public final class ReadyFuture implements Future {
+ private final V result;
+
+ public ReadyFuture(V result) {
+ this.result = result;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public V get() {
+ return result;
+ }
+
+ @Override
+ public V get(long timeout, TimeUnit unit) {
+ return result;
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/ArrayHelper.java b/src/main/java/ua/net/uid/utils/helpers/ArrayHelper.java
new file mode 100644
index 0000000..6f87c7b
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/ArrayHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+public class ArrayHelper {
+ private ArrayHelper() {
+ }
+
+ public static int indexOf(final T[] array, final T item) {
+ return indexOf(array, item, 0);
+ }
+
+ public static int indexOf(final T[] array, final T item, int from) {
+ if (array == null) return -1;
+ if (from < 0) from = 0;
+ final int length = array.length;
+ if (from >= length) return -1;
+ if (item == null) {
+ for (int i = from; i < length; ++i) {
+ if (array[i] == null) return i;
+ }
+ } else {
+ for (int i = from; i < length; ++i) {
+ if (item.equals(array[i])) return i;
+ }
+ }
+ return -1;
+ }
+
+ public static int lastIndexOf(final T[] array, final T item) {
+ return lastIndexOf(array, item, Integer.MAX_VALUE);
+ }
+
+ public static int lastIndexOf(final T[] array, final T item, int from) {
+ if (array == null || from < 0) return -1;
+ if (from >= array.length) from = array.length - 1;
+ if (item == null) {
+ for (int i = from; i >= 0; --i) {
+ if (array[i] == null) return i;
+ }
+ } else {
+ for (int i = from; i >= 0; --i) {
+ if (item.equals(array[i])) return i;
+ }
+ }
+ return -1;
+ }
+
+ /*
+ !!! not compiled in java 9
+ @SuppressWarnings("unchecked")
+ public static T[] toArray(Collection extends T> collection) {
+ return (T[]) collection.toArray();
+ }
+
+ public static T[] toSortedArray(Collection collection) {
+ final T[] result = toArray(collection);
+ Arrays.sort(result);
+ return result;
+ }
+
+ public static T[] toSortedArray(Collection collection, Comparator super T> comparator) {
+ final T[] result = toArray(collection);
+ Arrays.sort(result, comparator);
+ return result;
+ }
+ */
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/Cast.java b/src/main/java/ua/net/uid/utils/helpers/Cast.java
new file mode 100644
index 0000000..f646a9e
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/Cast.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Cast {
+ private static final Pattern REGEXP_BOOLEAN = Pattern.compile("^\\s*(false|f|no|off|n|0+|-)|(true|t|yes|on|y|\\d+|\\+)\\s*$", Pattern.CASE_INSENSITIVE);
+ private static final Pattern REGEXP_EMAIL = Pattern.compile("^\\s*([-a-z0-9~!$%^&*_=+}{'?]+(\\.[-a-z0-9~!$%^&*_=+}{'?]+)*@([a-z0-9_][-a-z0-9_]*(\\.[-a-z0-9_]+)*))\\s*$", Pattern.CASE_INSENSITIVE);
+
+ private Cast() {
+ }
+
+ public static Boolean toBoolean(final String text, Boolean defaults) {
+ if (text != null) {
+ Matcher matcher = REGEXP_BOOLEAN.matcher(text);
+ if (matcher.matches()) return matcher.group(2) != null;
+ }
+ return defaults;
+ }
+
+ public static Byte toByte(final String value, final Byte defaults) {
+ if (value != null) {
+ try {
+ return Byte.decode(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static Short toShort(final String value, final Short defaults) {
+ if (value != null) {
+ try {
+ return Short.decode(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static Integer toInteger(final String value, final Integer defaults) {
+ if (value != null) {
+ try {
+ return Integer.decode(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static Long toLong(final String value, final Long defaults) {
+ if (value != null) {
+ try {
+ return Long.decode(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static Float toFloat(final String value, final Float defaults) {
+ if (value != null) {
+ try {
+ return Float.parseFloat(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static Double toDouble(final String value, final Double defaults) {
+ if (value != null) {
+ try {
+ return Double.parseDouble(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static BigInteger toBigInteger(String value, BigInteger defaults) {
+ if (value != null) {
+ try {
+ return new BigInteger(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static BigDecimal toBigDecimal(String value, BigDecimal defaults) {
+ if (value != null) {
+ try {
+ return new BigDecimal(value);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ return defaults;
+ }
+
+ public static String toEmail(String value, final String defaults) {
+ if (value != null) {
+ Matcher matcher = REGEXP_EMAIL.matcher(value);
+ if (matcher.matches()) return matcher.group(1);
+ }
+ return defaults;
+ }
+
+ public static Date toDate(DateFormat format, String value, Date defValue) {
+ if (value != null) {
+ try {
+ return format.parse(value);
+ } catch (ParseException ignored) {
+ }
+ }
+ return defValue;
+ }
+
+ public static > T toEnum(Class enumType, String value, T defaults) {
+ return EnumHelper.valueOf(enumType, value, defaults);
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/CommonHelper.java b/src/main/java/ua/net/uid/utils/helpers/CommonHelper.java
new file mode 100644
index 0000000..4ca0bd6
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/CommonHelper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class CommonHelper {
+ private CommonHelper() {
+ }
+
+ public static void close(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (final Exception ignored) {
+ }
+ }
+ }
+
+ public static boolean isEmpty(CharSequence chars) {
+ return chars == null || chars.length() == 0;
+ }
+
+ public static boolean isEmpty(String string) {
+ return string == null || string.isEmpty();
+ }
+
+ public static boolean isEmpty(Collection> collection) {
+ return collection == null || collection.isEmpty();
+ }
+
+ public static boolean isEmpty(Map, ?> map) {
+ return map == null || map.isEmpty();
+ }
+
+ public static boolean isEmpty(T[] array) {
+ return array == null || array.length == 0;
+ }
+
+ public static Set setOf(T ... items) {
+ Set result = new HashSet<>();
+ if (!isEmpty(items))
+ Collections.addAll(result, items);
+ return result;
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/EnumHelper.java b/src/main/java/ua/net/uid/utils/helpers/EnumHelper.java
new file mode 100644
index 0000000..2735fd1
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/EnumHelper.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class EnumHelper {
+ private EnumHelper() {
+ }
+
+ public static > T valueOf(Class enumType, String value, T defValue) {
+ if (value == null) return defValue;
+ try {
+ return Enum.valueOf(enumType, value);
+ } catch (IllegalArgumentException ex) {
+ return defValue;
+ }
+ }
+
+ public static > T valueOf(Class enumType, String value) {
+ return valueOf(enumType, value, null);
+ }
+
+ public static > List toList(final Class enumType) {
+ return Arrays.asList(enumType.getEnumConstants());
+ }
+
+ public static > Map toMap(final Class enumType) {
+ Map map = new LinkedHashMap<>();
+ for (T e : enumType.getEnumConstants())
+ map.put(e.name(), e);
+ return map;
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/NumberHelper.java b/src/main/java/ua/net/uid/utils/helpers/NumberHelper.java
new file mode 100644
index 0000000..e8224aa
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/NumberHelper.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+public class NumberHelper {
+
+ private NumberHelper() {
+ }
+
+ public static byte[] longToBytes(long l) {
+ byte[] result = new byte[8];
+ for (int i = 7; i >= 0; --i) {
+ result[i] = (byte) (l & 0xff);
+ l >>= 8;
+ }
+ return result;
+ }
+
+ public static long bytesToLong(byte[] b) {
+ long result = 0;
+ for (int i = 0; i < 8; i++) {
+ result <<= 8;
+ result |= (b[i] & 0xFF);
+ }
+ return result;
+ }
+
+
+ /*public static Number parse(CharSequence source, ParsePosition position) {
+ int offset = StringHelper.skipWhitespace(source, position.getIndex());
+ if (offset < source.length()) {
+
+ }
+ position.setErrorIndex(offset);
+ return null;
+ }*/
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/RandomHelper.java b/src/main/java/ua/net/uid/utils/helpers/RandomHelper.java
new file mode 100644
index 0000000..4c2c64b
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/RandomHelper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class RandomHelper {
+ public static char[] randomChars(int minLength, int maxLength) {
+ return randomChars(minLength, maxLength, ThreadLocalRandom.current());
+ }
+
+ public static char[] randomChars(int minLength, int maxLength, Random random) {
+ assert minLength <= maxLength;
+ int length = minLength == maxLength ? minLength : minLength + random.nextInt(maxLength - minLength);
+ char[] text = new char[length];
+ for (int i = 0; i < length; ++i) {
+ text[i] = (char) (random.nextInt(126 - 32) + 32);
+ }
+ return text;
+ }
+
+ public static String randomString(int minLength, int maxLength) {
+ return new String(randomChars(minLength, maxLength));
+ }
+
+ public static String randomString(int minLength, int maxLength, Random random) {
+ return new String(randomChars(minLength, maxLength, random));
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/RegexHelper.java b/src/main/java/ua/net/uid/utils/helpers/RegexHelper.java
new file mode 100644
index 0000000..67071b9
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/RegexHelper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexHelper {
+ private static final Pattern NAMED_GROUP = Pattern.compile("(?:[^\\\\]+|(?:\\\\.)+)?\\(\\?<([a-z][a-z0-9]*)>");
+
+ private RegexHelper() {
+ }
+
+
+ public static Set getNamedGroupsSet(Pattern pattern) {
+ HashSet result = new HashSet<>();
+ Matcher matcher = NAMED_GROUP.matcher(pattern.pattern());
+ while (matcher.find()) {
+ result.add(matcher.group(1));
+ }
+ return result;
+ }
+
+ public static Map getNamedGroups(Pattern pattern) {
+ /*
+ !!! does not work in java 9
+ try {
+ Method method = Pattern.class.getDeclaredMethod("namedGroups");
+ method.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map groups = (Map) method.invoke(pattern);
+ return Collections.unmodifiableMap(groups);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+ */
+ throw new UnsupportedOperationException("does not work in java 9");
+ }
+
+ public static Map getGroupsByName(Matcher matcher, Set groups) {
+ if (groups != null) {
+ HashMap map = new HashMap<>();
+ for (String group : groups)
+ map.put(group, matcher.group(group));
+ return map;
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/StringHelper.java b/src/main/java/ua/net/uid/utils/helpers/StringHelper.java
new file mode 100644
index 0000000..c767bee
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/StringHelper.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.io.IOException;
+
+public class StringHelper {
+ private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ private StringHelper() {
+ }
+
+ public static char[] toHex(byte[] bytes) {
+ final char[] buffer = new char[bytes.length * 2];
+ for (int i = 0, n = 0; i < bytes.length; ++i) {
+ final int val = bytes[i] & 255;
+ buffer[n++] = HEX_CHARS[val >>> 4];
+ buffer[n++] = HEX_CHARS[val & 15];
+ }
+ return buffer;
+ }
+
+ public static void toHex(final Appendable builder, byte[] bytes) throws IOException {
+ for (byte b : bytes) {
+ final int val = b & 255;
+ builder.append(HEX_CHARS[val >>> 4]);
+ builder.append(HEX_CHARS[val & 15]);
+ }
+ }
+
+ public static void escape(final Appendable builder, final CharSequence string, int start, int end) throws IOException {
+ for (int e = start; e < end; ++e) {
+ char chr = string.charAt(e);
+ switch (chr) {
+ case '"':
+ case '\\':
+ break;
+ case '\t':
+ chr = 't';
+ break;
+ case '\b':
+ chr = 'b';
+ break;
+ case '\n':
+ chr = 'n';
+ break;
+ case '\r':
+ chr = 'r';
+ break;
+ case '\f':
+ chr = 'f';
+ break;
+ default:
+ if (chr < 32) {
+ if (start < e) builder.append(string, start, e);
+ builder.append("\\u");
+ for (int i = 12; i >= 0; i -= 4)
+ builder.append(HEX_CHARS[(chr >> i) & 15]);
+ start = e + 1;
+ }
+ continue;
+ }
+ if (start < e) builder.append(string, start, e);
+ start = e + 1;
+ builder.append('\\').append(chr);
+ }
+ if (start < end) builder.append(string, start, end);
+ }
+
+ public static void escape(final Appendable builder, final CharSequence string) throws IOException {
+ escape(builder, string, 0, string.length());
+ }
+
+ public static String escape(final CharSequence string) {
+ final StringBuilder builder = new StringBuilder(string.length() * 2);
+ try {
+ escape(builder, string);
+ } catch (IOException ignored) {
+ }
+ return builder.toString();
+ }
+
+ public static void unescape(final Appendable builder, final CharSequence string) throws IOException {
+ final int length = string.length();
+ int start = 0;
+ for (int end = 0; end < length; ++end) {
+ char chr = string.charAt(end);
+ if (chr == '\\') {
+ if (start < end) builder.append(string, start, end);
+ chr = string.charAt(++end);
+ switch (chr) {
+ case '"':
+ case '\\':
+ break;
+ case 't':
+ chr = '\t';
+ break;
+ case 'b':
+ chr = '\b';
+ break;
+ case 'n':
+ chr = '\n';
+ break;
+ case 'r':
+ chr = '\r';
+ break;
+ case 'f':
+ chr = '\f';
+ break;
+ case 'u':
+ chr = 0;
+ for (int i = 1; i <= 4; ++i) {
+ final char tmp = string.charAt(end + i);
+ chr <<= 4;
+ if (tmp >= '0' && tmp <= '9') {
+ chr += tmp - '0';
+ } else if (tmp >= 'A' && tmp <= 'F') {
+ chr += tmp - ('A' - 10);
+ } else if (tmp >= 'a' && tmp <= 'f') {
+ chr += tmp - ('a' - 10);
+ } else {
+ throw new NumberFormatException(string.subSequence(end - 1, end + 5).toString());
+ }
+ }
+ end += 4;
+ break;
+ default:
+ continue;
+ }
+ builder.append(chr);
+ start = end + 1;
+ }
+ }
+ if (start < length) builder.append(string, start, length);
+ }
+
+ public static String unescape(final CharSequence string) {
+ final StringBuilder builder = new StringBuilder(string.length());
+ try {
+ unescape(builder, string);
+ } catch (IOException ignored) {
+ }
+ return builder.toString();
+ }
+
+ public static String trim(String string) {
+ return string == null ? null : string.trim();
+ }
+
+ @SuppressWarnings("SpellCheckingInspection")
+ public static String ltrim(String string) {
+ if (string == null) return null;
+ int i = 0, length = string.length();
+ while (i < length && Character.isWhitespace(string.charAt(i)))
+ ++i;
+ return i == 0 ? string : (i < length ? string.substring(i) : "");
+ }
+
+ @SuppressWarnings("SpellCheckingInspection")
+ public static String rtrim(String string) {
+ if (string == null) return null;
+ int i = string.length();
+ while (i >= 0 && Character.isWhitespace(string.charAt(i - 1)))
+ --i;
+ return i <= 0 ? "" : string.substring(0, i);
+ }
+
+ public static int skipWhitespace(CharSequence source, int offset) {
+ while (offset < source.length() && Character.isWhitespace(source.charAt(offset)))
+ ++offset;
+ return offset;
+ }
+
+ public static boolean isAscii(int chr) {
+ return ((chr & 0xFFFFFF80) == 0);
+ }
+
+ public static boolean isAsciiUpper(int chr) {
+ return chr >= 'A' && chr <= 'Z';
+ }
+
+ public static boolean isAsciiLower(int chr) {
+ return chr >= 'a' && chr <= 'z';
+ }
+
+ public static boolean isAsciiDigit(int chr) {
+ return chr >= '0' && chr <= '9';
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/Transliterate.java b/src/main/java/ua/net/uid/utils/helpers/Transliterate.java
new file mode 100644
index 0000000..ad8447d
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/Transliterate.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.text.Normalizer;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+public class Transliterate {
+ private static final Pattern SYMBOLS = Pattern.compile("[^\\p{L}\\p{Nd}]+");
+ private static final char[][] CHARS = {
+ // Latin
+ {'À', 'A'}, {'Á', 'A'}, {'Â', 'A'}, {'Ã', 'A'}, {'Ä', 'A'}, {'Å', 'A'}, {'Æ', 'A', 'E'}, {'Ç', 'C'}, {'È', 'E'}, {'É', 'E'}, {'Ê', 'E'}, {'Ë', 'E'},
+ {'Ì', 'I'}, {'Í', 'I'}, {'Î', 'I'}, {'Ï', 'I'}, {'Ð', 'D'}, {'Ñ', 'N'}, {'Ò', 'O'}, {'Ó', 'O'}, {'Ô', 'O'}, {'Õ', 'O'}, {'Ö', 'O'}, {'Ő', 'O'},
+ {'Ø', 'O'}, {'Ù', 'U'}, {'Ú', 'U'}, {'Û', 'U'}, {'Ü', 'U'}, {'Ű', 'U'}, {'Ý', 'Y'}, {'Þ', 'T', 'H'}, {'ß', 's', 's'},
+ {'à', 'a'}, {'á', 'a'}, {'â', 'a'}, {'ã', 'a'}, {'ä', 'a'}, {'å', 'a'}, {'æ', 'a', 'e'}, {'ç', 'c'}, {'è', 'e'}, {'é', 'e'}, {'ê', 'e'}, {'ë', 'e'},
+ {'ì', 'i'}, {'í', 'i'}, {'î', 'i'}, {'ï', 'i'}, {'ð', 'd'}, {'ñ', 'n'}, {'ò', 'o'}, {'ó', 'o'}, {'ô', 'o'}, {'õ', 'o'}, {'ö', 'o'}, {'ő', 'o'},
+ {'ø', 'o'}, {'ù', 'u'}, {'ú', 'u'}, {'û', 'u'}, {'ü', 'u'}, {'ű', 'u'}, {'ý', 'y'}, {'þ', 't', 'h'}, {'ÿ', 'y'},
+ //Latin symbols
+ {'©', '(', 'c', ')'},
+ //Greek
+ {'Α', 'A'}, {'Β', 'B'}, {'Γ', 'G'}, {'Δ', 'D'}, {'Ε', 'E'}, {'Ζ', 'Z'}, {'Η', 'H'}, {'Θ', '8'}, {'Ι', 'I'}, {'Κ', 'K'}, {'Λ', 'L'}, {'Μ', 'M'},
+ {'Ν', 'N'}, {'Ξ', '3'}, {'Ο', 'O'}, {'Π', 'P'}, {'Ρ', 'R'}, {'Σ', 'S'}, {'Τ', 'T'}, {'Υ', 'Y'}, {'Φ', 'F'}, {'Χ', 'X'}, {'Ψ', 'P', 'S'}, {'Ω', 'W'},
+ {'Ά', 'A'}, {'Έ', 'E'}, {'Ί', 'I'}, {'Ό', 'O'}, {'Ύ', 'Y'}, {'Ή', 'H'}, {'Ώ', 'W'}, {'Ϊ', 'I'}, {'Ϋ', 'Y'},
+ {'α', 'a'}, {'β', 'b'}, {'γ', 'g'}, {'δ', 'd'}, {'ε', 'e'}, {'ζ', 'z'}, {'η', 'h'}, {'θ', '8'}, {'ι', 'i'}, {'κ', 'k'}, {'λ', 'l'}, {'μ', 'm'},
+ {'ν', 'n'}, {'ξ', '3'}, {'ο', 'o'}, {'π', 'p'}, {'ρ', 'r'}, {'σ', 's'}, {'τ', 't'}, {'υ', 'y'}, {'φ', 'f'}, {'χ', 'x'}, {'ψ', 'p', 's'}, {'ω', 'w'},
+ {'ά', 'a'}, {'έ', 'e'}, {'ί', 'i'}, {'ό', 'o'}, {'ύ', 'y'}, {'ή', 'h'}, {'ώ', 'w'}, {'ς', 's'}, {'ϊ', 'i'}, {'ΰ', 'y'}, {'ϋ', 'y'}, {'ΐ', 'i'},
+ //Turkish
+ {'Ş', 'S'}, {'İ', 'I'}, {'Ç', 'C'}, {'Ü', 'U'}, {'Ö', 'O'}, {'Ğ', 'G'}, {'ş', 's'}, {'ı', 'i'}, {'ç', 'c'}, {'ü', 'u'}, {'ö', 'o'}, {'ğ', 'g'},
+ //Russian
+ {'А', 'A'}, {'Б', 'B'}, {'В', 'V'}, {'Г', 'G'}, {'Д', 'D'}, {'Е', 'E'}, {'Ё', 'Y', 'o'}, {'Ж', 'Z', 'h'}, {'З', 'Z'}, {'И', 'I'}, {'Й', 'J'}, {'К', 'K'},
+ {'Л', 'L'}, {'М', 'M'}, {'Н', 'N'}, {'О', 'O'}, {'П', 'P'}, {'Р', 'R'}, {'С', 'S'}, {'Т', 'T'}, {'У', 'U'}, {'Ф', 'F'}, {'Х', 'H'}, {'Ц', 'C'},
+ {'Ч', 'C', 'h'}, {'Ш', 'S', 'h'}, {'Щ', 'S', 'h'}, {'Ъ'}, {'Ы', 'Y'}, {'Ь'}, {'Э', 'E'}, {'Ю', 'Y', 'u'}, {'Я', 'Y', 'a'},
+ {'а', 'a'}, {'б', 'b'}, {'в', 'v'}, {'г', 'g'}, {'д', 'd'}, {'е', 'e'}, {'ё', 'y', 'o'}, {'ж', 'z', 'h'}, {'з', 'z'}, {'и', 'i'}, {'й', 'j'}, {'к', 'k'},
+ {'л', 'l'}, {'м', 'm'}, {'н', 'n'}, {'о', 'o'}, {'п', 'p'}, {'р', 'r'}, {'с', 's'}, {'т', 't'}, {'у', 'u'}, {'ф', 'f'}, {'х', 'h'}, {'ц', 'c'},
+ {'ч', 'c', 'h'}, {'ш', 's', 'h'}, {'щ', 's', 'h'}, {'ъ'}, {'ы', 'y'}, {'ь'}, {'э', 'e'}, {'ю', 'y', 'u'}, {'я', 'y', 'a'},
+ //Ukrainian
+ {'Є', 'Y', 'e'}, {'І', 'I'}, {'Ї', 'Y', 'i'}, {'Ґ', 'G'}, {'є', 'y', 'e'}, {'і', 'i'}, {'ї', 'y', 'i'}, {'ґ', 'g'},
+ //Czech
+ {'Č', 'C'}, {'Ď', 'D'}, {'Ě', 'E'}, {'Ň', 'N'}, {'Ř', 'R'}, {'Š', 'S'}, {'Ť', 'T'}, {'Ů', 'U'}, {'Ž', 'Z'},
+ {'č', 'c'}, {'ď', 'd'}, {'ě', 'e'}, {'ň', 'n'}, {'ř', 'r'}, {'š', 's'}, {'ť', 't'}, {'ů', 'u'}, {'ž', 'z'},
+ //Polish
+ {'Ą', 'A'}, {'Ć', 'C'}, {'Ę', 'e'}, {'Ł', 'L'}, {'Ń', 'N'}, {'Ó', 'o'}, {'Ś', 'S'}, {'Ź', 'Z'}, {'Ż', 'Z'},
+ {'ą', 'a'}, {'ć', 'c'}, {'ę', 'e'}, {'ł', 'l'}, {'ń', 'n'}, {'ó', 'o'}, {'ś', 's'}, {'ź', 'z'}, {'ż', 'z'},
+ //Latvian
+ {'Ā', 'A'}, {'Č', 'C'}, {'Ē', 'E'}, {'Ģ', 'G'}, {'Ī', 'i'}, {'Ķ', 'k'}, {'Ļ', 'L'}, {'Ņ', 'N'}, {'Š', 'S'}, {'Ū', 'u'}, {'Ž', 'Z'},
+ {'ā', 'a'}, {'č', 'c'}, {'ē', 'e'}, {'ģ', 'g'}, {'ī', 'i'}, {'ķ', 'k'}, {'ļ', 'l'}, {'ņ', 'n'}, {'š', 's'}, {'ū', 'u'}, {'ž', 'z'}
+ };
+
+ static {
+ //noinspection ComparatorCombinators
+ Arrays.sort(CHARS, (char[] o1, char[] o2) -> (int) o1[0] - (int) o2[0]);
+ }
+
+ public static String process(String text, String delimiter) {
+ text = Normalizer.normalize(text, Normalizer.Form.NFC);
+ return SYMBOLS.matcher(transliterate(text)).replaceAll(delimiter);
+ }
+
+ public static void transliterate(StringBuilder builder, CharSequence string) {
+ int length = string.length();
+ for (int i = 0; i < length; ++i) {
+ put(builder, string.charAt(i));
+ }
+ }
+
+ public static CharSequence transliterate(CharSequence string) {
+ StringBuilder builder = new StringBuilder(string.length() + 10);
+ transliterate(builder, string);
+ return builder;
+ }
+
+ private static void put(StringBuilder output, char chr) {
+ int l = 0, r = CHARS.length - 1;
+ if (chr >= CHARS[0][0]) {
+ while (l <= r) {
+ int m = (r + l) >> 1;
+ char[] item = CHARS[m];
+ if (chr < item[0]) {
+ r = m - 1;
+ } else if (chr > item[0]) {
+ l = m + 1;
+ } else {
+ output.append(item, 1, item.length - 1);
+ return;
+ }
+ }
+ }
+ output.append(chr);
+ }
+
+ /* keyboard
+ [
+ 'ru' => [
+ 'code' => 'rus',
+ 'inverted' => [
+ 'q'=>'й','w'=>'ц','e'=>'у','r'=>'к','t'=>'е','y'=>'н','u'=>'г','i'=>'ш','o'=>'щ','p'=>'з','['=>'х','{'=>'х',']'=>'ъ','}'=>'ъ',
+ 'a'=>'ф','s'=>'ы','d'=>'в','f'=>'а','g'=>'п','h'=>'р','j'=>'о','k'=>'л','l'=>'д',';'=>'ж',':'=>'ж',"'"=>'э','"'=>'э',
+ 'z'=>'я','x'=>'ч','c'=>'с','v'=>'м','b'=>'и','n'=>'т','m'=>'ь',','=>'б','<'=>'б','.'=>'ю','>'=>'ю','`'=>'ё','~'=>'ё'
+ ]
+ ],
+ 'uk' => [
+ 'code' => 'ukr',
+ 'inverted' => [
+ 'q'=>'й','w'=>'ц','e'=>'у','r'=>'к','t'=>'е','y'=>'н','u'=>'г','i'=>'ш','o'=>'щ','p'=>'з','['=>'х','{'=>'х',']'=>'ї','}'=>'ї',
+ 'a'=>'ф','s'=>'і','d'=>'в','f'=>'а','g'=>'п','h'=>'р','j'=>'о','k'=>'л','l'=>'д',';'=>'ж',':'=>'ж',"'"=>'є','"'=>'є',
+ 'z'=>'я','x'=>'ч','c'=>'с','v'=>'м','b'=>'и','n'=>'т','m'=>'ь',','=>'б','<'=>'б','.'=>'ю','>'=>'ю','\\'=>'ґ','|'=>'ґ'
+ ]
+ ]
+ ]
+ */
+}
diff --git a/src/main/java/ua/net/uid/utils/helpers/XmlHelper.java b/src/main/java/ua/net/uid/utils/helpers/XmlHelper.java
new file mode 100644
index 0000000..384936d
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/helpers/XmlHelper.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.helpers;
+
+import java.io.IOException;
+
+public class XmlHelper {
+ private XmlHelper() {
+ }
+
+ public static void escape(Appendable builder, CharSequence string, int start, int end) throws IOException {
+ for (int i = start; i < end; ++i) {
+ char chr = string.charAt(i);
+ String entity;
+ switch (chr) {
+ case '&':
+ entity = "&";
+ break;
+ case '<':
+ entity = "<";
+ break;
+ case '>':
+ entity = ">";
+ break;
+ case '"':
+ entity = """;
+ break;
+ case '\'':
+ entity = "'";
+ break;
+ default:
+ continue;
+ }
+ if (start < i) builder.append(string, start, i);
+ start = i + 1;
+ builder.append(entity);
+ }
+ if (start < end) builder.append(string, start, end);
+ }
+
+ public static void escape(Appendable builder, CharSequence string) throws IOException {
+ escape(builder, string, 0, string.length());
+ }
+
+ public static String escape(CharSequence string, int start, int end) {
+ StringBuilder builder = new StringBuilder(string.length());
+ try {
+ escape(builder, string, start, end);
+ } catch (IOException ignored) {
+ }
+ return builder.toString();
+ }
+
+ public static String escape(CharSequence string) {
+ return escape(string, 0, string.length());
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/io/ExpiringCache.java b/src/main/java/ua/net/uid/utils/io/ExpiringCache.java
new file mode 100644
index 0000000..9fa4fa3
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/io/ExpiringCache.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.io;
+
+import ua.net.uid.utils.concurrent.ReadyFuture;
+
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class ExpiringCache {
+ static final int NCPU = Runtime.getRuntime().availableProcessors();
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private final AtomicInteger count = new AtomicInteger();
+ private final ExecutorService executor;
+ private Chain[] table;
+
+ public ExpiringCache() {
+ this(Executors.newCachedThreadPool());
+ }
+
+ public ExpiringCache(ExecutorService executor) {
+ this.executor = executor;
+ table = new Chain[NCPU * 2];
+ }
+
+ public Future future(K key) {
+ return chain(key).get(System.currentTimeMillis(), key);
+ }
+
+ public Future future(K key, Callable callable) {
+ return chain(key).future(System.currentTimeMillis(), key, callable, Long.MAX_VALUE);
+ }
+
+ public Future future(K key, Callable callable, long ttl) {
+ long now;
+ return chain(key).future(now = System.currentTimeMillis(), key, callable, now + ttl);
+ }
+
+ public Future future(K key, Callable callable, Date expiry) {
+ return chain(key).future(System.currentTimeMillis(), key, callable, expiry.getTime());
+ }
+
+ public V get(K key) {
+ Future future = future(key);
+ return future == null ? null : value(future);
+ }
+
+ public V get(K key, Callable callable) {
+ return value(future(key, callable));
+ }
+
+ public V get(K key, Callable callable, long ttl) {
+ return value(future(key, callable, ttl));
+ }
+
+ public V get(K key, Callable callable, Date expiry) {
+ return value(future(key, callable, expiry));
+ }
+
+ public void set(K key, V value) {
+ chain(key).set(System.currentTimeMillis(), key, value, Long.MAX_VALUE);
+ }
+
+ public void set(K key, V value, long ttl) {
+ long now;
+ chain(key).set(now = System.currentTimeMillis(), key, value, now + ttl);
+ }
+
+ public void set(K key, V value, Date expiry) {
+ chain(key).set(System.currentTimeMillis(), key, value, expiry.getTime());
+ }
+
+ public void put(K key, Callable callable) {
+ chain(key).put(System.currentTimeMillis(), key, callable, Long.MAX_VALUE);
+ }
+
+ public void put(K key, Callable callable, long ttl) {
+ long now;
+ chain(key).put(now = System.currentTimeMillis(), key, callable, now + ttl);
+ }
+
+ public void put(K key, Callable callable, Date expiry) {
+ chain(key).put(System.currentTimeMillis(), key, callable, expiry.getTime());
+ }
+
+ public Future extractFuture(K key) {
+ return chain(key).extract(System.currentTimeMillis(), key);
+ }
+
+ public V extract(K key) {
+ Future future = extractFuture(key);
+ return future == null ? null : value(future);
+ }
+
+ public void remove(K key) {
+ chain(key).remove(System.currentTimeMillis(), key);
+ }
+
+ public void clear() {
+ lock.writeLock().lock();
+ try {
+ for (Chain chain : table) {
+ chain.clear();
+ }
+ //count.set(0);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public int count() {
+ return count.get();
+ }
+
+ public void gc() {
+ lock.readLock().lock();
+ try {
+ long now = System.currentTimeMillis();
+ for (Chain chain : table) {
+ chain.gc(now);
+ }
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ private Chain chain(K key) {
+ int hash = key.hashCode();
+ lock.readLock().lock();
+ try {
+ return table[hash % table.length];
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ private V value(Future future) {
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ private static abstract class Item {
+ Entry next;
+ Item(Entry next) { this.next = next; }
+ }
+
+ private final static class Entry extends Item {
+ final K key;
+ Future future;
+ long expiry;
+
+ private Entry(K key, Future future, long expiry, Entry next) {
+ super(next);
+ this.key = key;
+ this.future = future;
+ this.expiry = expiry;
+ }
+ }
+
+ private final static class Chain extends Item {
+ private final ExpiringCache cache;
+ private int count = 0;
+
+ Chain(ExpiringCache cache, Entry next) {
+ super(next);
+ this.cache = cache;
+ }
+
+ synchronized Future get(long now, K key) {
+ Item item = this;
+ int cnt = 0;
+ while (next(now, item)) {
+ if (Objects.equals(key, item.next.key)) {
+ return item.next.future;
+ }
+ ++cnt;
+ item = item.next;
+ }
+ count(cnt);
+ return null;
+ }
+
+ synchronized Future future(long now, K key, Callable callable, long expiry) {
+ Item item = this, last = this;
+ int cnt = 0;
+ while (next(now, item)) {
+ if (Objects.equals(key, item.next.key)) {
+ return item.next.future;
+ }
+ ++cnt;
+ if (item.next.expiry > expiry) {
+ last = item.next;
+ }
+ item = item.next;
+ }
+ last.next = new Entry<>(key, cache.executor.submit(callable), expiry, last.next);
+ count(cnt + 1);
+ return last.next.future;
+ }
+
+ synchronized void set(long now, K key, V value, long expiry) {
+ Item item = this, last = this;
+ int cnt = 0;
+ while (next(now, item)) {
+ if (Objects.equals(key, item.next.key)) {
+ item.next = item.next.next;
+ continue;
+ }
+ ++cnt;
+ if (item.next.expiry > expiry) {
+ last = item.next;
+ }
+ item = item.next;
+ }
+ last.next = new Entry<>(key, new ReadyFuture<>(value), expiry, last.next);
+ count(cnt + 1);
+ }
+
+ synchronized void put(long now, K key, Callable callable, long expiry) {
+ Item item = this, last = this;
+ int cnt = 0;
+ while (next(now, item)) {
+ if (Objects.equals(key, item.next.key)) {
+ item.next = item.next.next;
+ continue;
+ }
+ ++cnt;
+ if (item.next.expiry > expiry) {
+ last = item.next;
+ }
+ item = item.next;
+ }
+ last.next = new Entry<>(key, cache.executor.submit(callable), expiry, last.next);
+ count(cnt + 1);
+ }
+
+ synchronized Future extract(long now, K key) {
+ Item item = this;
+ int cnt = 0;
+ while (next(now, item)) {
+ if (Objects.equals(key, item.next.key)) {
+ try {
+ return item.next.future;
+ } finally {
+ item.next = item.next.next;
+ --count;
+ cache.count.decrementAndGet();
+ }
+ }
+ ++cnt;
+ item = item.next;
+ }
+ count(cnt);
+ return null;
+ }
+
+ synchronized void remove(long now, K key) {
+ Item item = this;
+ int cnt = 0;
+ while (next(now, item)) {
+ if (Objects.equals(key, item.next.key)) {
+ item.next = item.next.next;
+ --count;
+ cache.count.decrementAndGet();
+ return;
+ }
+ ++cnt;
+ item = item.next;
+ }
+ count(cnt);
+ }
+
+ void gc(long now) {
+ Item item = this;
+ int cnt = 0;
+ while (next(now, item)) {
+ ++cnt;
+ item = item.next;
+ }
+ count(cnt);
+ }
+
+ void clear() {
+ cache.count.addAndGet(-count);
+ count = 0;
+ next = null;
+ }
+
+ private boolean next(long now, Item current) {
+ if (current.next != null) {
+ if (current.next.expiry <= now) {
+ current.next = null;
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void count(int value) {
+ if (count != value) {
+ cache.count.addAndGet(value - count);
+ count = value;
+ }
+ }
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/io/InternPool.java b/src/main/java/ua/net/uid/utils/io/InternPool.java
new file mode 100644
index 0000000..a85a25d
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/io/InternPool.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.io;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class InternPool {
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private final Map> pool = new WeakHashMap<>();
+
+ public V intern(V item) {
+ if (item != null) {
+ lock.readLock().lock();
+ try {
+ WeakReference cached = pool.get(item);
+ V result;
+ if (cached != null && (result = cached.get()) != null)
+ return result;
+ } finally {
+ lock.readLock().unlock();
+ }
+ lock.writeLock().lock();
+ try {
+ WeakReference cached = pool.get(item);
+ V result;
+ if (cached != null && (result = cached.get()) != null)
+ return result;
+ pool.put(item, new WeakReference<>(item));
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+ return item;
+ }
+
+ public void remove(V item) {
+ lock.writeLock().lock();
+ try {
+ pool.remove(item);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public void clear() {
+ lock.writeLock().lock();
+ try {
+ pool.clear();
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public int size() {
+ lock.readLock().lock();
+ try {
+ return pool.size();
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/io/LocalCache.java b/src/main/java/ua/net/uid/utils/io/LocalCache.java
new file mode 100644
index 0000000..6236512
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/io/LocalCache.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.io;
+
+import ua.net.uid.utils.concurrent.ReadyFuture;
+
+import java.util.Map;
+import java.util.concurrent.*;
+
+public class LocalCache {
+ private final ExecutorService executor;
+ private final Map> items = new ConcurrentHashMap<>();
+
+ public LocalCache() {
+ this(Executors.newCachedThreadPool());
+ }
+
+ public LocalCache(ExecutorService executor) {
+ this.executor = executor;
+ }
+
+ public Future future(K key) {
+ return items.get(key);
+ }
+
+ public Future future(K key, Callable callable) {
+ return items.computeIfAbsent(key, (K k) -> executor.submit(callable));
+ }
+
+ public V get(K key) {
+ Future future = future(key);
+ return future == null ? null : value(future);
+ }
+
+ public V get(K key, Callable callable) {
+ return value(future(key, callable));
+ }
+
+ public void set(K key, V value) {
+ items.put(key, new ReadyFuture<>(value));
+ }
+
+ public void put(K key, Callable callable) {
+ items.put(key, executor.submit(callable));
+ }
+
+ public Future extractFuture(K key) {
+ return items.remove(key);
+ }
+
+ public V extract(K key) {
+ Future future = extractFuture(key);
+ return future == null ? null : value(future);
+ }
+
+ public void remove(K key) {
+ items.remove(key);
+ }
+
+ public void clear() {
+ items.clear();
+ }
+
+ public int count() {
+ return items.size();
+ }
+
+ private V value(Future future) {
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/io/SimpleWriter.java b/src/main/java/ua/net/uid/utils/io/SimpleWriter.java
new file mode 100644
index 0000000..2c3536c
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/io/SimpleWriter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.io;
+
+import java.io.Writer;
+
+public class SimpleWriter extends Writer implements CharSequence {
+ private final StringBuilder builder;
+
+ public SimpleWriter() {
+ this(new StringBuilder());
+ }
+
+ public SimpleWriter(int initCapacity) {
+ this(new StringBuilder(initCapacity));
+ }
+
+ public SimpleWriter(StringBuilder builder) {
+ this.builder = builder;
+ this.lock = this.builder;
+ }
+
+ @Override
+ public void write(int c) {
+ builder.append((char) c);
+ }
+
+ @Override
+ public void write(char[] buf) {
+ builder.append(buf);
+ }
+
+ @Override
+ public void write(char[] buf, int off, int len) {
+ builder.append(buf, off, len);
+ }
+
+ @Override
+ public void write(String str) {
+ builder.append(str);
+ }
+
+ @Override
+ public void write(String str, int off, int len) {
+ builder.append(str, off, off + len);
+ }
+
+ @Override
+ public SimpleWriter append(CharSequence csq) {
+ builder.append(csq);
+ return this;
+ }
+
+ @Override
+ public SimpleWriter append(CharSequence csq, int start, int end) {
+ builder.append(csq, start, end);
+ return this;
+ }
+
+ @Override
+ public SimpleWriter append(char c) {
+ builder.append(c);
+ return this;
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public int length() {
+ return builder.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ return builder.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return builder.subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+
+ public StringBuilder getBuilder() {
+ return builder;
+ }
+
+ public char[] toCharArray() {
+ return toString().toCharArray();
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/iterators/AbstractFilteredIterator.java b/src/main/java/ua/net/uid/utils/iterators/AbstractFilteredIterator.java
new file mode 100644
index 0000000..a7302f9
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/iterators/AbstractFilteredIterator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.iterators;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public abstract class AbstractFilteredIterator implements Iterator {
+ private final Iterator extends T> iterator;
+ private T next;
+ private int flag = 0;
+
+ public AbstractFilteredIterator(Iterator extends T> iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (flag != 1) {
+ flag = 3;
+ while (iterator.hasNext()) {
+ T item = iterator.next();
+ if (check(item)) {
+ next = item;
+ flag = 1;
+ break;
+ }
+ }
+ }
+ return flag != 3;
+ }
+
+ protected abstract boolean check(T item);
+
+ @Override
+ public T next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ flag = 2;
+ return next;
+ }
+
+ @Override
+ public void remove() {
+ if (flag != 2)
+ throw new IllegalStateException();
+ iterator.remove();
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/iterators/AbstractTransformIterator.java b/src/main/java/ua/net/uid/utils/iterators/AbstractTransformIterator.java
new file mode 100644
index 0000000..14c675c
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/iterators/AbstractTransformIterator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.iterators;
+
+import java.util.Iterator;
+
+public abstract class AbstractTransformIterator implements Iterator {
+ private final Iterator extends S> iterator;
+
+ public AbstractTransformIterator(Iterator extends S> iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public T next() {
+ return transform(iterator.next());
+ }
+
+ protected abstract T transform(S source);
+
+ @Override
+ public void remove() {
+ iterator.remove();
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/iterators/ArrayIterator.java b/src/main/java/ua/net/uid/utils/iterators/ArrayIterator.java
new file mode 100644
index 0000000..765d899
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/iterators/ArrayIterator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.iterators;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class ArrayIterator implements Iterator {
+ private final E[] items;
+ private int offset = 0;
+
+ public ArrayIterator(E[] items) {
+ this.items = items;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return offset < items.length;
+ }
+
+ @Override
+ public E next() {
+ if (offset >= items.length) {
+ throw new NoSuchElementException();
+ } else {
+ return items[offset++];
+ }
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/tools/DailyEquation.java b/src/main/java/ua/net/uid/utils/tools/DailyEquation.java
new file mode 100644
index 0000000..90eefeb
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/tools/DailyEquation.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.tools;
+
+import java.util.Calendar;
+
+/**
+ *
+ *
+ * https://web.archive.org/web/20161202180207/http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
+ * http://edwilliams.org/sunrise_sunset_algorithm.htm
+ * https://edwilliams.org/sunrise_sunset_example.htm
+ *
+ * https://github.com/KlausBrunner/solarpositioning
+ * https://github.com/mikereedell/sunrisesunsetlib-java
+ * https://github.com/shred/commons-suncalc
+ * https://gist.github.com/Tafkas/4742250
+ *
+ * https://github.com/caarmen/SunriseSunset
+ * http://users.electromagnetic.net/bu/astro/sunrise-set.php
+ *
+ *
+ * https://www.aa.quae.nl/en/reken/zonpositie.html
+ *
+ * @author nightfall
+ */
+public class DailyEquation {
+ ////////////////////////////////////////////////////////////////////////////
+ public enum Zenith {
+ ASTRONOMICAL(108.), // Astronomical sunrise/set is when the sun is 18 degrees below the horizon.
+ NAUTICAL(102.), // Nautical sunrise/set is when the sun is 12 degrees below the horizon.
+ CIVIL(96.), // Civil sunrise/set (dawn/dusk) is when the sun is 6 degrees below the horizon.
+ OFFICIAL(90.8333); // Official sunrise/set is when the sun is 50' below the horizon.
+
+ private final double degrees;
+ Zenith(double degrees) { this.degrees = degrees; }
+ public double getDegrees() { return degrees; }
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ public enum Type {
+ NORMAL_DAY("normal"),
+ POLAR_NIGHT("night"),
+ POLAR_DAY("day");
+ private final String title;
+ Type(String title) { this.title = title; }
+ public String getTitle() { return title; }
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ public static class Events {
+ private final Long sunrise;
+ private final Long sunset;
+ private final Type type;
+ Events(Long sunrise, Long sunset, Type type) {
+ this.sunrise = sunrise;
+ this.sunset = sunset;
+ this.type = type;
+ }
+ public Long getSunrise() { return sunrise; }
+ public Long getSunset() { return sunset; }
+ public Type getType() { return type; }
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ public static Events getSunEvents(double latitude, double longitude, Calendar calendar, Zenith zenith) {
+ return getSunEvents(latitude, longitude, calendar, zenith.getDegrees());
+ }
+ public static Events getSunEvents(double latitude, double longitude, Calendar calendar, double zenith) {
+ longitude /= 15;
+ int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
+
+ double sunrizeTime = calculateEvent(latitude, longitude, zenith, dayOfYear, true);
+ double sunsetTime = calculateEvent(latitude, longitude, zenith, dayOfYear, false);
+
+ if (sunrizeTime == Double.NEGATIVE_INFINITY || sunsetTime == Double.NEGATIVE_INFINITY) { // the sun never rises on this location (on the specified date)
+ return new Events(null, null, Type.POLAR_NIGHT);
+ } else if (sunrizeTime == Double.POSITIVE_INFINITY || sunsetTime == Double.POSITIVE_INFINITY) { // the sun never sets on this location (on the specified date)
+ return new Events(null, null, Type.POLAR_DAY);
+ } else {
+ return new Events(convertDate(calendar, sunrizeTime), convertDate(calendar, sunsetTime), Type.NORMAL_DAY);
+ }
+ }
+
+ protected static double calculateEvent(double latitude, double baseLongitudeHour, double zenith, int dayOfYear, boolean rising) {
+ //t = ((rising ? 6 : 18) - lngHour) / 24 + dayOfYear;
+ double longitudeHour = ((rising ? 6 : 18) - baseLongitudeHour) / 24 + dayOfYear;
+ // M = 0.9856 * t - 3.289;
+ double meanAnomaly = 0.9856 * longitudeHour - 3.289;
+ double meanAnomalyRadians = Math.toRadians(meanAnomaly);
+ // L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634
+ double sunTrueLong = meanAnomaly + (1.916 * Math.sin(meanAnomalyRadians)) + (0.020 * Math.sin(2 * meanAnomalyRadians)) + 282.634; // L
+ if (sunTrueLong > 360) sunTrueLong -= 360;
+
+ // sinDec = 0.39782 * sin(L);
+ double sinSunDeclination = 0.39782 * Math.sin(Math.toRadians(sunTrueLong));
+ // cosDec = cos(asin(sinDec));
+ double cosineSunDeclination = Math.cos(Math.asin(sinSunDeclination));
+ // cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude));
+ double latitudeRadians = Math.toRadians(latitude);
+ double cosineSunLocalHour = (Math.cos(Math.toRadians(zenith)) - sinSunDeclination * Math.sin(latitudeRadians))
+ / (cosineSunDeclination * Math.cos(latitudeRadians));
+
+ if (cosineSunLocalHour > 1) return Double.NEGATIVE_INFINITY; // the sun never rises on this location (on the specified date)
+ if (cosineSunLocalHour < -1) return Double.POSITIVE_INFINITY; // the sun never sets on this location (on the specified date)
+
+ // H = (rising ? 360 - acos(cosH) : acos(cosH)) / 15;
+ double sunLocalHour = Math.toDegrees(Math.acos(cosineSunLocalHour));
+ sunLocalHour = (rising ? 360 - sunLocalHour : sunLocalHour) / 15;
+
+ // RA = atan(0.91764 * tan(L)); // RA
+ //double rightAscension = Math.atan(Math.toRadians(0.91764 * Math.toDegrees( Math.tan(Math.toRadians(sunTrueLong)) )));
+ double rightAscension = Math.toDegrees(Math.atan(Math.toRadians(0.91764 * Math.toDegrees( Math.tan(Math.toRadians(sunTrueLong)) ))));
+
+ //--- //RA potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
+ //--- if (rightAscension < 0) { rightAscension += 360; } else if (rightAscension > 360) { rightAscension -= 360; }
+ // RA = (RA + (floor(L/90) - floor(RA/90)) * 90) / 15;
+ rightAscension = (rightAscension + (Math.floor(sunTrueLong / 90) - Math.floor(rightAscension / 90)) * 90) / 15;
+
+ // T = H + RA - (0.06571 * t) - 6.622
+ double localMeanTime = sunLocalHour + rightAscension - (longitudeHour * 0.06571) - 6.622; // T
+
+ // UT = T - lngHour
+ return localMeanTime - baseLongitudeHour;
+ }
+
+ private static final int DAYMSEC = 24 * 60 * 60 * 1000;
+ protected static long convertDate(Calendar calendar, double utcTime) {
+ // localT = UT + localOffset
+ int time = (int)(utcTime * 3600000) + calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
+ // UT potentially needs to be adjusted into the range [0,24) by adding/subtracting 24
+ //if (utcTime >= 24.) utcTime -= 24; else if (utcTime < 0) utcTime += 24;
+ if (time >= DAYMSEC) time -= DAYMSEC; else if (time < 0) time += DAYMSEC;
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ calendar.add(Calendar.MILLISECOND, time);
+ return calendar.getTimeInMillis();
+ }
+}
diff --git a/src/main/java/ua/net/uid/utils/tools/Mask.java b/src/main/java/ua/net/uid/utils/tools/Mask.java
new file mode 100644
index 0000000..ce71b01
--- /dev/null
+++ b/src/main/java/ua/net/uid/utils/tools/Mask.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2019 nightfall.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ua.net.uid.utils.tools;
+
+import ua.net.uid.utils.Setter;
+import ua.net.uid.utils.helpers.RegexHelper;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public interface Mask {
+ boolean check(String value);
+
+ default boolean check(String value, Setter setter) {
+ return check(value);
+ }
+
+ @Override
+ boolean equals(Object obj);
+
+ @Override
+ int hashCode();
+
+ class Any implements Mask {
+ @Override
+ public boolean check(String value) {
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof Any;
+ }
+
+ @Override
+ public int hashCode() {
+ return 5;
+ }
+ }
+
+ class Match implements Mask {
+ private final String pattern;
+
+ public Match(String pattern) {
+ this.pattern = pattern;
+ }
+
+ @Override
+ public boolean check(String value) {
+ return pattern.equals(value);
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof Match && pattern.equals(((Match) another).pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return 23 * 5 + this.pattern.hashCode();
+ }
+ }
+
+ class IgnoreCase implements Mask {
+ private final String pattern;
+
+ public IgnoreCase(String pattern) {
+ this.pattern = pattern.toLowerCase();
+ }
+
+ @Override
+ public boolean check(String value) {
+ return pattern.equalsIgnoreCase(value);
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof IgnoreCase && pattern.equals(((IgnoreCase) another).pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return 37 * 3 + this.pattern.hashCode();
+ }
+ }
+
+ class Prefix implements Mask {
+ private final String prefix;
+
+ public Prefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Override
+ public boolean check(String value) {
+ return value.startsWith(prefix);
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof Prefix && prefix.equals(((Prefix) another).prefix);
+ }
+
+ @Override
+ public int hashCode() {
+ return 59 * 7 + prefix.hashCode();
+ }
+ }
+
+ class Suffix implements Mask {
+ private final String suffix;
+
+ public Suffix(String suffix) {
+ this.suffix = suffix;
+ }
+
+ @Override
+ public boolean check(String value) {
+ return value.endsWith(suffix);
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof Suffix && suffix.equals(((Suffix) another).suffix);
+ }
+
+ @Override
+ public int hashCode() {
+ return 53 * 5 + suffix.hashCode();
+ }
+ }
+
+ class Regexp implements Mask {
+ private final Pattern pattern;
+ private final Set groups;
+
+ public Regexp(Pattern pattern) {
+ this.pattern = pattern;
+ this.groups = RegexHelper.getNamedGroupsSet(pattern);
+ }
+
+ public Regexp(String pattern) {
+ this(Pattern.compile(pattern));
+ }
+
+ public Regexp(String pattern, int flags) {
+ this(Pattern.compile(pattern, flags));
+ }
+
+ @Override
+ public boolean check(String value) {
+ return check(value, null);
+ }
+
+ @Override
+ public boolean check(String value, Setter setter) {
+ final Matcher matcher = pattern.matcher(value);
+ if (matcher.matches()) {
+ if (setter != null && !groups.isEmpty()) {
+ for (String group : groups)
+ setter.set(group, matcher.group(group));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof Regexp && pattern.equals(((Regexp) another).pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return 97 * 7 + this.pattern.hashCode();
+ }
+ }
+
+ class Or implements Mask {
+ private final Mask[] items;
+
+ public Or(Mask... items) {
+ this.items = items;
+ }
+
+ @Override
+ public boolean check(String value) {
+ return check(value, null);
+ }
+
+ @Override
+ public boolean check(String value, Setter setter) {
+ for (Mask mask : items) {
+ if (mask.check(value, setter)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return 61 * 7 + Arrays.deepHashCode(items);
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof Or && Arrays.deepEquals(items, ((Or) another).items);
+ }
+ }
+
+ class And implements Mask {
+ private final Mask[] items;
+
+ public And(Mask... items) {
+ this.items = items;
+ }
+
+ @Override
+ public boolean check(String value) {
+ return check(value, null);
+ }
+
+ @Override
+ public boolean check(String value, Setter setter) {
+ for (Mask mask : items) {
+ if (!mask.check(value, setter)) return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return 67 * 3 + Arrays.deepHashCode(items);
+ }
+
+ @Override
+ public boolean equals(Object another) {
+ return another instanceof And && Arrays.deepEquals(items, ((And) another).items);
+ }
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/concurrent/ReadyFutureTest.java b/src/test/java/ua/net/uid/utils/concurrent/ReadyFutureTest.java
new file mode 100644
index 0000000..c1e15cc
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/concurrent/ReadyFutureTest.java
@@ -0,0 +1,41 @@
+package ua.net.uid.utils.concurrent;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ReadyFutureTest {
+
+ @Test
+ void testCancel() {
+ ReadyFuture instance = new ReadyFuture(null);
+ assertFalse(instance.cancel(true));
+ assertFalse(instance.cancel(false));
+ }
+
+ @Test
+ void testIsCancelled() {
+ ReadyFuture instance = new ReadyFuture(null);
+ assertFalse(instance.isCancelled());
+ }
+
+ @Test
+ void testIsDone() {
+ ReadyFuture instance = new ReadyFuture(null);
+ assertTrue(instance.isDone());
+ }
+
+ @Test
+ void testGet() {
+ ReadyFuture instance = new ReadyFuture(3);
+ assertEquals(3, instance.get());
+ }
+
+ @Test
+ void testGet_long_TimeUnit() {
+ ReadyFuture instance = new ReadyFuture(4.4);
+ assertEquals(4.4, instance.get(1, TimeUnit.NANOSECONDS));
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/ArrayHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/ArrayHelperTest.java
new file mode 100644
index 0000000..a7e1147
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/ArrayHelperTest.java
@@ -0,0 +1,68 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ArrayHelperTest {
+ final String[] source = new String[]{"v2", "v5", "v4", "v1", null, "v3", "v2", "v5", "v4", "v1", null, "v3"};
+
+ @Test
+ void indexOf() {
+ assertEquals(3, ArrayHelper.indexOf(source, "v1"));
+ assertEquals(0, ArrayHelper.indexOf(source, "v2"));
+ assertEquals(-1, ArrayHelper.indexOf(source, "s1"));
+ assertEquals(4, ArrayHelper.indexOf(source, null));
+ }
+
+ @Test
+ void indexOfFrom() {
+ assertEquals(-1, ArrayHelper.indexOf(source, "s1", 2));
+ assertEquals(3, ArrayHelper.indexOf(source, "v1", 3));
+ assertEquals(6, ArrayHelper.indexOf(source, "v2", 3));
+ assertEquals(4, ArrayHelper.indexOf(source, null, 4));
+ assertEquals(10, ArrayHelper.indexOf(source, null, 5));
+ assertEquals(-1, ArrayHelper.indexOf(source, null, 11));
+ }
+
+ @Test
+ void lastIndexOf() {
+ assertEquals(9, ArrayHelper.lastIndexOf(source, "v1"));
+ assertEquals(6, ArrayHelper.lastIndexOf(source, "v2"));
+ assertEquals(-1, ArrayHelper.lastIndexOf(source, "s1"));
+ assertEquals(10, ArrayHelper.lastIndexOf(source, null));
+ }
+
+ @Test
+ void lastIndexOfFrom() {
+ assertEquals(-1, ArrayHelper.lastIndexOf(source, "s1", 2));
+ assertEquals(3, ArrayHelper.lastIndexOf(source, "v1", 3));
+ assertEquals(0, ArrayHelper.lastIndexOf(source, "v2", 3));
+ assertEquals(4, ArrayHelper.lastIndexOf(source, null, 4));
+ assertEquals(-1, ArrayHelper.lastIndexOf(source, null, 3));
+ assertEquals(10, ArrayHelper.lastIndexOf(source, null, 11));
+ }
+
+ /*
+ @Test
+ void testCollectionToArray() {
+ List source = Arrays.asList("v2", "v5", "v4", "v1", "v3");
+ String[] target = ArrayHelper.toArray(source);
+ assertArrayEquals(new String[]{"v2", "v5", "v4", "v1", "v3"}, target);
+ }
+
+ @Test
+ void testCollectionToSortedArray() {
+ List source = Arrays.asList("v2", "v5", "v4", "v1", "v3");
+ String[] target = ArrayHelper.toSortedArray(source);
+ assertArrayEquals(new String[]{"v1", "v2", "v3", "v4", "v5"}, target);
+ }
+
+ @Test
+ void testCollectionToSortedArrayWithComparator() {
+ List source = Arrays.asList("v2", "v5", "v4", "v1", "v3");
+ @SuppressWarnings("ComparatorCombinators") String[] target = ArrayHelper.toSortedArray(source, (String o1, String o2) -> o2.compareTo(o1));
+ assertArrayEquals(new String[]{"v5", "v4", "v3", "v2", "v1"}, target);
+ }
+ */
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/CastTest.java b/src/test/java/ua/net/uid/utils/helpers/CastTest.java
new file mode 100644
index 0000000..ff0a055
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/CastTest.java
@@ -0,0 +1,171 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CastTest {
+ @Test
+ void testToBoolean() {
+ assertNull(Cast.toBoolean(null, null));
+ assertNull(Cast.toBoolean("", null));
+ assertNull(Cast.toBoolean("qwe", null));
+ assertTrue(Cast.toBoolean(null, true));
+ assertTrue(Cast.toBoolean("", true));
+ assertTrue(Cast.toBoolean("qwe", true));
+ assertFalse(Cast.toBoolean(null, false));
+ assertFalse(Cast.toBoolean("", false));
+ assertFalse(Cast.toBoolean("qwe", false));
+ assertTrue(Cast.toBoolean("true", false));
+ assertTrue(Cast.toBoolean("t", false));
+ assertTrue(Cast.toBoolean("yes", false));
+ assertTrue(Cast.toBoolean("y", false));
+ assertTrue(Cast.toBoolean("122", false));
+ assertTrue(Cast.toBoolean("+", false));
+ assertFalse(Cast.toBoolean("false", true));
+ assertFalse(Cast.toBoolean("f", true));
+ assertFalse(Cast.toBoolean("no", true));
+ assertFalse(Cast.toBoolean("n", true));
+ assertFalse(Cast.toBoolean("0000", true));
+ assertFalse(Cast.toBoolean("-", true));
+ }
+
+ @Test
+ void testToByte() {
+ assertNull(Cast.toByte(null, null));
+ assertNull(Cast.toByte("", null));
+ assertNull(Cast.toByte("qwe", null));
+ assertEquals((Object) (byte) 33, Cast.toByte(null, (byte) 33));
+ assertEquals((Object) (byte) 34, Cast.toByte("", (byte) 34));
+ assertEquals((Object) (byte) 35, Cast.toByte("qwe", (byte) 35));
+ assertEquals((Object) (byte) 12, Cast.toByte("12", (byte) 36));
+ assertEquals((Object) (byte) 0, Cast.toByte("0", (byte) 37));
+ }
+
+ @Test
+ void testToShort() {
+ assertNull(Cast.toShort(null, null));
+ assertNull(Cast.toShort("", null));
+ assertNull(Cast.toShort("qwe", null));
+ assertEquals((Object) (short) 33, Cast.toShort(null, (short) 33));
+ assertEquals((Object) (short) 34, Cast.toShort("", (short) 34));
+ assertEquals((Object) (short) 35, Cast.toShort("qwe", (short) 35));
+ assertEquals((Object) (short) 12, Cast.toShort("12", (short) 36));
+ assertEquals((Object) (short) 0, Cast.toShort("0", (short) 37));
+ }
+
+ @Test
+ void testToInteger() {
+ assertNull(Cast.toInteger(null, null));
+ assertNull(Cast.toInteger("", null));
+ assertNull(Cast.toInteger("qwe", null));
+ assertEquals((Object) (int) 33, Cast.toInteger(null, 33));
+ assertEquals((Object) (int) 34, Cast.toInteger("", 34));
+ assertEquals((Object) (int) 35, Cast.toInteger("qwe", 35));
+ assertEquals((Object) (int) 12, Cast.toInteger("12", 36));
+ assertEquals((Object) (int) 0, Cast.toInteger("0", 37));
+ }
+
+ @Test
+ void testToLong() {
+ assertNull(Cast.toLong(null, null));
+ assertNull(Cast.toLong("", null));
+ assertNull(Cast.toLong("qwe", null));
+ assertEquals((Object) (long) 33, Cast.toLong(null, (long) 33));
+ assertEquals((Object) (long) 34, Cast.toLong("", (long) 34));
+ assertEquals((Object) (long) 35, Cast.toLong("qwe", (long) 35));
+ assertEquals((Object) (long) 12, Cast.toLong("12", (long) 36));
+ assertEquals((Object) (long) 0, Cast.toLong("0", (long) 37));
+ }
+
+ @Test
+ void testToFloat() {
+ assertNull(Cast.toFloat(null, null));
+ assertNull(Cast.toFloat("", null));
+ assertNull(Cast.toFloat("qwe", null));
+ assertEquals((Object) (float) 33.1, Cast.toFloat(null, (float) 33.1));
+ assertEquals((Object) (float) 34.1, Cast.toFloat("", (float) 34.1));
+ assertEquals((Object) (float) 35.1, Cast.toFloat("qwe", (float) 35.1));
+ assertEquals((Object) (float) 12.2, Cast.toFloat("12.2", (float) 36.1));
+ assertEquals((Object) (float) 0.2, Cast.toFloat("0.2", (float) 37.1));
+ assertEquals((Object) (float) 0.0, Cast.toFloat("0.0", (float) 38.1));
+ }
+
+ @Test
+ void testToDouble() {
+ assertNull(Cast.toDouble(null, null));
+ assertNull(Cast.toDouble("", null));
+ assertNull(Cast.toDouble("qwe", null));
+ assertEquals((Object) (double) 33.1, Cast.toDouble(null, 33.1));
+ assertEquals((Object) (double) 34.1, Cast.toDouble("", 34.1));
+ assertEquals((Object) (double) 35.1, Cast.toDouble("qwe", 35.1));
+ assertEquals((Object) (double) 12.2, Cast.toDouble("12.2", 36.1));
+ assertEquals((Object) (double) 0.2, Cast.toDouble("0.2", 37.1));
+ assertEquals((Object) (double) 0.0, Cast.toDouble("0.0", 38.1));
+ }
+
+ @Test
+ void testToBigInteger() {
+ assertNull(Cast.toBigInteger(null, null));
+ assertNull(Cast.toBigInteger("", null));
+ assertNull(Cast.toBigInteger("qwe", null));
+ assertEquals(BigInteger.ZERO, Cast.toBigInteger(null, BigInteger.ZERO));
+ assertEquals(BigInteger.ONE, Cast.toBigInteger("", BigInteger.ONE));
+ assertEquals(BigInteger.TEN, Cast.toBigInteger("qwe", BigInteger.TEN));
+ assertEquals(BigInteger.valueOf(12), Cast.toBigInteger("12", BigInteger.ZERO));
+ assertEquals(BigInteger.ZERO, Cast.toBigInteger("0", BigInteger.TEN));
+ }
+
+ @Test
+ void testToBigDecimal() {
+ assertNull(Cast.toBigDecimal(null, null));
+ assertNull(Cast.toBigDecimal("", null));
+ assertNull(Cast.toBigDecimal("qwe", null));
+ assertEquals(BigDecimal.valueOf(33.1), Cast.toBigDecimal(null, BigDecimal.valueOf(33.1)));
+ assertEquals(BigDecimal.valueOf(34.1), Cast.toBigDecimal("", BigDecimal.valueOf(34.1)));
+ assertEquals(BigDecimal.valueOf(35.1), Cast.toBigDecimal("qwe", BigDecimal.valueOf(35.1)));
+ assertEquals(BigDecimal.valueOf(12.2), Cast.toBigDecimal("12.2", BigDecimal.valueOf(36.1)));
+ assertEquals(BigDecimal.valueOf(0.2), Cast.toBigDecimal("0.2", BigDecimal.valueOf(37.1)));
+ assertEquals(BigDecimal.valueOf(0.0), Cast.toBigDecimal("0.0", BigDecimal.valueOf(38.1)));
+ }
+
+ @Test
+ void testToEmail() {
+ assertNull(Cast.toEmail(null, null));
+ assertNull(Cast.toEmail("", null));
+ assertNull(Cast.toEmail("qwe", null));
+ assertEquals("default", Cast.toEmail(null, "default"));
+ assertEquals("default", Cast.toEmail("", "default"));
+ assertEquals("default", Cast.toEmail("qwe", "default"));
+ assertEquals("mail@test.net", Cast.toEmail(" mail@test.net ", "default"));
+ }
+
+ @Test
+ void testToDate() {
+ DateFormat format = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z", Locale.US);
+ assertNull(Cast.toDate(format, "Wed, Jul 4, '01", null));
+ assertEquals(994273736000L, Cast.toDate(format, "2001.07.04 AD at 12:08:56 PDT", null).getTime());
+ }
+
+ @Test
+ void testToEnum() {
+ assertNull(Cast.toEnum(TestEnum.class, null, null));
+ assertNull(Cast.toEnum(TestEnum.class, "", null));
+ assertNull(Cast.toEnum(TestEnum.class, "qwe", null));
+ assertEquals(TestEnum.three, Cast.toEnum(TestEnum.class, null, TestEnum.three));
+ assertEquals(TestEnum.two, Cast.toEnum(TestEnum.class, "", TestEnum.two));
+ assertEquals(TestEnum.one, Cast.toEnum(TestEnum.class, "qwe", TestEnum.one));
+ assertEquals(TestEnum.zero, Cast.toEnum(TestEnum.class, "zero", TestEnum.one));
+ }
+
+
+ enum TestEnum {zero, one, two, three}
+
+
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/CommonHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/CommonHelperTest.java
new file mode 100644
index 0000000..a2e8b33
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/CommonHelperTest.java
@@ -0,0 +1,85 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CommonHelperTest {
+
+ CommonHelperTest() {
+ }
+
+ @Test
+ void testClose() {
+ AutoCloseableImpl closeable = new AutoCloseableImpl();
+ CommonHelper.close(closeable);
+ assertTrue(closeable.called);
+ }
+
+ @Test
+ void testIsEmptyCharSequence() {
+ StringBuilder src = null;
+ assertTrue(CommonHelper.isEmpty((CharSequence) null));
+ src = new StringBuilder();
+ assertTrue(CommonHelper.isEmpty(src));
+ src.append("test");
+ assertFalse(CommonHelper.isEmpty(src));
+ }
+
+ @Test
+ void testIsEmptyString() {
+ String src = null;
+ assertTrue(CommonHelper.isEmpty(src));
+ src = "";
+ assertTrue(CommonHelper.isEmpty(src));
+ src = "test";
+ assertFalse(CommonHelper.isEmpty(src));
+ }
+
+ @Test
+ void testIsEmptyCollection() {
+ Collection src = null;
+ assertTrue(CommonHelper.isEmpty(src));
+ src = new ArrayList();
+ assertTrue(CommonHelper.isEmpty(src));
+ src.add("1");
+ assertFalse(CommonHelper.isEmpty(src));
+ }
+
+ @Test
+ void testIsEmptyMap() {
+ Map src = null;
+ assertTrue(CommonHelper.isEmpty(src));
+ src = new HashMap();
+ assertTrue(CommonHelper.isEmpty(src));
+ src.put(1, "1");
+ assertFalse(CommonHelper.isEmpty(src));
+ }
+
+ @Test
+ void testIsEmptyGenericArray() {
+ Integer[] src = null;
+ assertTrue(CommonHelper.isEmpty(src));
+ src = new Integer[0];
+ assertTrue(CommonHelper.isEmpty(src));
+ src = new Integer[]{1};
+ assertFalse(CommonHelper.isEmpty(src));
+ }
+
+ static class AutoCloseableImpl implements AutoCloseable {
+ boolean called = false;
+
+ @Override
+ public void close() throws Exception {
+ called = true;
+ throw new IOException("test exception");
+ }
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/EnumHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/EnumHelperTest.java
new file mode 100644
index 0000000..daf02b7
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/EnumHelperTest.java
@@ -0,0 +1,50 @@
+package ua.net.uid.utils.helpers;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import org.junit.jupiter.api.Test;
+
+public class EnumHelperTest {
+ @Test
+ void testStringToEnumWithDefault() {
+ assertNull(EnumHelper.valueOf(Source.class, "zero", null));
+ assertEquals(Source.four, EnumHelper.valueOf(Source.class, "zero", Source.four));
+ assertEquals(Source.one, EnumHelper.valueOf(Source.class, "one", null));
+ assertEquals(Source.four, EnumHelper.valueOf(Source.class, "four", null));
+ }
+
+ @Test
+ void testStringToEnumDefaultNull() {
+ assertNull(EnumHelper.valueOf(Source.class, "zero"));
+ assertEquals(Source.two, EnumHelper.valueOf(Source.class, "two"));
+ assertEquals(Source.three, EnumHelper.valueOf(Source.class, "three"));
+ }
+
+ @Test
+ void toList() {
+ assertEquals(
+ Arrays.asList(Source.one, Source.two, Source.three, Source.four, Source.five),
+ EnumHelper.toList(Source.class)
+ );
+ }
+
+ @Test
+ void toMap() {
+ Map map = new HashMap<>();
+ map.put(Source.one.toString(), Source.one);
+ map.put(Source.two.toString(), Source.two);
+ map.put(Source.three.toString(), Source.three);
+ map.put(Source.four.toString(), Source.four);
+ map.put(Source.five.toString(), Source.five);
+
+ assertEquals(map, EnumHelper.toMap(Source.class));
+ }
+
+ enum Source {
+ one, two, three, four, five
+ }
+}
+
diff --git a/src/test/java/ua/net/uid/utils/helpers/NumberHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/NumberHelperTest.java
new file mode 100644
index 0000000..e68b59e
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/NumberHelperTest.java
@@ -0,0 +1,24 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class NumberHelperTest {
+ private final long LONG_VALUE = 0x12345678a1b2c3d4L;
+ private final byte[] BYTE_ARRAY = {(byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0xa1, (byte) 0xb2, (byte) 0xc3, (byte) 0xd4};
+
+ NumberHelperTest() {
+ }
+
+ @Test
+ void testLongToBytes() {
+ assertArrayEquals(BYTE_ARRAY, NumberHelper.longToBytes(LONG_VALUE));
+ }
+
+ @Test
+ void testBytesToLong() {
+ assertEquals(LONG_VALUE, NumberHelper.bytesToLong(BYTE_ARRAY));
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/RandomHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/RandomHelperTest.java
new file mode 100644
index 0000000..751878c
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/RandomHelperTest.java
@@ -0,0 +1,55 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class RandomHelperTest { //TODO RandomHelperTest
+
+ RandomHelperTest() {
+ }
+
+ @Test
+ void testRandomCharsWithLimitLength() {
+ int minLength = 20;
+ int maxLength = 50;
+ char[] result = RandomHelper.randomChars(minLength, maxLength);
+ assertNotNull(result);
+ assertTrue(result.length >= minLength);
+ assertTrue(result.length <= maxLength);
+ }
+
+ @Test
+ void testRandomCharsWithLimitLengthAndCustomRandom() {
+ int minLength = 20;
+ int maxLength = 50;
+ Random random = new Random();
+ char[] result = RandomHelper.randomChars(minLength, maxLength, random);
+ assertNotNull(result);
+ assertTrue(result.length >= minLength);
+ assertTrue(result.length <= maxLength);
+ }
+
+ @Test
+ void testRandomStringWithLimitLength() {
+ int minLength = 2;
+ int maxLength = 5;
+ String result = RandomHelper.randomString(minLength, maxLength);
+ assertNotNull(result);
+ assertTrue(result.length() >= minLength);
+ assertTrue(result.length() <= maxLength);
+ }
+
+ @Test
+ void testRandomStringWithLimitLengthAndCustomRandom() {
+ int minLength = 2;
+ int maxLength = 5;
+ Random random = new Random();
+ String result = RandomHelper.randomString(minLength, maxLength, random);
+ assertTrue(result.length() >= minLength);
+ assertTrue(result.length() <= maxLength);
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/RegexHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/RegexHelperTest.java
new file mode 100644
index 0000000..bf65a04
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/RegexHelperTest.java
@@ -0,0 +1,43 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+class RegexHelperTest {
+ @Test
+ void testGetNamedGroups() {
+ Pattern pattern = Pattern.compile("(.+)@(?[^@]+(\\.)(?[^.@]+))");
+ Set result = RegexHelper.getNamedGroupsSet(pattern);
+ assertTrue(result.contains("host"));
+ assertTrue(result.contains("zone"));
+ }
+
+ /*
+ @Test
+ void testGetNamedGroups() {
+ Pattern pattern = Pattern.compile("(.+)@(?[^@]+(\\.)(?[^.@]+))");
+ Map result = RegexHelper.getNamedGroups(pattern);
+ assertEquals((Integer) 2, result.get("host"));
+ assertEquals((Integer) 4, result.get("zone"));
+ }
+
+ @Test
+ void testGetGroupsByName() {
+ Pattern pattern = Pattern.compile("^(.+)@(?([^@]+\\.)?(?[^.@]+))$");
+ Map groupsMap = RegexHelper.getNamedGroups(pattern);
+ Set groups = groupsMap.keySet();
+ Matcher matcher = pattern.matcher("nobody@test.mail.com");
+ assertTrue(matcher.matches());
+ Map result = RegexHelper.getGroupsByName(matcher, groups);
+ assertEquals("test.mail.com", matcher.group(groupsMap.get("host")));
+ assertEquals("test.mail.com", result.get("host"));
+ assertEquals("com", matcher.group(groupsMap.get("zone")));
+ assertEquals("com", result.get("zone"));
+ }
+ */
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/StringHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/StringHelperTest.java
new file mode 100644
index 0000000..4ac9e70
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/StringHelperTest.java
@@ -0,0 +1,102 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SuppressWarnings("SpellCheckingInspection")
+class StringHelperTest {
+
+ @Test
+ void testByteArrayAsHexToCharArray() {
+ byte[] src = new byte[256];
+ for (int i = 0; i < 256; ++i) {
+ src[i] = (byte) i;
+ }
+ char[] dst = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff".toCharArray();
+ char[] result = StringHelper.toHex(src);
+ assertArrayEquals(dst, result);
+ }
+
+ @Test
+ void testByteArrayAsHexToAppendable() throws Exception {
+ byte[] src = new byte[256];
+ for (int i = 0; i < 256; ++i) {
+ src[i] = (byte) i;
+ }
+ String dst = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
+ Appendable builder = new StringBuilder();
+ StringHelper.toHex(builder, src);
+ assertEquals(dst, builder.toString());
+ }
+
+ @Test
+ void testEscapeToAppendableWithRange() throws Exception {
+ String src = "a\nsd`!@#$%^&*()\u0001\"'\n\r\\\t\b\fsdfцыувфыв\u0017";
+ String dst = "d`!@#$%^&*()\\u0001\\\"'\\n\\r\\\\\\t\\b\\fsdfцыувф";
+ Appendable builder = new StringBuilder();
+ StringHelper.escape(builder, src, 3, src.length() - 3);
+ assertEquals(dst, builder.toString());
+ }
+
+ @Test
+ void testEscapeToAppendable() throws Exception {
+ String src = "asd`!@#$%^&*()\u0001\"'\n\r\\\t\b\fsdfцыувфыв\u0017";
+ String dst = "asd`!@#$%^&*()\\u0001\\\"'\\n\\r\\\\\\t\\b\\fsdfцыувфыв\\u0017";
+ Appendable builder = new StringBuilder();
+ StringHelper.escape(builder, src);
+ assertEquals(dst, builder.toString());
+ }
+
+ @Test
+ void testEscapeToString() {
+ String src = "asd`!@#$%^&*()\u0001\"'\n\r\\\t\b\fsdfцыувфыв\u0017";
+ String dst = "asd`!@#$%^&*()\\u0001\\\"'\\n\\r\\\\\\t\\b\\fsdfцыувфыв\\u0017";
+ assertEquals(dst, StringHelper.escape(src));
+ }
+
+ @Test
+ void testUnescapeToAppendable() throws Exception {
+ String src = "asd`!@#$%^&*()\\u0001\\\"'\\n\\r\\\\\\t\\b\\fsdfцыувфыв\\u0017";
+ String dst = "asd`!@#$%^&*()\u0001\"'\n\r\\\t\b\fsdfцыувфыв\u0017";
+ Appendable builder = new StringBuilder();
+ StringHelper.unescape(builder, src);
+ assertEquals(dst, builder.toString());
+ }
+
+ @Test
+ void testUnescapeToString() {
+ String src = "asd`!@#$%^&*()\\u0001\\\"'\\n\\r\\\\\\t\\b\\fsdfцыувфыв\\u0017";
+ String dst = "asd`!@#$%^&*()\u0001\"'\n\r\\\t\b\fsdfцыувфыв\u0017";
+ assertEquals(dst, StringHelper.unescape(src));
+ }
+
+ @Test
+ void testTrimStringNotThrowNPE() {
+ assertNull(StringHelper.trim(null));
+ assertEquals("abc", StringHelper.trim("\t abc\r\n"));
+ }
+
+ @Test
+ void testLtrimStringNotThrowNPE() {
+ assertNull(StringHelper.trim(null));
+ assertEquals("abc\r\n", StringHelper.ltrim("\t abc\r\n"));
+ }
+
+ @Test
+ void testRtrimStringNotThrowNPE() {
+ assertNull(StringHelper.trim(null));
+ assertEquals("\t abc", StringHelper.rtrim("\t abc\r\n"));
+ }
+
+ @Test
+ void testSkipWhitespaceTest() {
+ String src = "asd qwe\t\n123\f\f\fzzz ";
+ assertEquals(0, StringHelper.skipWhitespace(src, 0));
+ assertEquals(4, StringHelper.skipWhitespace(src, 3));
+ assertEquals(9, StringHelper.skipWhitespace(src, 7));
+ assertEquals(15, StringHelper.skipWhitespace(src, 12));
+ assertEquals(15, StringHelper.skipWhitespace(src, 15));
+ assertEquals(src.length(), StringHelper.skipWhitespace(src, 18));
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/TransliterateTest.java b/src/test/java/ua/net/uid/utils/helpers/TransliterateTest.java
new file mode 100644
index 0000000..fda62ce
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/TransliterateTest.java
@@ -0,0 +1,25 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class TransliterateTest {
+ @Test
+ void testTransliterateFromCharSequenceToStringBuilder() {
+ StringBuilder builder = new StringBuilder();
+ String test = "Сжатие SSL/TLS было запрещено по умолчанию ©";
+ Transliterate.transliterate(builder, test);
+ //noinspection SpellCheckingInspection
+ assertEquals("Szhatie SSL/TLS bylo zapresheno po umolchaniyu (c)", builder.toString());
+ }
+
+ @Test
+ void testTransliterateFromCharSequence() {
+ StringBuilder builder = new StringBuilder();
+ String test = "Сжатие SSL/TLS было запрещено по умолчанию";
+ Transliterate.transliterate(builder, test);
+ //noinspection SpellCheckingInspection
+ assertEquals("Szhatie SSL/TLS bylo zapresheno po umolchaniyu", Transliterate.transliterate(test).toString());
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/helpers/XmlHelperTest.java b/src/test/java/ua/net/uid/utils/helpers/XmlHelperTest.java
new file mode 100644
index 0000000..5c27a1f
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/helpers/XmlHelperTest.java
@@ -0,0 +1,46 @@
+package ua.net.uid.utils.helpers;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class XmlHelperTest {
+ @SuppressWarnings("SpellCheckingInspection")
+ private static final String src = " &< &-amp; <-lt; >-gt; \"-quot; '-#39; >& ";
+
+ @Test
+ void testEscapeXmlEntitiesByRangeToAppendable() throws Exception {
+ StringBuilder builder = new StringBuilder();
+ XmlHelper.escape(builder, src, 4, src.length() - 4);
+ assertEquals(
+ "&-amp; <-lt; >-gt; "-quot; '-#39;",
+ builder.toString()
+ );
+ }
+
+ @Test
+ void testEscapeXmlEntitiesToAppendable() throws Exception {
+ StringBuilder builder = new StringBuilder();
+ XmlHelper.escape(builder, src);
+ assertEquals(
+ " &< &-amp; <-lt; >-gt; "-quot; '-#39; >& ",
+ builder.toString()
+ );
+ }
+
+ @Test
+ void testEscapeXmlEntitiesByRangeToString() {
+ assertEquals(
+ "&-amp; <-lt; >-gt; "-quot; '-#39;",
+ XmlHelper.escape(src, 4, src.length() - 4)
+ );
+ }
+
+ @Test
+ void testEscapeXmlEntitiesToString() {
+ assertEquals(
+ " &< &-amp; <-lt; >-gt; "-quot; '-#39; >& ",
+ XmlHelper.escape(src)
+ );
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/io/ExpiringCacheTest.java b/src/test/java/ua/net/uid/utils/io/ExpiringCacheTest.java
new file mode 100644
index 0000000..3002b9f
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/io/ExpiringCacheTest.java
@@ -0,0 +1,89 @@
+package ua.net.uid.utils.io;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+//TODO ExpiringCacheTest
+class ExpiringCacheTest {
+
+ @Test
+ void future() {
+ }
+
+ @Test
+ void future1() {
+ }
+
+ @Test
+ void future2() {
+ }
+
+ @Test
+ void future3() {
+ }
+
+ @Test
+ void get() {
+ }
+
+ @Test
+ void get1() {
+ }
+
+ @Test
+ void get2() {
+ }
+
+ @Test
+ void get3() {
+ }
+
+ @Test
+ void set() {
+ }
+
+ @Test
+ void set1() {
+ }
+
+ @Test
+ void set2() {
+ }
+
+ @Test
+ void put() {
+ }
+
+ @Test
+ void put1() {
+ }
+
+ @Test
+ void put2() {
+ }
+
+ @Test
+ void extractFuture() {
+ }
+
+ @Test
+ void extract() {
+ }
+
+ @Test
+ void remove() {
+ }
+
+ @Test
+ void clear() {
+ }
+
+ @Test
+ void count() {
+ }
+
+ @Test
+ void gc() {
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/ua/net/uid/utils/io/LocalCacheTest.java b/src/test/java/ua/net/uid/utils/io/LocalCacheTest.java
new file mode 100644
index 0000000..2cfef8b
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/io/LocalCacheTest.java
@@ -0,0 +1,109 @@
+package ua.net.uid.utils.io;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SuppressWarnings("unchecked")
+class LocalCacheTest {
+
+ @Test
+ void testFuture_GenericType() throws InterruptedException, ExecutionException {
+ LocalCache instance = new LocalCache();
+ instance.set("test", 123);
+ Future result = instance.future("test");
+ assertNotNull(result);
+ assertEquals(123, result.get());
+ }
+
+ @Test
+ void testFuture_GenericType_Callable() throws InterruptedException, ExecutionException {
+ LocalCache instance = new LocalCache();
+ Future result = instance.future("test", () -> 456);
+ assertNotNull(result);
+ assertEquals(456, result.get());
+ }
+
+ @Test
+ void testGet_GenericType() {
+ LocalCache instance = new LocalCache();
+ instance.set("test", 789);
+ Object result = instance.get("test");
+ assertNotNull(result);
+ assertEquals(789, result);
+ }
+
+ @Test
+ void testGet_GenericType_Callable() {
+ LocalCache instance = new LocalCache();
+ Object result = instance.get("test", () -> 65535);
+ assertNotNull(result);
+ assertEquals(65535, result);
+ }
+
+ @Test
+ void testSet_GenericType_GenericType() throws InterruptedException, ExecutionException {
+ LocalCache instance = new LocalCache();
+ instance.set("test", 2345);
+ Future result = instance.future("test");
+ assertNotNull(result);
+ assertEquals(2345, result.get());
+ }
+
+ @Test
+ void testSet_GenericType_Callable() throws InterruptedException, ExecutionException {
+ LocalCache instance = new LocalCache();
+ instance.put("testSet_GenericType_Callable", () -> "testSet_GenericType_Callable");
+ Future result = instance.future("testSet_GenericType_Callable");
+ assertNotNull(result);
+ assertEquals("testSet_GenericType_Callable", result.get());
+ }
+
+ @Test
+ void testExtractFuture() throws InterruptedException, ExecutionException {
+ LocalCache instance = new LocalCache();
+ instance.put("testExtractFuture", () -> "testExtractFuture");
+ Future result = instance.extractFuture("testExtractFuture");
+ assertNotNull(result);
+ assertNull(instance.future("testExtractFuture"));
+ assertEquals("testExtractFuture", result.get());
+ }
+
+ @Test
+ void testExtract() {
+ LocalCache instance = new LocalCache();
+ instance.put("testExtract", () -> "testExtract");
+ Object result = instance.extract("testExtract");
+ assertNotNull(result);
+ assertNull(instance.future("testExtract"));
+ assertEquals("testExtract", result);
+ }
+
+ @Test
+ void testRemove() {
+ LocalCache instance = new LocalCache();
+ instance.put("testRemove", () -> "testRemove");
+ instance.remove("testExtract");
+ assertNull(instance.future("testExtract"));
+ }
+
+ @Test
+ void testClear() {
+ LocalCache instance = new LocalCache();
+ instance.put("testClear", () -> "testClear");
+ instance.clear();
+ assertNull(instance.future("testClear"));
+ }
+
+ @Test
+ void testCount() {
+ LocalCache instance = new LocalCache();
+ instance.set(0, 0);
+ instance.set(1, 1);
+ instance.set(2, 2);
+ assertEquals(3, instance.count());
+ }
+}
diff --git a/src/test/java/ua/net/uid/utils/io/SimpleWriterTest.java b/src/test/java/ua/net/uid/utils/io/SimpleWriterTest.java
new file mode 100644
index 0000000..cd3bb91
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/io/SimpleWriterTest.java
@@ -0,0 +1,134 @@
+package ua.net.uid.utils.io;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.Writer;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class SimpleWriterTest {
+
+ @Test
+ void testWriteChar() {
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(32);
+ assertEquals(" ", instance.toString());
+ }
+
+ @Test
+ void testWriteCharsFromOffsetWithLength() {
+ char[] buf = "aaa0123456789bbb".toCharArray();
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(buf, 3, 10);
+ assertEquals("0123456789", instance.toString());
+ }
+
+ @Test
+ void testWriteString() {
+ String str = "prototype";
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(str);
+ assertEquals(str, instance.toString());
+ }
+
+ @Test
+ void testWriteStringFromOffsetWithLength() {
+ String buf = "aaa0123456789bbb";
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(buf, 3, 10);
+ assertEquals("0123456789", instance.toString());
+ }
+
+ @Test
+ void testAppendCharSequence() {
+ SimpleWriter instance = new SimpleWriter();
+ Writer result = instance.append("test");
+ assertEquals(result, instance);
+ assertEquals("test", result.toString());
+ }
+
+ @Test
+ void testAppendCharSequenceFromOffsetWithLength() {
+ CharSequence data = "aaa0123456789bbb";
+ SimpleWriter instance = new SimpleWriter();
+ Writer result = instance.append(data, 3, 10);
+ assertEquals(result, instance);
+ assertEquals("0123456", result.toString());
+ }
+
+ @Test
+ void testAppendChar() {
+ SimpleWriter instance = new SimpleWriter();
+ Writer result = instance.append(' ');
+ assertEquals(result, instance);
+ assertEquals(" ", instance.toString());
+ }
+
+ @Test
+ void testFlush() {
+ SimpleWriter instance = new SimpleWriter();
+ instance.write("The test case is a prototype.");
+ instance.flush();
+ assertEquals("The test case is a prototype.", instance.toString());
+ }
+
+ @Test
+ void testClose() {
+ SimpleWriter instance = new SimpleWriter();
+ instance.write("The test case is a prototype.");
+ instance.close();
+ assertEquals("The test case is a prototype.", instance.toString());
+ }
+
+ @Test
+ void testLength() {
+ String data = "aaa0123456789bbb";
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(data);
+ assertEquals(data.length(), instance.length());
+ }
+
+ @Test
+ void testCharAt() {
+ String data = "aaa0123456789bbb";
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(data);
+ assertEquals(data.charAt(3), instance.charAt(3));
+ }
+
+ @Test
+ void testSubSequence() {
+ String data = "aaa0123456789bbb";
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(data);
+ assertEquals("0123456", instance.subSequence(3, 10));
+ }
+
+ @Test
+ void testToString() {
+ String data = "aaa0123456789bbb";
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(data);
+ assertEquals(data, instance.toString());
+ }
+
+ @Test
+ void testGetBuilder() {
+ String data = "aaa0123456789bbb";
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(data);
+ StringBuilder result = instance.getBuilder();
+ assertEquals(data, result.toString());
+ }
+
+ @Test
+ void testToCharArray() {
+ char[] data = "aaa0123456789bbb".toCharArray();
+ SimpleWriter instance = new SimpleWriter();
+ instance.write(data);
+ char[] result = instance.toCharArray();
+ assertArrayEquals(data, result);
+ }
+
+}
diff --git a/src/test/java/ua/net/uid/utils/iterators/AbstractFilteredIteratorTest.java b/src/test/java/ua/net/uid/utils/iterators/AbstractFilteredIteratorTest.java
new file mode 100644
index 0000000..e69baf2
--- /dev/null
+++ b/src/test/java/ua/net/uid/utils/iterators/AbstractFilteredIteratorTest.java
@@ -0,0 +1,76 @@
+package ua.net.uid.utils.iterators;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AbstractFilteredIteratorTest {
+ private final List