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

com.github.underscore.U Maven / Gradle / Ivy

There is a newer version: 1.64
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright 2015-2024 Valentyn Kolesnikov
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.github.underscore;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.zip.GZIPInputStream;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.NodeList;

@SuppressWarnings({
    "java:S135",
    "java:S1168",
    "java:S3655",
    "java:S3740",
    "java:S3776",
    "java:S4423",
    "java:S4830",
    "java:S5843",
    "java:S5996",
    "java:S5998"
})
public class U extends Underscore {
    private static final int DEFAULT_TRUNC_LENGTH = 30;
    private static final String DEFAULT_TRUNC_OMISSION = "...";
    private static final java.util.regex.Pattern RE_LATIN_1 =
            java.util.regex.Pattern.compile("[\\xc0-\\xd6\\xd8-\\xde\\xdf-\\xf6\\xf8-\\xff]");
    private static final java.util.regex.Pattern RE_PROP_NAME =
            java.util.regex.Pattern.compile(
                    "[^.\\[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\2)\\[^\\]|\\.)*?)\2)\\]|"
                            + "(?=(\\.|\\[\\])(?:\4|$))");
    private static final Map DEBURRED_LETTERS = new LinkedHashMap<>();
    private static final Map> DEFAULT_HEADER_FIELDS = new HashMap<>();
    private static final Set SUPPORTED_HTTP_METHODS =
            new HashSet<>(Arrays.asList("GET", "POST", "PUT", "DELETE"));
    private static final int BUFFER_LENGTH_1024 = 1024;
    private static final int RESPONSE_CODE_400 = 400;
    private static final String ROOT = "root";
    private static String upper = "[A-Z\\xc0-\\xd6\\xd8-\\xde\\u0400-\\u04FF]";
    private static String lower = "[a-z\\xdf-\\xf6\\xf8-\\xff]+";
    private static String selfClosing = "-self-closing";
    private static String nilKey = "-nil";
    private static java.util.regex.Pattern reWords =
            java.util.regex.Pattern.compile(
                    upper
                            + "+(?="
                            + upper
                            + lower
                            + ")|"
                            + upper
                            + "?"
                            + lower
                            + "|"
                            + upper
                            + "+|[0-9]+");

    static {
        String[] deburredLetters =
                new String[] {
                    "\u00c0", "A", "\u00c1", "A", "\u00c2", "A", "\u00c3", "A", "\u00c4", "A",
                    "\u00c5", "A", "\u00e0", "a", "\u00e1", "a", "\u00e2", "a", "\u00e3", "a",
                    "\u00e4", "a", "\u00e5", "a", "\u00c7", "C", "\u00e7", "c", "\u00d0", "D",
                    "\u00f0", "d", "\u00c8", "E", "\u00c9", "E", "\u00ca", "E", "\u00cb", "E",
                    "\u00e8", "e", "\u00e9", "e", "\u00ea", "e", "\u00eb", "e", "\u00cC", "I",
                    "\u00cd", "I", "\u00ce", "I", "\u00cf", "I", "\u00eC", "i", "\u00ed", "i",
                    "\u00ee", "i", "\u00ef", "i", "\u00d1", "N", "\u00f1", "n", "\u00d2", "O",
                    "\u00d3", "O", "\u00d4", "O", "\u00d5", "O", "\u00d6", "O", "\u00d8", "O",
                    "\u00f2", "o", "\u00f3", "o", "\u00f4", "o", "\u00f5", "o", "\u00f6", "o",
                    "\u00f8", "o", "\u00d9", "U", "\u00da", "U", "\u00db", "U", "\u00dc", "U",
                    "\u00f9", "u", "\u00fa", "u", "\u00fb", "u", "\u00fc", "u", "\u00dd", "Y",
                    "\u00fd", "y", "\u00ff", "y", "\u00c6", "Ae", "\u00e6", "ae", "\u00de", "Th",
                    "\u00fe", "th", "\u00df", "ss"
                };
        for (int index = 0; index < deburredLetters.length; index += 2) {
            DEBURRED_LETTERS.put(deburredLetters[index], deburredLetters[index + 1]);
        }
        DEFAULT_HEADER_FIELDS.put(
                "Content-Type", Arrays.asList("application/json", "charset=utf-8"));
    }

    public enum XmlToJsonMode {
        REPLACE_SELF_CLOSING_WITH_NULL,
        REPLACE_SELF_CLOSING_WITH_STRING,
        REPLACE_EMPTY_VALUE_WITH_NULL,
        REPLACE_EMPTY_TAG_WITH_NULL,
        REPLACE_EMPTY_TAG_WITH_STRING,
        REMOVE_FIRST_LEVEL,
        WITHOUT_NAMESPACES,
        REPLACE_MINUS_WITH_AT,
        REPLACE_EMPTY_TAG_WITH_NULL_AND_MINUS_WITH_AT
    }

    public enum JsonToXmlMode {
        FORCE_ATTRIBUTE_USAGE,
        DEFINE_ROOT_NAME,
        REPLACE_NULL_WITH_EMPTY_VALUE,
        REPLACE_EMPTY_STRING_WITH_EMPTY_VALUE,
        ADD_ROOT,
        REMOVE_ARRAY_ATTRIBUTE,
        REMOVE_ATTRIBUTES
    }

    public U(final Iterable iterable) {
        super(iterable);
    }

    public U(final String string) {
        super(string);
    }

    public static class Chain extends Underscore.Chain {
        public Chain(final T item) {
            super(item);
        }

        public Chain(final List list) {
            super(list);
        }

        public Chain(final Map map) {
            super(map);
        }

        @Override
        public Chain first() {
            return new Chain<>(Underscore.first(value()));
        }

        @Override
        public Chain first(int n) {
            return new Chain<>(Underscore.first(value(), n));
        }

        @Override
        public Chain firstOrNull() {
            return new Chain<>(Underscore.firstOrNull(value()));
        }

        @Override
        public Chain firstOrNull(final Predicate pred) {
            return new Chain<>(Underscore.firstOrNull(value(), pred));
        }

        @Override
        public Chain initial() {
            return new Chain<>(Underscore.initial(value()));
        }

        @Override
        public Chain initial(int n) {
            return new Chain<>(Underscore.initial(value(), n));
        }

        @Override
        public Chain last() {
            return new Chain<>(Underscore.last(value()));
        }

        @Override
        public Chain last(int n) {
            return new Chain<>(Underscore.last(value(), n));
        }

        @Override
        public Chain lastOrNull() {
            return new Chain<>(Underscore.lastOrNull(value()));
        }

        @Override
        public Chain lastOrNull(final Predicate pred) {
            return new Chain<>(Underscore.lastOrNull(value(), pred));
        }

        @Override
        public Chain rest() {
            return new Chain<>(Underscore.rest(value()));
        }

        @Override
        public Chain rest(int n) {
            return new Chain<>(Underscore.rest(value(), n));
        }

        @Override
        public Chain compact() {
            return new Chain<>(Underscore.compact(value()));
        }

        @Override
        public Chain compact(final T falsyValue) {
            return new Chain<>(Underscore.compact(value(), falsyValue));
        }

        @Override
        public Chain flatten() {
            return new Chain<>(Underscore.flatten(value()));
        }

        @Override
        public  Chain map(final Function func) {
            return new Chain<>(Underscore.map(value(), func));
        }

        @Override
        public  Chain mapMulti(final BiConsumer> mapper) {
            return new Chain<>(Underscore.mapMulti(value(), mapper));
        }

        @Override
        public  Chain mapIndexed(final BiFunction func) {
            return new Chain<>(Underscore.mapIndexed(value(), func));
        }

        @Override
        public Chain filter(final Predicate pred) {
            return new Chain<>(Underscore.filter(value(), pred));
        }

        @Override
        public Chain filterIndexed(final PredicateIndexed pred) {
            return new Chain<>(Underscore.filterIndexed(value(), pred));
        }

        @Override
        public Chain rejectIndexed(final PredicateIndexed pred) {
            return new Chain<>(Underscore.rejectIndexed(value(), pred));
        }

        @Override
        public Chain reject(final Predicate pred) {
            return new Chain<>(Underscore.reject(value(), pred));
        }

        @Override
        public Chain filterFalse(final Predicate pred) {
            return new Chain<>(Underscore.filterFalse(value(), pred));
        }

        @Override
        public  Chain reduce(final BiFunction func, final F zeroElem) {
            return new Chain<>(Underscore.reduce(value(), func, zeroElem));
        }

        @Override
        public Chain> reduce(final BinaryOperator func) {
            return new Chain<>(Underscore.reduce(value(), func));
        }

        @Override
        public  Chain reduceRight(final BiFunction func, final F zeroElem) {
            return new Chain<>(Underscore.reduceRight(value(), func, zeroElem));
        }

        @Override
        public Chain> reduceRight(final BinaryOperator func) {
            return new Chain<>(Underscore.reduceRight(value(), func));
        }

        @Override
        public Chain> find(final Predicate pred) {
            return new Chain<>(Underscore.find(value(), pred));
        }

        @Override
        public Chain> findLast(final Predicate pred) {
            return new Chain<>(Underscore.findLast(value(), pred));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain max() {
            return new Chain<>(Underscore.max((Collection) value()));
        }

        @Override
        public > Chain max(final Function func) {
            return new Chain<>(Underscore.max(value(), func));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain min() {
            return new Chain<>(Underscore.min((Collection) value()));
        }

        @Override
        public > Chain min(final Function func) {
            return new Chain<>(Underscore.min(value(), func));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain sort() {
            return new Chain<>(Underscore.sort((List) value()));
        }

        @Override
        @SuppressWarnings("unchecked")
        public > Chain sortWith(final Comparator comparator) {
            return new Chain<>(Underscore.sortWith((List) value(), comparator));
        }

        @Override
        public > Chain sortBy(final Function func) {
            return new Chain<>(Underscore.sortBy(value(), func));
        }

        @Override
        @SuppressWarnings("unchecked")
        public  Chain> sortBy(final K key) {
            return new Chain<>(Underscore.sortBy((List>) value(), key));
        }

        @Override
        public  Chain>> groupBy(final Function func) {
            return new Chain<>(Underscore.groupBy(value(), func));
        }

        @Override
        public  Chain> associateBy(final Function func) {
            return new Chain<>(Underscore.associateBy(value(), func));
        }

        @Override
        public  Chain>> groupBy(
                final Function func, final BinaryOperator binaryOperator) {
            return new Chain<>(Underscore.groupBy(value(), func, binaryOperator));
        }

        @Override
        public Chain>> indexBy(final String property) {
            return new Chain<>(Underscore.indexBy(value(), property));
        }

        @Override
        public  Chain> countBy(final Function func) {
            return new Chain<>(Underscore.countBy(value(), func));
        }

        @Override
        public Chain> countBy() {
            return new Chain<>(Underscore.countBy(value()));
        }

        @Override
        public Chain shuffle() {
            return new Chain<>(Underscore.shuffle(value()));
        }

        @Override
        public Chain sample() {
            return new Chain<>(Underscore.sample(value()));
        }

        @Override
        public Chain sample(final int howMany) {
            return new Chain<>(newArrayList(Underscore.sample(value(), howMany)));
        }

        @Override
        public Chain tap(final Consumer func) {
            Underscore.tap(value(), func);
            return new Chain<>(value());
        }

        @Override
        public Chain forEach(final Consumer func) {
            Underscore.forEach(value(), func);
            return new Chain<>(value());
        }

        @Override
        public Chain forEachRight(final Consumer func) {
            Underscore.forEachRight(value(), func);
            return new Chain<>(value());
        }

        @Override
        public Chain every(final Predicate pred) {
            return new Chain<>(Underscore.every(value(), pred));
        }

        @Override
        public Chain some(final Predicate pred) {
            return new Chain<>(Underscore.some(value(), pred));
        }

        @Override
        public Chain count(final Predicate pred) {
            return new Chain<>(Underscore.count(value(), pred));
        }

        @Override
        public Chain contains(final T elem) {
            return new Chain<>(Underscore.contains(value(), elem));
        }

        @Override
        public Chain containsWith(final T elem) {
            return new Chain<>(Underscore.containsWith(value(), elem));
        }

        @Override
        public Chain invoke(final String methodName, final List args) {
            return new Chain<>(Underscore.invoke(value(), methodName, args));
        }

        @Override
        public Chain invoke(final String methodName) {
            return new Chain<>(Underscore.invoke(value(), methodName));
        }

        @Override
        public Chain pluck(final String propertyName) {
            return new Chain<>(Underscore.pluck(value(), propertyName));
        }

        @Override
        public  Chain where(final List> properties) {
            return new Chain<>(Underscore.where(value(), properties));
        }

        @Override
        public  Chain> findWhere(final List> properties) {
            return new Chain<>(Underscore.findWhere(value(), properties));
        }

        @Override
        public Chain uniq() {
            return new Chain<>(Underscore.uniq(value()));
        }

        @Override
        public  Chain uniq(final Function func) {
            return new Chain<>(newArrayList(Underscore.uniq(value(), func)));
        }

        @Override
        public Chain distinct() {
            return new Chain<>(Underscore.uniq(value()));
        }

        @Override
        @SuppressWarnings("unchecked")
        public  Chain distinctBy(final Function func) {
            return new Chain<>(newArrayList((Iterable) Underscore.uniq(value(), func)));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain union(final List... lists) {
            return new Chain<>(Underscore.union(value(), lists));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain intersection(final List... lists) {
            return new Chain<>(Underscore.intersection(value(), lists));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain difference(final List... lists) {
            return new Chain<>(Underscore.difference(value(), lists));
        }

        @Override
        public Chain range(final int stop) {
            return new Chain<>(Underscore.range(stop));
        }

        @Override
        public Chain range(final int start, final int stop) {
            return new Chain<>(Underscore.range(start, stop));
        }

        @Override
        public Chain range(final int start, final int stop, final int step) {
            return new Chain<>(Underscore.range(start, stop, step));
        }

        @Override
        public Chain> chunk(final int size) {
            return new Chain<>(Underscore.chunk(value(), size, size));
        }

        @Override
        public Chain> chunk(final int size, final int step) {
            return new Chain<>(Underscore.chunk(value(), size, step));
        }

        @Override
        public Chain> chunkFill(final int size, final T fillValue) {
            return new Chain<>(Underscore.chunkFill(value(), size, size, fillValue));
        }

        @Override
        public Chain> chunkFill(final int size, final int step, final T fillValue) {
            return new Chain<>(Underscore.chunkFill(value(), size, step, fillValue));
        }

        @Override
        public Chain cycle(final int times) {
            return new Chain<>(Underscore.cycle(value(), times));
        }

        @Override
        public Chain interpose(final T element) {
            return new Chain<>(Underscore.interpose(value(), element));
        }

        @Override
        public Chain interposeByList(final Iterable interIter) {
            return new Chain<>(Underscore.interposeByList(value(), interIter));
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain concat(final List... lists) {
            return new Chain<>(Underscore.concat(value(), lists));
        }

        @Override
        public Chain slice(final int start) {
            return new Chain<>(Underscore.slice(value(), start));
        }

        @Override
        public Chain slice(final int start, final int end) {
            return new Chain<>(Underscore.slice(value(), start, end));
        }

        public Chain> set(final String path, Object value) {
            U.set(map(), path, value);
            return new Chain<>(map());
        }

        public Chain> set(final List paths, Object value) {
            U.set(map(), paths, value);
            return new Chain<>(map());
        }

        @Override
        public Chain reverse() {
            return new Chain<>(Underscore.reverse(value()));
        }

        @Override
        public Chain join() {
            return new Chain<>(Underscore.join(value()));
        }

        @Override
        public Chain join(final String separator) {
            return new Chain<>(Underscore.join(value(), separator));
        }

        @Override
        public Chain skip(final int numberToSkip) {
            return new Chain<>(value().subList(numberToSkip, value().size()));
        }

        @Override
        public Chain limit(final int size) {
            return new Chain<>(value().subList(0, size));
        }

        @Override
        @SuppressWarnings("unchecked")
        public  Chain> toMap() {
            return new Chain<>(Underscore.toMap((Iterable>) value()));
        }

        public Chain drop() {
            return new Chain<>(Underscore.drop(value()));
        }

        public Chain drop(final Integer n) {
            return new Chain<>(Underscore.drop(value(), n));
        }

        public Chain dropRight() {
            return new Chain<>(U.dropRight(value()));
        }

        public Chain dropRight(final Integer n) {
            return new Chain<>(U.dropRight(value(), n));
        }

        public Chain dropWhile(final Predicate pred) {
            return new Chain<>(U.dropWhile(value(), pred));
        }

        public Chain dropRightWhile(final Predicate pred) {
            return new Chain<>(U.dropRightWhile(value(), pred));
        }

        @SuppressWarnings("unchecked")
        public Chain fill(final Object value) {
            return new Chain<>(U.fill((List) value(), value));
        }

        @SuppressWarnings("unchecked")
        public Chain fill(final Object value, final Integer start, final Integer end) {
            return new Chain<>(U.fill((List) value(), value, start, end));
        }

        public Chain flattenDeep() {
            return new Chain<>(U.flattenDeep(value()));
        }

        @SuppressWarnings("unchecked")
        public Chain pull(final Object... values) {
            return new Chain<>(U.pull((List) value(), values));
        }

        @SuppressWarnings("unchecked")
        public Chain pullAt(final Integer... indexes) {
            return new Chain<>(U.pullAt((List) value(), indexes));
        }

        public Chain remove(final Predicate pred) {
            return new Chain<>(U.remove(value(), pred));
        }

        public Chain take() {
            return new Chain<>(U.take(value()));
        }

        public Chain takeRight() {
            return new Chain<>(U.takeRight(value()));
        }

        public Chain take(final Integer n) {
            return new Chain<>(U.take(value(), n));
        }

        public Chain takeRight(final Integer n) {
            return new Chain<>(U.takeRight(value(), n));
        }

        public Chain takeWhile(final Predicate pred) {
            return new Chain<>(U.takeWhile(value(), pred));
        }

        public Chain takeRightWhile(final Predicate pred) {
            return new Chain<>(U.takeRightWhile(value(), pred));
        }

        @SuppressWarnings("unchecked")
        public Chain xor(final List list) {
            return new Chain<>(U.xor(value(), list));
        }

        public Chain at(final Integer... indexes) {
            return new Chain<>(U.at(value(), indexes));
        }

        @SuppressWarnings("unchecked")
        public  Chain sum() {
            return new Chain<>(U.sum((List) value()));
        }

        public  Chain sum(final Function func) {
            return new Chain<>(U.sum(value(), func));
        }

        @SuppressWarnings("unchecked")
        public Chain mean() {
            return new Chain<>(U.mean((List) value()));
        }

        @SuppressWarnings("unchecked")
        public Chain median() {
            return new Chain<>(U.median((List) value()));
        }

        public Chain camelCase() {
            return new Chain<>(U.camelCase((String) item()));
        }

        public Chain lowerFirst() {
            return new Chain<>(U.lowerFirst((String) item()));
        }

        public Chain upperFirst() {
            return new Chain<>(U.upperFirst((String) item()));
        }

        public Chain capitalize() {
            return new Chain<>(U.capitalize((String) item()));
        }

        public Chain deburr() {
            return new Chain<>(U.deburr((String) item()));
        }

        public Chain endsWith(final String target) {
            return new Chain<>(U.endsWith((String) item(), target));
        }

        public Chain endsWith(final String target, final Integer position) {
            return new Chain<>(U.endsWith((String) item(), target, position));
        }

        public Chain kebabCase() {
            return new Chain<>(U.kebabCase((String) item()));
        }

        public Chain repeat(final int length) {
            return new Chain<>(U.repeat((String) item(), length));
        }

        public Chain pad(final int length) {
            return new Chain<>(U.pad((String) item(), length));
        }

        public Chain pad(final int length, final String chars) {
            return new Chain<>(U.pad((String) item(), length, chars));
        }

        public Chain padStart(final int length) {
            return new Chain<>(U.padStart((String) item(), length));
        }

        public Chain padStart(final int length, final String chars) {
            return new Chain<>(U.padStart((String) item(), length, chars));
        }

        public Chain padEnd(final int length) {
            return new Chain<>(U.padEnd((String) item(), length));
        }

        public Chain padEnd(final int length, final String chars) {
            return new Chain<>(U.padEnd((String) item(), length, chars));
        }

        public Chain snakeCase() {
            return new Chain<>(U.snakeCase((String) item()));
        }

        public Chain startCase() {
            return new Chain<>(U.startCase((String) item()));
        }

        public Chain startsWith(final String target) {
            return new Chain<>(U.startsWith((String) item(), target));
        }

        public Chain startsWith(final String target, final Integer position) {
            return new Chain<>(U.startsWith((String) item(), target, position));
        }

        public Chain trim() {
            return new Chain<>(U.trim((String) item()));
        }

        public Chain trim(final String chars) {
            return new Chain<>(U.trim((String) item(), chars));
        }

        public Chain trimStart() {
            return new Chain<>(U.trimStart((String) item()));
        }

        public Chain trimStart(final String chars) {
            return new Chain<>(U.trimStart((String) item(), chars));
        }

        public Chain trimEnd() {
            return new Chain<>(U.trimEnd((String) item()));
        }

        public Chain trunc() {
            return new Chain<>(U.trunc((String) item()));
        }

        public Chain trunc(final int length) {
            return new Chain<>(U.trunc((String) item(), length));
        }

        public Chain trimEnd(final String chars) {
            return new Chain<>(U.trimEnd((String) item(), chars));
        }

        public Chain uncapitalize() {
            return new Chain<>(U.uncapitalize((String) item()));
        }

        public Chain words() {
            return new Chain<>(U.words((String) item()));
        }

        public Chain toJson() {
            return new Chain<>(Json.toJson(value()));
        }

        public Chain fromJson() {
            return new Chain<>(Json.fromJson((String) item()));
        }

        public Chain toXml() {
            return new Chain<>(Xml.toXml(value()));
        }

        public Chain fromXml() {
            return new Chain<>(Xml.fromXml((String) item()));
        }

        public Chain fetch() {
            return new Chain<>(U.fetch((String) item()).text());
        }

        public Chain fetch(final String method, final String body) {
            return new Chain<>(U.fetch((String) item(), method, body).text());
        }

        public Chain> createPermutationWithRepetition(final int permutationLength) {
            return new Chain<>(U.createPermutationWithRepetition(value(), permutationLength));
        }

        public Chain xmlToJson() {
            return new Chain<>(U.xmlToJson((String) item()));
        }

        public Chain jsonToXml() {
            return new Chain<>(U.jsonToXml((String) item()));
        }
    }

    public static Chain chain(final String item) {
        return new U.Chain<>(item);
    }

    public static  Chain chain(final List list) {
        return new U.Chain<>(list);
    }

    public static Chain> chain(final Map map) {
        return new U.Chain<>(map);
    }

    public static  Chain chain(final Iterable iterable) {
        return new U.Chain<>(newArrayList(iterable));
    }

    public static  Chain chain(final Iterable iterable, int size) {
        return new U.Chain<>(newArrayList(iterable, size));
    }

    @SuppressWarnings("unchecked")
    public static  Chain chain(final T... list) {
        return new U.Chain<>(Arrays.asList(list));
    }

    public static Chain chain(final int[] array) {
        return new U.Chain<>(newIntegerList(array));
    }

    @Override
    public Chain chain() {
        return new U.Chain<>(newArrayList(value()));
    }

    public static Chain of(final String item) {
        return new U.Chain<>(item);
    }

    public static  Chain of(final List list) {
        return new U.Chain<>(list);
    }

    public static Chain> of(final Map map) {
        return new U.Chain<>(map);
    }

    public static  Chain of(final Iterable iterable) {
        return new U.Chain<>(newArrayList(iterable));
    }

    public static  Chain of(final Iterable iterable, int size) {
        return new U.Chain<>(newArrayList(iterable, size));
    }

    @SuppressWarnings("unchecked")
    public static  Chain of(final T... list) {
        return new U.Chain<>(Arrays.asList(list));
    }

    public static Chain of(final int[] array) {
        return new U.Chain<>(newIntegerList(array));
    }

    @Override
    public Chain of() {
        return new U.Chain<>(newArrayList(value()));
    }

    public static  List drop(final Iterable iterable) {
        return rest(newArrayList(iterable));
    }

    public List drop() {
        return drop(getIterable());
    }

    public static  List drop(final Iterable iterable, final Integer n) {
        return rest(newArrayList(iterable), n);
    }

    public List drop(final Integer n) {
        return drop(getIterable(), n);
    }

    public static  List dropRight(final Iterable iterable) {
        return initial(newArrayList(iterable));
    }

    public List dropRight() {
        return dropRight(getIterable());
    }

    public static  List dropRight(final Iterable iterable, final Integer n) {
        return initial(newArrayList(iterable), n);
    }

    public List dropRight(final Integer n) {
        return dropRight(getIterable(), n);
    }

    public static  List dropWhile(final Iterable iterable, final Predicate pred) {
        return rest(newArrayList(iterable), findIndex(newArrayList(iterable), negate(pred)));
    }

    public List dropWhile(final Predicate pred) {
        return dropWhile(getIterable(), pred);
    }

    public static  List dropRightWhile(final Iterable iterable, final Predicate pred) {
        return reverse(dropWhile(reverse(iterable), pred));
    }

    public List dropRightWhile(final Predicate pred) {
        return dropRightWhile(getIterable(), pred);
    }

    public static  List fill(List list, T item) {
        for (int i = 0; i < size(list); i++) {
            list.set(i, item);
        }
        return list;
    }

    public static  T[] fill(T[] array, T item) {
        Arrays.fill(array, item);
        return array;
    }

    @SuppressWarnings("unchecked")
    public List fill(Object value) {
        return fill((List) getIterable(), value);
    }

    public static List fill(
            final List list, Object value, Integer start, Integer end) {
        for (int index = start; index < end; index += 1) {
            list.set(index, value);
        }
        return list;
    }

    @SuppressWarnings("unchecked")
    public List fill(Object value, Integer start, Integer end) {
        return fill((List) getIterable(), value, start, end);
    }

    public static  List flattenDeep(final List list) {
        return flatten(list, false);
    }

    public List flattenDeep() {
        return flattenDeep((List) getIterable());
    }

    public static List pull(final List list, Object... values) {
        final List valuesList = Arrays.asList(values);
        list.removeIf(valuesList::contains);
        return list;
    }

    @SuppressWarnings("unchecked")
    public List pull(Object... values) {
        return pull((List) getIterable(), values);
    }

    public static List pullAt(final List list, final Integer... indexes) {
        final List result = new ArrayList<>();
        final List indexesList = Arrays.asList(indexes);
        int index = 0;
        for (final Iterator iterator = list.iterator(); iterator.hasNext(); ) {
            final Object object = iterator.next();
            if (indexesList.contains(index)) {
                result.add(object);
                iterator.remove();
            }
            index += 1;
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public List pullAt(final Integer... indexes) {
        return pullAt((List) getIterable(), indexes);
    }

    public static  List remove(final List list, final Predicate pred) {
        final List result = new ArrayList<>();
        for (final Iterator iterator = list.iterator(); iterator.hasNext(); ) {
            final T object = iterator.next();
            if (pred.test(object)) {
                result.add(object);
                iterator.remove();
            }
        }
        return result;
    }

    public List remove(final Predicate pred) {
        return remove((List) getIterable(), pred);
    }

    public static  List take(final Iterable iterable) {
        return first(newArrayList(iterable), 1);
    }

    public List take() {
        return take(getIterable());
    }

    public static  List takeRight(final Iterable iterable) {
        return last(newArrayList(iterable), 1);
    }

    public List takeRight() {
        return takeRight(getIterable());
    }

    public static  List take(final Iterable iterable, final Integer n) {
        return first(newArrayList(iterable), n);
    }

    public List take(final Integer n) {
        return take(getIterable(), n);
    }

    public static  List takeRight(final Iterable iterable, final Integer n) {
        return last(newArrayList(iterable), n);
    }

    public List takeRight(final Integer n) {
        return takeRight(getIterable(), n);
    }

    public static  List takeWhile(final Iterable iterable, final Predicate pred) {
        return first(newArrayList(iterable), findIndex(newArrayList(iterable), negate(pred)));
    }

    public List takeWhile(final Predicate pred) {
        return takeWhile(getIterable(), pred);
    }

    public static  List takeRightWhile(final Iterable iterable, final Predicate pred) {
        return reverse(takeWhile(reverse(iterable), pred));
    }

    public List takeRightWhile(final Predicate pred) {
        return takeRightWhile(getIterable(), pred);
    }

    @SuppressWarnings("unchecked")
    public static  List xor(final List... lists) {
        int index = -1;
        int length = lists.length;
        List result = null;
        while (++index < length) {
            final List array = lists[index];
            result =
                    result == null
                            ? array
                            : concat(difference(result, array), difference(array, result));
        }
        return uniq(result);
    }

    @SuppressWarnings("unchecked")
    public List xor(final List list) {
        return xor((List) getIterable(), list);
    }

    public static  List at(final List list, final Integer... indexes) {
        final List result = new ArrayList<>();
        final List indexesList = Arrays.asList(indexes);
        int index = 0;
        for (final T object : list) {
            if (indexesList.contains(index)) {
                result.add(object);
            }
            index += 1;
        }
        return result;
    }

    public List at(final Integer... indexes) {
        return at((List) getIterable(), indexes);
    }

    public static  Double average(final Iterable iterable) {
        T sum = sum(iterable);
        if (sum == null) {
            return null;
        }
        return sum.doubleValue() / size(iterable);
    }

    public static  Double average(
            final Iterable iterable, final Function func) {
        F sum = sum(iterable, func);
        if (sum == null) {
            return null;
        }
        return sum.doubleValue() / size(iterable);
    }

    public static  Double average(N[] array) {
        N sum = sum(array);
        if (sum == null) {
            return null;
        }
        return sum.doubleValue() / array.length;
    }

    public static Double average(java.math.BigDecimal first, java.math.BigDecimal second) {
        if (first == null || second == null) {
            return null;
        }
        return sum(first, second).doubleValue() / 2;
    }

    public static Double average(java.math.BigInteger first, java.math.BigInteger second) {
        if (first == null || second == null) {
            return null;
        }
        return sum(first, second).doubleValue() / 2;
    }

    public static Double average(Byte first, Byte second) {
        if (first == null || second == null) {
            return null;
        }
        return sum(first, second).doubleValue() / 2;
    }

    public static Double average(Double first, Double second) {
        if (first == null || second == null) {
            return null;
        }
        return sum(first, second) / 2;
    }

    public static Double average(Float first, Float second) {
        if (first == null || second == null) {
            return null;
        }
        return sum(first, second).doubleValue() / 2;
    }

    public static Double average(Integer first, Integer second) {
        if (first == null || second == null) {
            return null;
        }
        return sum(first, second).doubleValue() / 2;
    }

    public static Double average(Long first, Long second) {
        if (first == null || second == null) {
            return null;
        }
        return sum(first, second).doubleValue() / 2;
    }

    public static  T sum(final Iterable iterable) {
        T result = null;
        for (final T item : iterable) {
            result = add(result, item);
        }
        return result;
    }

    public static  F sum(
            final Iterable iterable, final Function func) {
        F result = null;
        for (final E item : iterable) {
            result = add(result, func.apply(item));
        }
        return result;
    }

    public static  N sum(N[] array) {
        N result = null;
        for (final N item : array) {
            result = add(result, item);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public  F sum() {
        return sum((List) getIterable());
    }

    @SuppressWarnings("unchecked")
    public  F sum(final Function func) {
        return sum((List) getIterable(), func);
    }

    @SuppressWarnings("unchecked")
    public static  T add(final T first, final T second) {
        if (first == null) {
            return second;
        } else if (second == null) {
            return first;
        } else if (first instanceof java.math.BigDecimal) {
            return (T) sum((java.math.BigDecimal) first, (java.math.BigDecimal) second);
        } else if (second instanceof java.math.BigInteger) {
            return (T) sum((java.math.BigInteger) first, (java.math.BigInteger) second);
        } else if (first instanceof Byte) {
            return (T) sum((Byte) first, (Byte) second);
        } else if (first instanceof Double) {
            return (T) sum((Double) first, (Double) second);
        } else if (first instanceof Float) {
            return (T) sum((Float) first, (Float) second);
        } else if (first instanceof Integer) {
            return (T) sum((Integer) first, (Integer) second);
        } else if (first instanceof Long) {
            return (T) sum((Long) first, (Long) second);
        } else if (first instanceof Short) {
            return (T) sum((Short) first, (Short) second);
        } else {
            throw new UnsupportedOperationException(
                    "Sum only supports official subclasses of Number");
        }
    }

    private static java.math.BigDecimal sum(
            java.math.BigDecimal first, java.math.BigDecimal second) {
        return first.add(second);
    }

    private static java.math.BigInteger sum(
            java.math.BigInteger first, java.math.BigInteger second) {
        return first.add(second);
    }

    private static Byte sum(Byte first, Byte second) {
        return (byte) (first + second);
    }

    private static Double sum(Double first, Double second) {
        return first + second;
    }

    private static Float sum(Float first, Float second) {
        return first + second;
    }

    private static Integer sum(Integer first, Integer second) {
        return first + second;
    }

    private static Long sum(Long first, Long second) {
        return first + second;
    }

    private static Short sum(Short first, Short second) {
        return (short) (first + second);
    }

    @SuppressWarnings("unchecked")
    public static  T subtract(final T... values) {
        if (values.length == 0) {
            return null;
        }
        T result = values[0];
        for (int i = 1; i < values.length; i++) {
            if (result instanceof java.math.BigDecimal) {
                java.math.BigDecimal value = (java.math.BigDecimal) values[i];
                result = add(result, (T) value.negate());
            } else if (result instanceof java.math.BigInteger) {
                java.math.BigInteger value = (java.math.BigInteger) values[i];
                result = add(result, (T) value.negate());
            } else if (result instanceof Byte) {
                result = add(result, (T) Byte.valueOf((byte) (values[i].byteValue() * -1)));
            } else if (result instanceof Double) {
                result = add(result, (T) Double.valueOf(values[i].doubleValue() * -1));
            } else if (result instanceof Float) {
                result = add(result, (T) Float.valueOf(values[i].floatValue() * -1));
            } else if (result instanceof Integer) {
                result = add(result, (T) Integer.valueOf(values[i].intValue() * -1));
            } else if (result instanceof Long) {
                result = add(result, (T) Long.valueOf(values[i].longValue() * -1));
            } else if (result instanceof Short) {
                result = add(result, (T) Short.valueOf((short) (values[i].shortValue() * -1)));
            } else {
                throw new UnsupportedOperationException(
                        "Subtract only supports official subclasses of Number");
            }
        }
        return result;
    }

    public static  double mean(final Iterable iterable) {
        T result = null;
        int count = 0;
        for (final T item : iterable) {
            result = add(result, item);
            count += 1;
        }
        if (result == null) {
            return 0d;
        }
        return result.doubleValue() / count;
    }

    @SuppressWarnings("unchecked")
    public double mean() {
        return mean((Iterable) getIterable());
    }

    @SuppressWarnings("unchecked")
    public static  double median(final Iterable iterable) {
        final List result = newArrayList((Collection) iterable);
        final int size = size(iterable);
        if (size == 0) {
            throw new IllegalArgumentException("Iterable cannot be empty");
        }
        if (size % 2 != 0) {
            return result.get(size / 2).doubleValue();
        }
        return (result.get(size / 2 - 1).doubleValue() + result.get(size / 2).doubleValue()) / 2;
    }

    @SuppressWarnings("unchecked")
    public double median() {
        return median((Iterable) getIterable());
    }

    public static String camelCase(final String string) {
        return createCompounder(
                        (result, word, index) -> {
                            final String localWord = word.toLowerCase(Locale.getDefault());
                            return result
                                    + (index > 0
                                            ? localWord
                                                            .substring(0, 1)
                                                            .toUpperCase(Locale.getDefault())
                                                    + localWord.substring(1)
                                            : localWord);
                        })
                .apply(string);
    }

    public static String lowerFirst(final String string) {
        return createCaseFirst("toLowerCase").apply(string);
    }

    public static String upperFirst(final String string) {
        return createCaseFirst("toUpperCase").apply(string);
    }

    public static String capitalize(final String string) {
        return upperFirst(baseToString(string));
    }

    public static String uncapitalize(final String string) {
        return lowerFirst(baseToString(string));
    }

    private static String baseToString(String value) {
        return value == null ? "" : value;
    }

    public static String deburr(final String string) {
        final String localString = baseToString(string);
        final StringBuilder sb = new StringBuilder();
        for (final String str : localString.split("")) {
            if (RE_LATIN_1.matcher(str).matches()) {
                sb.append(DEBURRED_LETTERS.get(str));
            } else {
                sb.append(str);
            }
        }
        return sb.toString();
    }

    public static List words(final String string) {
        final String localString = baseToString(string);
        final List result = new ArrayList<>();
        final java.util.regex.Matcher matcher = reWords.matcher(localString);
        while (matcher.find()) {
            result.add(matcher.group());
        }
        return result;
    }

    private static Function createCompounder(
            final Function3 callback) {
        return string -> {
            int index = -1;
            List array = words(deburr(string));
            int length = array.size();
            String result = "";

            while (++index < length) {
                result = callback.apply(result, array.get(index), index);
            }
            return result;
        };
    }

    private static Function createCaseFirst(final String methodName) {
        return string -> {
            final String localString = baseToString(string);
            final String chr = localString.isEmpty() ? "" : localString.substring(0, 1);
            final String trailing = localString.length() > 1 ? localString.substring(1) : "";
            return Underscore.invoke(Collections.singletonList(chr), methodName).get(0) + trailing;
        };
    }

    public static boolean endsWith(final String string, final String target) {
        return endsWith(string, target, null);
    }

    public static boolean endsWith(
            final String string, final String target, final Integer position) {
        if (string == null || target == null) {
            return false;
        }
        final String localString = baseToString(string);

        final int length = localString.length();
        final int fixedPosition = position == null || position < 0 ? 0 : position;
        final int localPosition = position == null ? length : Math.min(fixedPosition, length);

        final int localPosition2 = localPosition - target.length();
        return localPosition2 >= 0 && localString.indexOf(target, localPosition2) == localPosition2;
    }

    public static String kebabCase(final String string) {
        return createCompounder(
                        (result, word, index) ->
                                result
                                        + (index > 0 ? "-" : "")
                                        + word.toLowerCase(Locale.getDefault()))
                .apply(string);
    }

    public static String repeat(final String string, final int length) {
        final StringBuilder result = new StringBuilder();
        final StringBuilder localString = new StringBuilder(baseToString(string));
        if (length < 1 || string == null) {
            return result.toString();
        }
        int n = length;
        do {
            if (n % 2 != 0) {
                result.append(localString);
            }
            n = (int) Math.floor(n / (double) 2);
            localString.append(localString);
        } while (n > 0);
        return result.toString();
    }

    private static String createPadding(final String string, final int length, final String chars) {
        final int strLength = string.length();
        final int padLength = length - strLength;
        final String localChars = chars == null ? " " : chars;
        return repeat(localChars, (int) Math.ceil(padLength / (double) localChars.length()))
                .substring(0, padLength);
    }

    public static String pad(final String string, final int length) {
        return pad(string, length, null);
    }

    public static String pad(final String string, final int length, final String chars) {
        final String localString = baseToString(string);
        final int strLength = localString.length();
        if (strLength >= length) {
            return localString;
        }
        final double mid = (length - strLength) / (double) 2;
        final int leftLength = (int) Math.floor(mid);
        final int rightLength = (int) Math.ceil(mid);
        final String localChars = createPadding("", rightLength, chars);
        return localChars.substring(0, leftLength) + localString + localChars;
    }

    private static Function3 createPadDir(
            final boolean fromRight) {
        return (string, length, chars) -> {
            final String localString = baseToString(string);
            return (fromRight ? localString : "")
                    + createPadding(localString, length, chars)
                    + (fromRight ? "" : localString);
        };
    }

    public static String padStart(final String string, final Integer length) {
        return createPadDir(false).apply(string, length, null);
    }

    public static String padStart(final String string, final Integer length, final String chars) {
        return createPadDir(false).apply(string, length, chars);
    }

    public static String padEnd(final String string, final Integer length) {
        return createPadDir(true).apply(string, length, null);
    }

    public static String padEnd(final String string, final Integer length, final String chars) {
        return createPadDir(true).apply(string, length, chars);
    }

    public static String snakeCase(final String string) {
        return createCompounder(
                        (result, word, index) ->
                                result
                                        + (index > 0 ? "_" : "")
                                        + word.toLowerCase(Locale.getDefault()))
                .apply(string);
    }

    public static String startCase(final String string) {
        return createCompounder(
                        (result, word, index) ->
                                result
                                        + (index > 0 ? " " : "")
                                        + word.substring(0, 1).toUpperCase(Locale.getDefault())
                                        + word.substring(1))
                .apply(string);
    }

    public static boolean startsWith(final String string, final String target) {
        return startsWith(string, target, null);
    }

    public static boolean startsWith(
            final String string, final String target, final Integer position) {
        if (string == null || target == null) {
            return false;
        }
        final String localString = baseToString(string);

        final int length = localString.length();
        final int localPosition;
        if (position == null) {
            localPosition = 0;
        } else {
            final int from = position < 0 ? 0 : position;
            localPosition = Math.min(from, length);
        }

        return localString.lastIndexOf(target, localPosition) == localPosition;
    }

    private static int charsLeftIndex(final String string, final String chars) {
        int index = 0;
        final int length = string.length();
        while (index < length && chars.indexOf(string.charAt(index)) > -1) {
            index += 1;
        }
        return index == length ? -1 : index;
    }

    private static int charsRightIndex(final String string, final String chars) {
        int index = string.length() - 1;
        while (index >= 0 && chars.indexOf(string.charAt(index)) > -1) {
            index -= 1;
        }
        return index;
    }

    public static String trim(final String string) {
        return trim(string, null);
    }

    public static String trim(final String string, final String chars) {
        final String localString = baseToString(string);
        if (localString.isEmpty()) {
            return localString;
        }
        final String localChars;
        if (chars == null) {
            localChars = " ";
        } else {
            localChars = chars;
        }
        final int leftIndex = charsLeftIndex(localString, localChars);
        final int rightIndex = charsRightIndex(localString, localChars);
        return leftIndex > -1 ? localString.substring(leftIndex, rightIndex + 1) : localString;
    }

    public static String trimStart(final String string) {
        return trimStart(string, null);
    }

    public static String trimStart(final String string, final String chars) {
        final String localString = baseToString(string);
        if (localString.isEmpty()) {
            return localString;
        }
        final String localChars;
        if (chars == null) {
            localChars = " ";
        } else {
            localChars = chars;
        }
        final int leftIndex = charsLeftIndex(localString, localChars);
        return leftIndex > -1 ? localString.substring(leftIndex) : localString;
    }

    public static String trimEnd(final String string) {
        return trimEnd(string, null);
    }

    public static String trimEnd(final String string, final String chars) {
        final String localString = baseToString(string);
        if (localString.isEmpty()) {
            return localString;
        }
        final String localChars;
        if (chars == null) {
            localChars = " ";
        } else {
            localChars = chars;
        }
        final int rightIndex = charsRightIndex(localString, localChars);
        return rightIndex > -1 ? localString.substring(0, rightIndex + 1) : localString;
    }

    public static String trunc(final String string) {
        return trunc(string, DEFAULT_TRUNC_LENGTH);
    }

    public static String trunc(final String string, final Integer length) {
        final String localString = baseToString(string);
        final String omission = DEFAULT_TRUNC_OMISSION;
        if (length >= localString.length()) {
            return localString;
        }
        final int end = length - omission.length();
        final String result = localString.substring(0, end);
        return result + omission;
    }

    public static List stringToPath(final String string) {
        final List result = new ArrayList<>();
        final java.util.regex.Matcher matcher = RE_PROP_NAME.matcher(baseToString(string));
        while (matcher.find()) {
            result.add(matcher.group(1) == null ? matcher.group(0) : matcher.group(1));
        }
        return result;
    }

    private enum OperationType {
        GET,
        SET,
        UPDATE,
        REMOVE
    }

    @SuppressWarnings("unchecked")
    private static  T baseGetOrSetOrRemove(
            final Map object,
            final List paths,
            final Object value,
            OperationType operationType) {
        int index = 0;
        final int length = paths.size();

        Object localObject = object;
        Object savedLocalObject = null;
        String savedPath = null;
        while (localObject != null && index < length) {
            if (localObject instanceof Map) {
                Map.Entry mapEntry = getMapEntry((Map) localObject);
                if (mapEntry != null && "#item".equals(mapEntry.getKey())) {
                    localObject = mapEntry.getValue();
                    continue;
                }
                savedLocalObject = localObject;
                savedPath = paths.get(index);
                localObject = ((Map) localObject).get(paths.get(index));
            } else if (localObject instanceof List) {
                savedLocalObject = localObject;
                savedPath = paths.get(index);
                localObject = ((List) localObject).get(Integer.parseInt(paths.get(index)));
            } else {
                break;
            }
            index += 1;
        }
        if (index > 0 && index == length) {
            checkSetAndRemove(value, operationType, savedLocalObject, savedPath);
            return (T) localObject;
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private static void checkSetAndRemove(
            Object value, OperationType operationType, Object savedLocalObject, String savedPath) {
        if (operationType == OperationType.SET || operationType == OperationType.UPDATE) {
            if (savedLocalObject instanceof Map) {
                checkSetOrUpdate(
                        value, operationType, (Map) savedLocalObject, savedPath);
            } else {
                ((List) savedLocalObject).set(Integer.parseInt(savedPath), value);
            }
        } else if (operationType == OperationType.REMOVE) {
            if (savedLocalObject instanceof Map) {
                ((Map) savedLocalObject).remove(savedPath);
            } else {
                ((List) savedLocalObject).remove(Integer.parseInt(savedPath));
            }
        }
    }

    private static void checkSetOrUpdate(
            Object value,
            OperationType operationType,
            Map savedLocalObject,
            String savedPath) {
        if (operationType == OperationType.UPDATE && savedLocalObject.containsKey(savedPath)) {
            savedLocalObject.put(Underscore.uniqueId(savedPath), value);
        } else {
            savedLocalObject.put(savedPath, value);
        }
    }

    private static Map.Entry getMapEntry(Map map) {
        return map.isEmpty() ? null : (Map.Entry) map.entrySet().iterator().next();
    }

    public static  T get(final Map object, final String path) {
        return get(object, stringToPath(path));
    }

    public static  T get(final Map object, final List paths) {
        return baseGetOrSetOrRemove(object, paths, null, OperationType.GET);
    }

    public static String selectToken(final Map object, final String expression) {
        final String xml = toXml(object);
        try {
            final XPath xPath = XPathFactory.newInstance().newXPath();
            final org.w3c.dom.Document document = Xml.Document.createDocument(xml);
            final NodeList nodes =
                    (NodeList) xPath.compile(expression).evaluate(document, XPathConstants.NODESET);
            if (nodes.getLength() == 0) {
                return null;
            }
            return nodes.item(0).getNodeValue();
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    public static List selectTokens(
            final Map object, final String expression) {
        final String xml = toXml(object);
        try {
            final XPath xPath = XPathFactory.newInstance().newXPath();
            final org.w3c.dom.Document document = Xml.Document.createDocument(xml);
            final NodeList nodes =
                    (NodeList) xPath.compile(expression).evaluate(document, XPathConstants.NODESET);
            final List result = new ArrayList<>();
            for (int i = 0; i < nodes.getLength(); i++) {
                result.add(nodes.item(i).getNodeValue());
            }
            return result;
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    public static  T set(final Map object, final String path, Object value) {
        return set(object, stringToPath(path), value);
    }

    public static  T set(
            final Map object, final List paths, Object value) {
        return baseGetOrSetOrRemove(object, paths, value, OperationType.SET);
    }

    public static  T update(final Map object, final String path, Object value) {
        return update(object, stringToPath(path), value);
    }

    public static  T update(
            final Map object, final List paths, Object value) {
        return baseGetOrSetOrRemove(object, paths, value, OperationType.UPDATE);
    }

    public static  T remove(final Map object, final String path) {
        return remove(object, stringToPath(path));
    }

    public static  T remove(final Map object, final List paths) {
        return baseGetOrSetOrRemove(object, paths, null, OperationType.REMOVE);
    }

    public static Map rename(
            final Map map, final String oldKey, final String newKey) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            if (entry.getKey().equals(oldKey)) {
                outMap.put(newKey, makeObjectForRename(entry.getValue(), oldKey, newKey));
            } else {
                outMap.put(entry.getKey(), makeObjectForRename(entry.getValue(), oldKey, newKey));
            }
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeObjectForRename(
            Object value, final String oldKey, final String newKey) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(
                        item instanceof Map
                                ? rename((Map) item, oldKey, newKey)
                                : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = rename((Map) value, oldKey, newKey);
        } else {
            result = value;
        }
        return result;
    }

    public static Map setValue(
            final Map map, final String key, final Object newValue) {
        return setValue(map, key, (key1, value) -> newValue);
    }

    public static Map setValue(
            final Map map,
            final String key,
            final BiFunction newValue) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            if (entry.getKey().equals(key)) {
                outMap.put(
                        key,
                        makeObjectForSetValue(
                                newValue.apply(key, entry.getValue()), key, newValue));
            } else {
                outMap.put(entry.getKey(), makeObjectForSetValue(entry.getValue(), key, newValue));
            }
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeObjectForSetValue(
            Object value, final String key, final BiFunction newValue) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(
                        item instanceof Map
                                ? setValue((Map) item, key, newValue)
                                : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = setValue((Map) value, key, newValue);
        } else {
            result = value;
        }
        return result;
    }

    public static Map update(
            final Map map1, final Map map2) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map1.entrySet()) {
            String key = entry.getKey();
            Object value2 = entry.getValue();
            if (map2.containsKey(key)) {
                createKey(map2, key, value2, outMap);
            } else {
                outMap.put(key, value2);
            }
        }
        for (Map.Entry entry : map2.entrySet()) {
            String key = entry.getKey();
            Object value2 = entry.getValue();
            if (map1.containsKey(key)) {
                createKey(map1, key, value2, outMap);
            } else {
                outMap.put(key, value2);
            }
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static void createKey(
            final Map map, String key, Object value2, Map outMap) {
        Object value1 = map.get(key);
        if (value1 instanceof Map && value2 instanceof Map) {
            outMap.put(key, update((Map) value1, (Map) value2));
        } else if (value1 instanceof List && value2 instanceof List) {
            outMap.put(key, merge((List) value1, (List) value2));
        } else if (value1 instanceof List) {
            outMap.put(key, merge((List) value1, newArrayList(value2)));
        } else if (value2 instanceof List) {
            outMap.put(key, merge(newArrayList(value1), (List) value2));
        } else {
            outMap.put(key, value2);
        }
    }

    public static List merge(List list1, List list2) {
        List outList1 = newArrayList(list1);
        List outList2 = newArrayList(list2);
        outList2.removeAll(list1);
        outList1.addAll(outList2);
        return outList1;
    }

    public static class FetchResponse {
        private final boolean ok;
        private final int status;
        private final Map> headerFields;
        private final java.io.ByteArrayOutputStream stream;

        public FetchResponse(
                final boolean ok,
                final int status,
                final Map> headerFields,
                final java.io.ByteArrayOutputStream stream) {
            this.ok = ok;
            this.status = status;
            this.stream = stream;
            this.headerFields = headerFields;
        }

        public boolean isOk() {
            return ok;
        }

        public int getStatus() {
            return status;
        }

        public Map> getHeaderFields() {
            return headerFields;
        }

        public byte[] blob() {
            return stream.toByteArray();
        }

        public String text() {
            return stream.toString(StandardCharsets.UTF_8);
        }

        public Object json() {
            return Json.fromJson(text());
        }

        public Map jsonMap() {
            return fromJsonMap(text());
        }

        public Object xml() {
            return Xml.fromXml(text());
        }

        public Map xmlMap() {
            return fromXmlMap(text());
        }
    }

    public static long downloadUrl(final String url, final String fileName)
            throws IOException, URISyntaxException {
        final URL website = new URI(url).toURL();
        try (ReadableByteChannel rbc = Channels.newChannel(website.openStream());
                final FileOutputStream fos = new FileOutputStream(fileName)) {
            return fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        }
    }

    public static void decompressGzip(final String sourceFileName, final String targetFileName)
            throws IOException {
        try (GZIPInputStream gis =
                new GZIPInputStream(new FileInputStream(new File(sourceFileName)))) {
            Files.copy(gis, Paths.get(targetFileName));
        }
    }

    public static FetchResponse fetch(final String url) {
        return fetch(url, null, null, DEFAULT_HEADER_FIELDS, null, null);
    }

    public static FetchResponse fetch(
            final String url, final Integer connectTimeout, final Integer readTimeout) {
        return fetch(url, null, null, DEFAULT_HEADER_FIELDS, connectTimeout, readTimeout);
    }

    public static FetchResponse fetch(
            final String url,
            final Integer connectTimeout,
            final Integer readTimeout,
            final Integer retryCount,
            final Integer timeBetweenRetry) {
        return Fetch.fetch(
                url,
                null,
                null,
                DEFAULT_HEADER_FIELDS,
                connectTimeout,
                readTimeout,
                retryCount,
                timeBetweenRetry);
    }

    public static FetchResponse fetch(final String url, final String method, final String body) {
        return fetch(url, method, body, DEFAULT_HEADER_FIELDS, null, null);
    }

    public static class BaseHttpSslSocketFactory extends javax.net.ssl.SSLSocketFactory {
        private javax.net.ssl.SSLContext getSslContext() {
            return createEasySslContext();
        }

        @Override
        public java.net.Socket createSocket(
                java.net.InetAddress arg0, int arg1, java.net.InetAddress arg2, int arg3)
                throws java.io.IOException {
            return getSslContext().getSocketFactory().createSocket(arg0, arg1, arg2, arg3);
        }

        @Override
        public java.net.Socket createSocket(
                String arg0, int arg1, java.net.InetAddress arg2, int arg3)
                throws java.io.IOException {
            return getSslContext().getSocketFactory().createSocket(arg0, arg1, arg2, arg3);
        }

        @Override
        public java.net.Socket createSocket(java.net.InetAddress arg0, int arg1)
                throws java.io.IOException {
            return getSslContext().getSocketFactory().createSocket(arg0, arg1);
        }

        @Override
        public java.net.Socket createSocket(String arg0, int arg1) throws java.io.IOException {
            return getSslContext().getSocketFactory().createSocket(arg0, arg1);
        }

        @Override
        public String[] getSupportedCipherSuites() {
            return new String[] {};
        }

        @Override
        public String[] getDefaultCipherSuites() {
            return new String[] {};
        }

        @Override
        public java.net.Socket createSocket(
                java.net.Socket arg0, String arg1, int arg2, boolean arg3)
                throws java.io.IOException {
            return getSslContext().getSocketFactory().createSocket(arg0, arg1, arg2, arg3);
        }

        private javax.net.ssl.SSLContext createEasySslContext() {
            try {
                javax.net.ssl.SSLContext context = javax.net.ssl.SSLContext.getInstance("SSL");
                context.init(
                        null, new javax.net.ssl.TrustManager[] {MyX509TrustManager.manger}, null);
                return context;
            } catch (Exception ex) {
                throw new UnsupportedOperationException(ex);
            }
        }

        public static class MyX509TrustManager implements javax.net.ssl.X509TrustManager {

            static MyX509TrustManager manger = new MyX509TrustManager();

            public MyX509TrustManager() {
                // ignore MyX509TrustManager
            }

            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[] {};
            }

            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] chain, String authType) {
                // ignore checkClientTrusted
            }

            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] chain, String authType) {
                // ignore checkServerTrusted
            }
        }
    }

    public static void setupConnection(
            final java.net.HttpURLConnection connection,
            final String method,
            final Map> headerFields,
            final Integer connectTimeout,
            final Integer readTimeout)
            throws java.io.IOException {
        final String localMethod;
        if (SUPPORTED_HTTP_METHODS.contains(method)) {
            localMethod = method;
        } else {
            localMethod = "GET";
        }
        connection.setRequestMethod(localMethod);
        if (connectTimeout != null) {
            connection.setConnectTimeout(connectTimeout);
        }
        if (readTimeout != null) {
            connection.setReadTimeout(readTimeout);
        }
        if (connection instanceof javax.net.ssl.HttpsURLConnection) {
            ((javax.net.ssl.HttpsURLConnection) connection)
                    .setSSLSocketFactory(new BaseHttpSslSocketFactory());
        }
        if (headerFields != null) {
            for (final Map.Entry> header : headerFields.entrySet()) {
                connection.setRequestProperty(header.getKey(), join(header.getValue(), ";"));
            }
        }
    }

    public static FetchResponse fetch(
            final String url,
            final String method,
            final String body,
            final Map> headerFields,
            final Integer connectTimeout,
            final Integer readTimeout) {
        try {
            final java.net.URL localUrl = new java.net.URI(url).toURL();
            final java.net.HttpURLConnection connection =
                    (java.net.HttpURLConnection) localUrl.openConnection();
            setupConnection(connection, method, headerFields, connectTimeout, readTimeout);
            if (body != null) {
                connection.setDoOutput(true);
                final java.io.DataOutputStream outputStream =
                        new java.io.DataOutputStream(connection.getOutputStream());
                outputStream.write(body.getBytes(StandardCharsets.UTF_8));
                outputStream.close();
            }
            final int responseCode = connection.getResponseCode();
            final java.io.InputStream inputStream;
            if (responseCode < RESPONSE_CODE_400) {
                inputStream = connection.getInputStream();
            } else {
                inputStream = connection.getErrorStream();
            }
            final java.io.ByteArrayOutputStream result = new java.io.ByteArrayOutputStream();
            final byte[] buffer = new byte[BUFFER_LENGTH_1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                result.write(buffer, 0, length);
            }
            inputStream.close();
            return new FetchResponse(
                    responseCode < RESPONSE_CODE_400,
                    responseCode,
                    connection.getHeaderFields(),
                    result);
        } catch (java.io.IOException | java.net.URISyntaxException ex) {
            throw new UnsupportedOperationException(ex);
        }
    }

    public static class Fetch {
        private Fetch() {}

        @SuppressWarnings("java:S107")
        public static FetchResponse fetch(
                final String url,
                final String method,
                final String body,
                final Map> headerFields,
                final Integer connectTimeout,
                final Integer readTimeout,
                final Integer retryCount,
                final Integer timeBetweenRetry) {
            if (nonNull(retryCount)
                    && retryCount > 0
                    && retryCount <= 10
                    && nonNull(timeBetweenRetry)
                    && timeBetweenRetry > 0) {
                int localRetryCount = 0;
                UnsupportedOperationException saveException;
                do {
                    try {
                        final FetchResponse fetchResponse =
                                U.fetch(
                                        url,
                                        method,
                                        body,
                                        headerFields,
                                        connectTimeout,
                                        readTimeout);
                        if (fetchResponse.getStatus() == 429) {
                            saveException = new UnsupportedOperationException("Too Many Requests");
                        } else {
                            return fetchResponse;
                        }
                    } catch (UnsupportedOperationException ex) {
                        saveException = ex;
                    }
                    localRetryCount += 1;
                    try {
                        java.util.concurrent.TimeUnit.MILLISECONDS.sleep(timeBetweenRetry);
                    } catch (InterruptedException ex) {
                        saveException = new UnsupportedOperationException(ex);
                        Thread.currentThread().interrupt();
                    }
                } while (localRetryCount <= retryCount);
                throw saveException;
            }
            return U.fetch(url, method, body, headerFields, connectTimeout, readTimeout);
        }
    }

    public static List explode(final String input) {
        List result = new ArrayList<>();
        if (isNull(input)) {
            return result;
        }
        for (char character : input.toCharArray()) {
            result.add(String.valueOf(character));
        }
        return result;
    }

    public static String implode(final String[] input) {
        StringBuilder builder = new StringBuilder();
        for (String character : input) {
            if (nonNull(character)) {
                builder.append(character);
            }
        }
        return builder.toString();
    }

    public static String implode(final Iterable input) {
        StringBuilder builder = new StringBuilder();
        for (String character : input) {
            if (nonNull(character)) {
                builder.append(character);
            }
        }
        return builder.toString();
    }

    public String camelCase() {
        return camelCase(getString().get());
    }

    public String lowerFirst() {
        return lowerFirst(getString().get());
    }

    public String upperFirst() {
        return upperFirst(getString().get());
    }

    public String capitalize() {
        return capitalize(getString().get());
    }

    public String deburr() {
        return deburr(getString().get());
    }

    public boolean endsWith(final String target) {
        return endsWith(getString().get(), target);
    }

    public boolean endsWith(final String target, final Integer position) {
        return endsWith(getString().get(), target, position);
    }

    public String kebabCase() {
        return kebabCase(getString().get());
    }

    public String repeat(final int length) {
        return repeat(getString().get(), length);
    }

    public String pad(final int length) {
        return pad(getString().get(), length);
    }

    public String pad(final int length, final String chars) {
        return pad(getString().get(), length, chars);
    }

    public String padStart(final int length) {
        return padStart(getString().get(), length);
    }

    public String padStart(final int length, final String chars) {
        return padStart(getString().get(), length, chars);
    }

    public String padEnd(final int length) {
        return padEnd(getString().get(), length);
    }

    public String padEnd(final int length, final String chars) {
        return padEnd(getString().get(), length, chars);
    }

    public String snakeCase() {
        return snakeCase(getString().get());
    }

    public String startCase() {
        return startCase(getString().get());
    }

    public boolean startsWith(final String target) {
        return startsWith(getString().get(), target);
    }

    public boolean startsWith(final String target, final Integer position) {
        return startsWith(getString().get(), target, position);
    }

    public String trim() {
        return trim(getString().get());
    }

    public String trimWith(final String chars) {
        return trim(getString().get(), chars);
    }

    public String trimStart() {
        return trimStart(getString().get());
    }

    public String trimStartWith(final String chars) {
        return trimStart(getString().get(), chars);
    }

    public String trimEnd() {
        return trimEnd(getString().get());
    }

    public String trimEndWith(final String chars) {
        return trimEnd(getString().get(), chars);
    }

    public String trunc() {
        return trunc(getString().get());
    }

    public String trunc(final int length) {
        return trunc(getString().get(), length);
    }

    public String uncapitalize() {
        return uncapitalize(getString().get());
    }

    public List words() {
        return words(getString().get());
    }

    public static class LruCache {
        private static final boolean SORT_BY_ACCESS = true;
        private static final float LOAD_FACTOR = 0.75F;
        private final Map lruCacheMap;
        private final int capacity;

        public LruCache(int capacity) {
            this.capacity = capacity;
            this.lruCacheMap = new LinkedHashMap<>(capacity, LOAD_FACTOR, SORT_BY_ACCESS);
        }

        public V get(K key) {
            return lruCacheMap.get(key);
        }

        public void put(K key, V value) {
            if (lruCacheMap.containsKey(key)) {
                lruCacheMap.remove(key);
            } else if (lruCacheMap.size() >= capacity) {
                lruCacheMap.remove(lruCacheMap.keySet().iterator().next());
            }
            lruCacheMap.put(key, value);
        }
    }

    public static  LruCache createLruCache(final int capacity) {
        return new LruCache<>(capacity);
    }

    public static  List> createPermutationWithRepetition(
            final List list, final int permutationLength) {
        final long resultSize = (long) Math.pow(list.size(), permutationLength);
        final List> result = new ArrayList<>((int) resultSize);
        final int[] bitVector = new int[permutationLength];
        for (int index = 0; index < resultSize; index += 1) {
            List result2 = new ArrayList<>(permutationLength);
            for (int index2 = 0; index2 < permutationLength; index2 += 1) {
                result2.add(list.get(bitVector[index2]));
            }
            int index3 = 0;
            while (index3 < permutationLength && bitVector[index3] == list.size() - 1) {
                bitVector[index3] = 0;
                index3 += 1;
            }
            if (index3 < permutationLength) {
                bitVector[index3] += 1;
            }
            result.add(result2);
        }
        return result;
    }

    public List> createPermutationWithRepetition(final int permutationLength) {
        return createPermutationWithRepetition((List) value(), permutationLength);
    }

    protected static  List newArrayList(final Iterable iterable) {
        return Underscore.newArrayList(iterable);
    }

    public static  String join(final Iterable iterable, final String separator) {
        return Underscore.join(iterable, separator);
    }

    public static String toJson(Collection collection) {
        return Json.toJson(collection);
    }

    public static String toJson(Map map) {
        return Json.toJson(map);
    }

    public String toJson() {
        return Json.toJson((Collection) getIterable());
    }

    @SuppressWarnings("unchecked")
    public static  T fromXml(final String xml) {
        return (T) Xml.fromXml(xml);
    }

    public static Map fromXmlMap(final String xml) {
        return fromXmlMap(xml, Xml.FromType.FOR_CONVERT);
    }

    public static Map fromXmlMap(final String xml, final Xml.FromType fromType) {
        final Object object = Xml.fromXml(xml, fromType);
        return getStringObjectMap(object);
    }

    @SuppressWarnings("unchecked")
    public static  T fromXml(final String xml, final Xml.FromType fromType) {
        return (T) Xml.fromXml(xml, fromType);
    }

    @SuppressWarnings("unchecked")
    public static  T fromXmlMakeArrays(final String xml) {
        return (T) Xml.fromXmlMakeArrays(xml);
    }

    @SuppressWarnings("unchecked")
    public static  T fromXmlWithoutNamespaces(final String xml) {
        return (T) Xml.fromXmlWithoutNamespaces(xml);
    }

    public static Map fromXmlWithoutNamespacesMap(final String xml) {
        final Object object = Xml.fromXmlWithoutNamespaces(xml);
        return getStringObjectMap(object);
    }

    @SuppressWarnings("unchecked")
    public static  T fromXmlWithoutAttributes(final String xml) {
        return (T) Xml.fromXmlWithoutAttributes(xml);
    }

    @SuppressWarnings("unchecked")
    public static  T fromXmlWithoutNamespacesAndAttributes(final String xml) {
        return (T) Xml.fromXmlWithoutNamespacesAndAttributes(xml);
    }

    public static String toXml(Collection collection) {
        return Xml.toXml(collection);
    }

    public static String toXml(Map map) {
        return Xml.toXml(map);
    }

    @SuppressWarnings("unchecked")
    public static  T fromJson(String string) {
        return (T) Json.fromJson(string);
    }

    public Object fromJson() {
        return Json.fromJson(getString().get());
    }

    public static Map fromJsonMap(final String string) {
        final Object object = Json.fromJson(string);
        return getStringObjectMap(object);
    }

    public static Map fromJsonMap(final String string, final int maxDepth) {
        final Object object = Json.fromJson(string, maxDepth);
        return getStringObjectMap(object);
    }

    @SuppressWarnings("unchecked")
    private static Map getStringObjectMap(Object object) {
        final Map result;
        if (object instanceof Map) {
            result = (Map) object;
        } else {
            result = new LinkedHashMap<>();
            result.put("value", object);
        }
        return result;
    }

    public String toXml() {
        return Xml.toXml((Collection) getIterable());
    }

    public Object fromXml() {
        return Xml.fromXml(getString().get());
    }

    @SuppressWarnings("unchecked")
    public static String jsonToXml(
            String json,
            Xml.XmlStringBuilder.Step identStep,
            JsonToXmlMode mode,
            String newRootName) {
        Object object = Json.fromJson(json);
        final String result;
        if (object instanceof Map) {
            if (mode == JsonToXmlMode.FORCE_ATTRIBUTE_USAGE) {
                result = Xml.toXml(forceAttributeUsage((Map) object), identStep, newRootName);
            } else if (mode == JsonToXmlMode.DEFINE_ROOT_NAME) {
                result = Xml.toXml((Map) object, identStep, newRootName);
            } else if (mode == JsonToXmlMode.REPLACE_NULL_WITH_EMPTY_VALUE) {
                result = Xml.toXml(replaceNullWithEmptyValue((Map) object), identStep, newRootName);
            } else if (mode == JsonToXmlMode.REPLACE_EMPTY_STRING_WITH_EMPTY_VALUE) {
                result =
                        Xml.toXml(
                                replaceEmptyStringWithEmptyValue((Map) object),
                                identStep,
                                newRootName);
            } else if (mode == JsonToXmlMode.ADD_ROOT
                    && !Xml.XmlValue.getMapKey(object).equals(ROOT)) {
                final Map map = new LinkedHashMap<>();
                map.put(newRootName, object);
                result = Xml.toXml(map, identStep);
            } else if (mode == JsonToXmlMode.REMOVE_ARRAY_ATTRIBUTE) {
                result = Xml.toXml((Map) object, identStep, newRootName, Xml.ArrayTrue.SKIP);
            } else if (mode == JsonToXmlMode.REMOVE_ATTRIBUTES) {
                result =
                        Xml.toXml(
                                replaceNumberAndBooleanWithString((Map) object),
                                identStep,
                                newRootName,
                                Xml.ArrayTrue.SKIP);
            } else {
                result = Xml.toXml((Map) object, identStep);
            }
            return result;
        }
        return Xml.toXml((List) object, identStep);
    }

    public static String jsonToXml(String json, Xml.XmlStringBuilder.Step identStep) {
        return jsonToXml(json, identStep, null, ROOT);
    }

    public static String jsonToXml(String json, JsonToXmlMode mode) {
        return jsonToXml(json, Xml.XmlStringBuilder.Step.TWO_SPACES, mode, ROOT);
    }

    public static String jsonToXml(String json, JsonToXmlMode mode, String newRootName) {
        return jsonToXml(json, Xml.XmlStringBuilder.Step.TWO_SPACES, mode, newRootName);
    }

    public static String jsonToXml(String json, String newRootName) {
        return jsonToXml(
                json,
                Xml.XmlStringBuilder.Step.TWO_SPACES,
                JsonToXmlMode.DEFINE_ROOT_NAME,
                newRootName);
    }

    public static String jsonToXml(String json) {
        return jsonToXml(json, Xml.XmlStringBuilder.Step.TWO_SPACES, null, null);
    }

    @SuppressWarnings("unchecked")
    public static String xmlToJson(
            String xml, Json.JsonStringBuilder.Step identStep, XmlToJsonMode mode) {
        Object object = Xml.fromXml(xml);
        final String result;
        if (object instanceof Map) {
            if (mode == XmlToJsonMode.REPLACE_SELF_CLOSING_WITH_NULL) {
                result = Json.toJson(replaceSelfClosingWithNull((Map) object), identStep);
            } else if (mode == XmlToJsonMode.REPLACE_SELF_CLOSING_WITH_STRING) {
                result = Json.toJson(replaceSelfClosingWithEmpty((Map) object), identStep);
            } else if (mode == XmlToJsonMode.REPLACE_EMPTY_VALUE_WITH_NULL) {
                result = Json.toJson(replaceEmptyValueWithNull((Map) object), identStep);
            } else if (mode == XmlToJsonMode.REPLACE_MINUS_WITH_AT) {
                result = Json.toJson(replaceMinusWithAt((Map) object), identStep);
            } else if (mode == XmlToJsonMode.REPLACE_EMPTY_TAG_WITH_NULL_AND_MINUS_WITH_AT) {
                result =
                        Json.toJson(
                                replaceMinusWithAt(
                                        replaceEmptyValueWithNull(
                                                replaceSelfClosingWithNull((Map) object))),
                                identStep);
            } else if (mode == XmlToJsonMode.REPLACE_EMPTY_TAG_WITH_NULL) {
                result =
                        Json.toJson(
                                replaceEmptyValueWithNull(replaceSelfClosingWithNull((Map) object)),
                                identStep);
            } else if (mode == XmlToJsonMode.REPLACE_EMPTY_TAG_WITH_STRING) {
                result =
                        Json.toJson(
                                (Map)
                                        replaceEmptyValueWithEmptyString(
                                                replaceSelfClosingWithEmpty((Map) object)),
                                identStep);
            } else if (mode == XmlToJsonMode.REMOVE_FIRST_LEVEL) {
                result = Json.toJson(replaceFirstLevel((Map) object), identStep);
            } else if (mode == XmlToJsonMode.WITHOUT_NAMESPACES) {
                result = Json.toJson((Map) Xml.fromXmlWithoutNamespaces(xml), identStep);
            } else {
                result = Json.toJson((Map) object, identStep);
            }
            return result;
        }
        return Json.toJson((List) object, identStep);
    }

    public static String xmlToJson(String xml) {
        return xmlToJson(xml, Json.JsonStringBuilder.Step.TWO_SPACES, null);
    }

    public static String xmlToJson(String xml, Json.JsonStringBuilder.Step identStep) {
        return xmlToJson(xml, identStep, null);
    }

    public static String xmlToJson(String xml, XmlToJsonMode mode) {
        return xmlToJson(xml, Json.JsonStringBuilder.Step.TWO_SPACES, mode);
    }

    public static String xmlOrJsonToJson(String xmlOrJson, Json.JsonStringBuilder.Step identStep) {
        TextType textType = getTextType(xmlOrJson);
        final String result;
        if (textType == TextType.JSON) {
            result = getJsonString(identStep, fromJson(xmlOrJson));
        } else if (textType == TextType.XML) {
            result = getJsonString(identStep, fromXml(xmlOrJson));
        } else {
            result = xmlOrJson;
        }
        return result;
    }

    public static String xmlOrJsonToJson(String xmlOrJson) {
        return xmlOrJsonToJson(xmlOrJson, Json.JsonStringBuilder.Step.TWO_SPACES);
    }

    @SuppressWarnings("unchecked")
    private static String getJsonString(Json.JsonStringBuilder.Step identStep, Object object) {
        final String result;
        if (object instanceof Map) {
            result = Json.toJson((Map) object, identStep);
        } else {
            result = Json.toJson((List) object, identStep);
        }
        return result;
    }

    public static String xmlOrJsonToXml(String xmlOrJson, Xml.XmlStringBuilder.Step identStep) {
        TextType textType = getTextType(xmlOrJson);
        final String result;
        if (textType == TextType.JSON) {
            result = getXmlString(identStep, fromJson(xmlOrJson));
        } else if (textType == TextType.XML) {
            result = getXmlString(identStep, fromXml(xmlOrJson));
        } else {
            result = xmlOrJson;
        }
        return result;
    }

    public static String xmlOrJsonToXml(String xmlOrJson) {
        return xmlOrJsonToXml(xmlOrJson, Xml.XmlStringBuilder.Step.TWO_SPACES);
    }

    @SuppressWarnings("unchecked")
    private static String getXmlString(Xml.XmlStringBuilder.Step identStep, Object object) {
        final String result;
        if (object instanceof Map) {
            result = Xml.toXml((Map) object, identStep);
        } else {
            result = Xml.toXml((List) object, identStep);
        }
        return result;
    }

    public enum TextType {
        JSON,
        XML,
        OTHER
    }

    public static TextType getTextType(String text) {
        String trimmed = trim(text);
        final TextType textType;
        if (trimmed.startsWith("{") && trimmed.endsWith("}")
                || trimmed.startsWith("[") && trimmed.endsWith("]")) {
            textType = TextType.JSON;
        } else if (trimmed.startsWith("<") && trimmed.endsWith(">")) {
            textType = TextType.XML;
        } else {
            textType = TextType.OTHER;
        }
        return textType;
    }

    public static String formatJsonOrXml(String jsonOrXml, String identStep) {
        TextType textType = getTextType(jsonOrXml);
        final String result;
        if (textType == TextType.JSON) {
            result = formatJson(jsonOrXml, Json.JsonStringBuilder.Step.valueOf(identStep));
        } else if (textType == TextType.XML) {
            result = formatXml(jsonOrXml, Xml.XmlStringBuilder.Step.valueOf(identStep));
        } else {
            result = jsonOrXml;
        }
        return result;
    }

    public static String formatJsonOrXml(String jsonOrXml) {
        return formatJsonOrXml(jsonOrXml, "TWO_SPACES");
    }

    public static String formatJson(String json, Json.JsonStringBuilder.Step identStep) {
        return Json.formatJson(json, identStep);
    }

    public static String formatJson(String json) {
        return Json.formatJson(json);
    }

    public static String formatXml(String xml, Xml.XmlStringBuilder.Step identStep) {
        return Xml.formatXml(xml, identStep);
    }

    public static String formatXml(String xml) {
        return Xml.formatXml(xml);
    }

    public static String changeXmlEncoding(
            String xml, Xml.XmlStringBuilder.Step identStep, String encoding) {
        return Xml.changeXmlEncoding(xml, identStep, encoding);
    }

    public static String changeXmlEncoding(String xml, String encoding) {
        return Xml.changeXmlEncoding(xml, encoding);
    }

    public static Map removeMinusesAndConvertNumbers(Map map) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            final String newKey;
            if (entry.getKey().startsWith("-")) {
                newKey = entry.getKey().substring(1);
            } else {
                newKey = entry.getKey();
            }
            if (!entry.getKey().equals(selfClosing)
                    && !entry.getKey().equals("#omit-xml-declaration")) {
                outMap.put(newKey, makeObject(entry.getValue()));
            }
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeObject(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(
                        item instanceof Map
                                ? removeMinusesAndConvertNumbers((Map) item)
                                : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = removeMinusesAndConvertNumbers((Map) value);
        } else {
            String stringValue = String.valueOf(value);
            result = isJsonNumber(stringValue) ? Xml.stringToNumber(stringValue) : value;
        }
        return result;
    }

    public static boolean isJsonNumber(final String string) {
        boolean eFound = false;
        boolean periodValid = true;
        boolean pmValid = true;
        boolean numberEncountered = false;
        for (char ch : string.toCharArray()) {
            if (pmValid) {
                pmValid = false;
                if (ch == '-') {
                    continue;
                }
            }
            if (!eFound && (ch == 'e' || ch == 'E')) {
                eFound = true;
                periodValid = false;
                pmValid = true;
                numberEncountered = false;
                continue;
            }
            if (periodValid && ch == '.') {
                periodValid = false;
                continue;
            }
            if (ch < '0' || ch > '9') {
                return false;
            }
            numberEncountered = true;
        }
        return numberEncountered;
    }

    @SuppressWarnings("unchecked")
    public static Map replaceSelfClosingWithNull(Map map) {
        return (Map) replaceSelfClosingWithValue(map, null);
    }

    @SuppressWarnings("unchecked")
    public static Map replaceSelfClosingWithEmpty(Map map) {
        Object result = replaceSelfClosingWithValue(map, "");
        if (result instanceof Map) {
            return (Map) result;
        }
        return Collections.emptyMap();
    }

    @SuppressWarnings("unchecked")
    public static Object replaceSelfClosingWithValue(Map map, String value) {
        Object outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            if (selfClosing.equals(entry.getKey()) && "true".equals(entry.getValue())) {
                if (map.size() == 1) {
                    outMap = value;
                    break;
                }
            } else {
                ((Map) outMap)
                        .put(
                                String.valueOf(entry.getKey()),
                                makeObjectSelfClose(entry.getValue(), value));
            }
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeObjectSelfClose(Object value, String newValue) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(
                        item instanceof Map
                                ? replaceSelfClosingWithValue((Map) item, newValue)
                                : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceSelfClosingWithValue((Map) value, newValue);
        } else {
            result = value;
        }
        return result;
    }

    public static Map replaceMinusWithAt(Map map) {
        if (map == null) {
            return null;
        }
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(
                    String.valueOf(entry.getKey()).startsWith("-")
                            ? "@" + String.valueOf(entry.getKey()).substring(1)
                            : String.valueOf(entry.getKey()),
                    replaceMinusWithAtValue(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object replaceMinusWithAtValue(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(item instanceof Map ? replaceMinusWithAt((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceMinusWithAt((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Map replaceEmptyValueWithNull(Map map) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(String.valueOf(entry.getKey()), makeObjectEmptyValue(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeObjectEmptyValue(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(item instanceof Map ? replaceEmptyValueWithNull((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceEmptyValueWithNull((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Object replaceEmptyValueWithEmptyString(Map map) {
        if (map.isEmpty()) {
            return "";
        }
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(String.valueOf(entry.getKey()), makeObjectEmptyString(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeObjectEmptyString(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(
                        item instanceof Map ? replaceEmptyValueWithEmptyString((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceEmptyValueWithEmptyString((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Map forceAttributeUsage(Map map) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(
                    entry.getValue() instanceof Map
                                    || entry.getValue() instanceof List
                                    || String.valueOf(entry.getKey()).startsWith("-")
                            ? String.valueOf(entry.getKey())
                            : "-" + entry.getKey(),
                    makeAttributeUsage(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeAttributeUsage(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(item instanceof Map ? forceAttributeUsage((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = forceAttributeUsage((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Map replaceNullWithEmptyValue(Map map) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(
                    entry.getKey(),
                    entry.getValue() == null
                            ? new LinkedHashMap<>()
                            : makeReplaceNullValue(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeReplaceNullValue(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(item instanceof Map ? replaceNullWithEmptyValue((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceNullWithEmptyValue((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Map replaceEmptyStringWithEmptyValue(Map map) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(
                    entry.getKey(),
                    "".equals(entry.getValue())
                            ? new LinkedHashMap<>()
                            : makeReplaceEmptyString(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeReplaceEmptyString(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(
                        item instanceof Map ? replaceEmptyStringWithEmptyValue((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceEmptyStringWithEmptyValue((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Map replaceNumberAndBooleanWithString(Map map) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(
                    entry.getKey(),
                    entry.getValue() instanceof Boolean || entry.getValue() instanceof Number
                            ? String.valueOf(entry.getValue())
                            : makeReplaceNumberAndBoolean(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeReplaceNumberAndBoolean(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                if (item instanceof Map) {
                    values.add(replaceNumberAndBooleanWithString((Map) item));
                } else if (item instanceof Number || item instanceof Boolean || isNull(item)) {
                    values.add(String.valueOf(item));
                } else {
                    values.add(item);
                }
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceNumberAndBooleanWithString((Map) value);
        } else if (isNull(value)) {
            result = "null";
        } else {
            result = value;
        }
        return result;
    }

    public static Map replaceFirstLevel(Map map) {
        return replaceFirstLevel(map, 0);
    }

    @SuppressWarnings("unchecked")
    public static Map replaceFirstLevel(Map map, int level) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(entry.getKey(), makeReplaceFirstLevel(entry.getValue(), level + 1));
        }
        if (level == 0 && Xml.XmlValue.getMapValue(outMap) instanceof Map) {
            Map outMap2 = (Map) Xml.XmlValue.getMapValue(outMap);
            if (selfClosing.equals(Xml.XmlValue.getMapKey(outMap2))
                    && "true".equals(Xml.XmlValue.getMapValue(outMap2))) {
                outMap2.remove(selfClosing);
            }
            return outMap2;
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeReplaceFirstLevel(Object value, int level) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(item instanceof Map ? replaceFirstLevel((Map) item, level + 1) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceFirstLevel((Map) value, level + 1);
        } else {
            result = value;
        }
        return result;
    }

    public static Map replaceNilWithNull(Map map) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            Object outValue = makeReplaceNilWithNull(entry.getValue());
            if (outValue instanceof Map
                    && (nilKey.equals(Xml.XmlValue.getMapKey(outValue))
                            || Xml.XmlValue.getMapKey(outValue).endsWith(":nil"))
                    && "true".equals(Xml.XmlValue.getMapValue(outValue))
                    && ((Map) outValue).containsKey(selfClosing)
                    && "true".equals(((Map) outValue).get(selfClosing))) {
                outValue = null;
            }
            outMap.put(entry.getKey(), outValue);
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeReplaceNilWithNull(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(item instanceof Map ? replaceNilWithNull((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = replaceNilWithNull((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Map deepCopyMap(Map map) {
        Map outMap = new LinkedHashMap<>();
        for (Map.Entry entry : map.entrySet()) {
            outMap.put(entry.getKey(), makeDeepCopyMap(entry.getValue()));
        }
        return outMap;
    }

    @SuppressWarnings("unchecked")
    private static Object makeDeepCopyMap(Object value) {
        final Object result;
        if (value instanceof List) {
            List values = new ArrayList<>();
            for (Object item : (List) value) {
                values.add(item instanceof Map ? deepCopyMap((Map) item) : item);
            }
            result = values;
        } else if (value instanceof Map) {
            result = deepCopyMap((Map) value);
        } else {
            result = value;
        }
        return result;
    }

    public static Builder objectBuilder() {
        return new U.Builder();
    }

    public static class Builder {
        private final Map data;

        public Builder() {
            data = new LinkedHashMap<>();
        }

        public Builder add(final String key, final Object value) {
            data.put(key, value);
            return this;
        }

        public Builder add(final Object value) {
            data.put(String.valueOf(data.size()), value);
            return this;
        }

        public  T get(final String path) {
            return U.get(data, path);
        }

        public  T get(final List paths) {
            return U.get(data, paths);
        }

        public Builder set(final String path, final Object value) {
            U.set(data, path, value);
            return this;
        }

        public Builder set(final List paths, final Object value) {
            U.set(data, paths, value);
            return this;
        }

        public Builder remove(final String key) {
            U.remove(data, key);
            return this;
        }

        public Builder remove(final List keys) {
            U.remove(data, keys);
            return this;
        }

        public Builder clear() {
            data.clear();
            return this;
        }

        public boolean isEmpty() {
            return data.isEmpty();
        }

        public int size() {
            return data.size();
        }

        public Builder add(final Builder builder) {
            data.put(String.valueOf(data.size()), builder.build());
            return this;
        }

        public Builder add(final String key, final ArrayBuilder builder) {
            data.put(key, builder.build());
            return this;
        }

        public Builder add(final String key, final Builder builder) {
            data.put(key, builder.build());
            return this;
        }

        public Builder add(final Map map) {
            data.putAll(deepCopyMap(map));
            return this;
        }

        public Builder update(final Map map) {
            U.update(data, deepCopyMap(map));
            return this;
        }

        public Builder addNull(final String key) {
            data.put(key, null);
            return this;
        }

        @SuppressWarnings("unchecked")
        public Map build() {
            return (Map) ((LinkedHashMap) data).clone();
        }

        public String toXml() {
            return Xml.toXml(data);
        }

        public static Builder fromXml(final String xml) {
            final Builder builder = new Builder();
            builder.data.putAll(fromXmlMap(xml));
            return builder;
        }

        public static Builder fromMap(final Map map) {
            final Builder builder = new Builder();
            builder.data.putAll(deepCopyMap(map));
            return builder;
        }

        public String toJson() {
            return Json.toJson(data);
        }

        public static Builder fromJson(final String json) {
            final Builder builder = new Builder();
            builder.data.putAll(fromJsonMap(json));
            return builder;
        }

        public Chain toChain() {
            return new U.Chain<>(data.entrySet());
        }

        @Override
        public String toString() {
            return data.toString();
        }
    }

    public static ArrayBuilder arrayBuilder() {
        return new U.ArrayBuilder();
    }

    public static class ArrayBuilder {
        private final List data;

        public ArrayBuilder() {
            data = new ArrayList<>();
        }

        public ArrayBuilder add(final Object value) {
            data.add(value);
            return this;
        }

        public ArrayBuilder addNull() {
            data.add(null);
            return this;
        }

        public  T get(final String path) {
            return U.get(U.getStringObjectMap(data), "value." + path);
        }

        public  T get(final List paths) {
            List newPaths = new ArrayList<>();
            newPaths.add("value");
            newPaths.addAll(paths);
            return U.get(U.getStringObjectMap(data), newPaths);
        }

        public ArrayBuilder set(final int index, final Object value) {
            data.set(index, value);
            return this;
        }

        public ArrayBuilder remove(final int index) {
            data.remove(index);
            return this;
        }

        public ArrayBuilder clear() {
            data.clear();
            return this;
        }

        public boolean isEmpty() {
            return data.isEmpty();
        }

        public int size() {
            return data.size();
        }

        public ArrayBuilder add(final ArrayBuilder builder) {
            data.addAll(builder.build());
            return this;
        }

        public ArrayBuilder add(final Builder builder) {
            data.add(builder.build());
            return this;
        }

        @SuppressWarnings("unchecked")
        public ArrayBuilder merge(final List list) {
            U.merge(data, (List) ((ArrayList) list).clone());
            return this;
        }

        @SuppressWarnings("unchecked")
        public List build() {
            return (List) ((ArrayList) data).clone();
        }

        public String toXml() {
            return Xml.toXml(data);
        }

        public static ArrayBuilder fromXml(final String xml) {
            final ArrayBuilder builder = new ArrayBuilder();
            builder.data.addAll(U.>fromXml(xml));
            return builder;
        }

        public String toJson() {
            return Json.toJson(data);
        }

        public static ArrayBuilder fromJson(final String json) {
            final ArrayBuilder builder = new ArrayBuilder();
            builder.data.addAll(U.>fromJson(json));
            return builder;
        }

        public Chain toChain() {
            return new U.Chain<>(data);
        }

        @Override
        public String toString() {
            return data.toString();
        }
    }

    public static Map propertiesToMap(Properties properties) {
        Map map = new LinkedHashMap<>();
        if (properties != null && !properties.isEmpty()) {
            Enumeration enumProperties = properties.propertyNames();
            while (enumProperties.hasMoreElements()) {
                String name = (String) enumProperties.nextElement();
                map.put(name, properties.getProperty(name));
            }
        }
        return map;
    }

    public static Properties mapToProperties(Map map) {
        Properties properties = new Properties();
        if (map != null) {
            for (final Map.Entry entry : map.entrySet()) {
                if (!isNull(entry.getValue())) {
                    properties.put(entry.getKey(), String.valueOf(entry.getValue()));
                }
            }
        }
        return properties;
    }
}