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

functionalj.functions.StrFuncs Maven / Gradle / Ivy

There is a newer version: 1.0.17
Show newest version
// ============================================================================
// Copyright (c) 2017-2021 Nawapunth Manusitthipol (NawaMan - http://nawaman.net).
// ----------------------------------------------------------------------------
// MIT License
// 
// 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 functionalj.functions;

import static functionalj.function.Absent.__;

import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.StreamSupport;

import functionalj.function.Func;
import functionalj.function.Func1;
import functionalj.function.Func2;
import functionalj.function.Func3;
import functionalj.function.Func4;
import functionalj.function.Func5;
import functionalj.function.Func6;
import functionalj.list.FuncList;
import functionalj.stream.AsStreamPlus;
import functionalj.stream.IteratorPlus;
import functionalj.stream.StreamPlus;
import lombok.val;


// TODO - Should only contains methods that return functions or constance of functions

public class StrFuncs {
    
    @SuppressWarnings("unused")
    private static final Map indentTabs = new ConcurrentHashMap<>();
    
    /**
     * Return the string representation of the given object or null if the object is null.
     * 
     * @param inputObject the input objects.
     * @return the string representation of the input object.
     */
    public static String toStr(Object inputObject) {
        return (inputObject == null) ? null : String.valueOf(inputObject);
    }
    
    public static boolean isEmpty(String str) {
        return (str == null) || str.isEmpty();
    }
    
    public static boolean isBlank(String str) {
        return (str == null) || str.isEmpty() || str.trim().isEmpty();
    }
    
    /**
     * Returns a function that return the string representation of the given object or null if the object is null.
     * 
     * @param   the input data type.
     * @return the function.
     */
    public static  Function toStr() {
        return (i) -> toStr(i);
    }
    
    /**
     * Returns a predicate to check if the given string is not null and not empty.
     * 
     * @return the predicate.
     */
    public static Predicate strNotNullOrEmpty() {
        return str -> (str != null) && !str.isEmpty();
    }
    
    public static  Function, String> concat(Class clz) {
        return concat();
    }
    
    public static  Function, String> concat() {
        return strings -> strings.streamPlus().map(StrFuncs::toStr).join();
    }
    
    public static  BiFunction concat2() {
        return (i1, i2) -> toStr(i1) + toStr(i2);
    }
    public static  Func3 concat3() {
        return (i1, i2, i3) -> toStr(i1) + toStr(i2) + toStr(i3);
    }
    public static  Func4 concat4() {
        return (i1, i2, i3, i4) -> toStr(i1) + toStr(i2) + toStr(i3) + toStr(i4);
    }
    public static  Func5 concat5() {
        return (i1, i2, i3, i4, i5) -> toStr(i1) + toStr(i2) + toStr(i3) + toStr(i4) + toStr(i5);
    }
    public static  Func6 concat6() {
        return (i1, i2, i3, i4, i5, i6) -> toStr(i1) + toStr(i2) + toStr(i3) + toStr(i4) + toStr(i5) + toStr(i6);
    }
    
    public static  Func1, String> join(Class clz, String delimiter) {
        return join(delimiter);
    }
    
    public static  Func1, String> join(String delimiter) {
        return strings -> strings.streamPlus().map(StrFuncs::toStr).join(delimiter);
    }
    
    public static  Func2 join2(String delimiter) {
        return (i1, i2) -> 
                    toStr(i1) + delimiter + 
                    toStr(i2);
    }
    public static  Func3 join3(String delimiter) {
        return (i1, i2, i3) -> 
                    toStr(i1) + delimiter + 
                    toStr(i2) + delimiter + 
                    toStr(i3);
    }
    public static  Func4 join4(String delimiter) {
        return (i1, i2, i3, i4) -> 
                    toStr(i1) + delimiter + 
                    toStr(i2) + delimiter + 
                    toStr(i3) + delimiter + 
                    toStr(i4);
    }
    public static  Func5 join5(String delimiter) {
        return (i1, i2, i3, i4, i5) -> 
                    toStr(i1) + delimiter + 
                    toStr(i2) + delimiter + 
                    toStr(i3) + delimiter + 
                    toStr(i4) + delimiter + 
                    toStr(i5);
    }
    public static  Func6 join6(String delimiter) {
        return (i1, i2, i3, i4, i5, i6) -> 
                    toStr(i1) + delimiter + 
                    toStr(i2) + delimiter + 
                    toStr(i3) + delimiter + 
                    toStr(i4) + delimiter + 
                    toStr(i5) + delimiter + 
                    toStr(i6);
    }
    
    /**
     * Returns a function that add prefix and suffix to the given input.
     * 
     * @param   the input data type.
     * @param prefix  the prefix.
     * @param suffix  the suffix.
     * @return  the function.
     */
    @SuppressWarnings("unchecked")
    public static  Func1 wrapWith(String prefix, String suffix) {
        return (Func1)concat3().bind(prefix, __, suffix);
    }
    
    /**
     * Returns a function that add prefix to the given input.
     * 
     * @param   the input data type.
     * @param prefix  the prefix.
     * @return  the function.
     */
    @SuppressWarnings("unchecked")
    public static  Func1 prependWith(String prefix) {
        return (Func1)concat3().bind(prefix, __, "");
    }
    
    /**
     * Returns a function that add suffix to the given input.
     * 
     * @param   the input data type.
     * @param suffix  the suffix.
     * @return  the function.
     */
    @SuppressWarnings("unchecked")
    public static  Func1 appendWith(String suffix) {
        return (Func1)concat3().bind("", __, suffix);
    }
    
    public static  Func1 withPrefix(String prefix) {
        return input -> prefix + input;
    }
    
    public static  Func1 withSuffix(String suffix) {
        return input -> input + suffix;
    }
    
    public static  Func1 withWrap(String prefix, String suffix) {
        return input -> prefix + input + suffix;
    }
    
    public static  Func1 withFormat(String format) {
        return input -> String.format(format, input);
    }
    
    public static  Func1 withFormat(MessageFormat format) {
        return input -> format.format(input);
    }
    
    public static  Func1 withPattern(Pattern pattern, String defaultValue) {
        return input -> {
            val inputStr = String.valueOf(input);
            val matcher  = pattern.matcher(inputStr);
            return matcher.find()
                    ? matcher.group()
                    : defaultValue;
        };
    }
    public static  Func1 withPattern(Pattern pattern, Supplier defaultValueSupplier) {
        return input -> {
            val inputStr = String.valueOf(input);
            val matcher  = pattern.matcher(inputStr);
            return matcher.find()
                    ? matcher.group()
                    : defaultValueSupplier.get();
        };
    }
    public static  Func1 withPattern(Pattern pattern, Function defaultValueFunction) {
        return input -> {
            val inputStr = String.valueOf(input);
            val matcher  = pattern.matcher(inputStr);
            return matcher.find()
                    ? matcher.group()
                    : defaultValueFunction.apply(inputStr);
        };
    }
    public static  Func1 withPath(Pattern pattern, BiFunction defaultValueFunction) {
        return input -> {
            val inputStr = String.valueOf(input);
            val matcher  = pattern.matcher(inputStr);
            return matcher.find()
                    ? matcher.group()
                    : defaultValueFunction.apply(inputStr, matcher);
        };
    }
    
    public static  Func1 formatWith1(String template) {
        return (i1) -> String.format(template, i1);
    }
    
    public static  Func2 formatWith2(String template) {
        return (i1, i2) -> String.format(template, i1, i2);
    }
    
    public static  Func3 formatWith3(String template) {
        return (i1, i2, i3) -> String.format(template, i1, i2, i3);
    }
    
    public static  Func4 formatWith4(String template) {
        return (i1, i2, i3, i4) -> String.format(template, i1, i2, i3, i4);
    }
    
    public static  Func5 formatWith5(String template) {
        return (i1, i2, i3, i4, i5) -> String.format(template, i1, i2, i3, i4, i5);
    }
    
    public static  Func6 formatWith6(String template) {
        return (i1, i2, i3, i4, i5, i6) -> String.format(template, i1, i2, i3, i4, i5, i6);
    }
    
    public static  Func2 strFormat1() {
        return (template, i1) -> String.format(template, i1);
    }
    
    public static  Func3 strFormat2() {
        return (template, i1, i2) -> String.format(template, i1, i2);
    }
    public static  Func4 strFormat3() {
        return (template, i1, i2, i3) -> String.format(template, i1, i2, i3);
    }
    
    public static  Func5 strFormat4() {
        return (template, i1, i2, i3, i4) -> String.format(template, i1, i2, i3, i4);
    }
    
    public static  Func6 strFormat5() {
        return (template, i1, i2, i3, i4, i5) -> String.format(template, i1, i2, i3, i4, i5);
    }
    
    // TODO Rethink this at some point ..... should this be constance when no generic?
    
    public static Func3 replaceAll() {
        return (str, regex, replacement) -> str.replaceAll(regex, replacement);
    }
    public static Func1 replaceAll(String regex, String replacement) {
        return (str) -> str.replaceAll(regex, replacement);
    }
    
    public static String repeat(char chr, int count) {
        if (count <= 0)
            return "";
        val buffer = new StringBuffer();
        for (int i = 0; i < count; i++)
            buffer.append(chr);
        return buffer.toString();
    }
    public static String repeat(String str, int count) {
        if (count <= 0)
            return "";
        val buffer = new StringBuffer();
        for (int i = 0; i < count; i++)
            buffer.append(str);
        return buffer.toString();
    }
    
    public static StreamPlus split(String str, String regexDelimiter) {
        return split((CharSequence)str, regexDelimiter, -1);
    }
    public static StreamPlus split(CharSequence str, String regexDelimiter) {
        return split((CharSequence)str, regexDelimiter, -1);
    }
    public static StreamPlus split(String str, String regexDelimiter, RegExFlag flags) {
        return split((CharSequence)str, regexDelimiter, (flags != null) ? flags.getIntValue() : -1);
    }
    public static StreamPlus split(CharSequence str, String regexDelimiter, RegExFlag flags) {
        return split((CharSequence)str, regexDelimiter, (flags != null) ? flags.getIntValue() : -1);
    }
    public static StreamPlus split(String str, String regexDelimiter, int flags) {
        return split((CharSequence)str, regexDelimiter, flags);
    }
    public static StreamPlus split(CharSequence str, String regexDelimiter, int flags) {
        if (str == null || (str.length() == 0))
            return StreamPlus.empty();
        
        val pattern = (flags < 0) ? Pattern.compile(regexDelimiter) : Pattern.compile(regexDelimiter, flags);
        val matcher = pattern.matcher(str);
        val offset  = new AtomicInteger(0);
        val isLast  = new AtomicReference(null);
        Iterable iterable = new Iterable() {
            @Override
            public Iterator iterator() {
                return new Iterator() {
                    @Override
                    public boolean hasNext() {
                        val find = matcher.find();
                        if (!find) {
                            if (isLast.get() == null) {
                                isLast.set(true);
                                return true;
                            } else {
                                return false;
                            }
                        }
                        return find;
                    }
                    @Override
                    public String next() {
                        val isLastBoolean = isLast.get();
                        if (isLastBoolean == null) {
                            val start = matcher.start();
                            val end   = matcher.end();
                            val next  = str.subSequence(offset.get(), start);
                            offset.set(end);
                            return next.toString();
                        }
                        if (isLastBoolean == true) {
                            val next  = str.subSequence(offset.get(), str.length());
                            return next.toString();
                        }
                        return null;
                    }
                };
            }
        };
        return StreamPlus.from(StreamSupport.stream(iterable.spliterator(), false));
    }
    public static StreamPlus lines(String str) {
        return split(str, "(\n|\r\n?)");
    }
    
    public static String indent(String str) {
        if (str == null || str.isEmpty())
            return "";
        return "\t" + str.replaceAll("(\n|\r\n?)", "$1\t");
    }
    
    public static String leftPadding(String str, char prefix, int width) {
        if (str == null || str.isEmpty())
            return repeat(prefix, width);
        
        if (str.length() >= prefix)
            return str;
        
        return repeat(prefix, width - str.length()) + str;
    }
    
    public static  Func1> grab(String regex) {
        return strValue -> grab(strValue, regex, 0);
    }
    
    public static  Func1> grab(String regex, RegExFlag flags) {
        return strValue -> grab(strValue, regex, flags);
    }
    
    public static  Func1> grab(String regex, int patternFlags) {
        return strValue -> grab(strValue, regex, patternFlags);
    }
    
    public static FuncList grab(CharSequence strValue, String regex) {
        return grab(strValue, regex, 0);
    }
    
    public static FuncList grab(CharSequence strValue, String regex, RegExFlag flags) {
        return grab(strValue, regex, flags.getIntValue());
    }
    
    public static FuncList grab(CharSequence strValue, String regex, int patternFlags) {
        val pattern  = Pattern.compile(regex, patternFlags);
        return FuncList.from(FuncList.from(()->{
            val matcher  = pattern.matcher(strValue);
            try (val iterator = createMatchIterator(matcher)) {
                return iterator.stream();
            }
        }));
    }
    
    private static IteratorPlus createMatchIterator(final java.util.regex.Matcher matcher) {
        return new IteratorPlus() {
            @Override
            public Iterator asIterator() {
                return new Iterator() {
                    @Override
                    public boolean hasNext() {
                        return matcher.find();
                    }
                    @Override
                    public String next() {
                        return matcher.group();
                    }
                };
            }
        };
    }
    
    public static Func1 matches(String regex) {
        return str -> matches(str, regex, -1);
    }
    public static Func1 matches(String regex, RegExFlag flags) {
        return str -> matches(str, regex, flags.getIntValue());
    }
    public static Func1 matches(String regex, int flags) {
        return str -> matches(str, regex, flags);
    }
    public static RegExMatchResultStream matches(CharSequence str, String regex) {
        return matches(str, regex, -1);
    }
    public static RegExMatchResultStream matches(CharSequence str, String regex, RegExFlag flags) {
        return matches(str, regex, flags.getIntValue());
    }
    public static RegExMatchResultStream matches(CharSequence str, String regex, int flags) {
        if (str == null || (str.length() == 0))
            return RegExMatchResultStream.empty;
        
        val pattern = (flags < 0) ? Pattern.compile(regex) : Pattern.compile(regex, flags);
        val matcher = pattern.matcher(str);
        val source  = Func.lazy(()->str.toString());
        val index   = new AtomicInteger();
        
        Iterable iterable = new Iterable() {
            @Override
            public Iterator iterator() {
                return new Iterator() {
                    @Override
                    public boolean hasNext() {
                        return matcher.find();
                    }
                    @Override
                    public RegExMatchResult next() {
                        return new RegExMatchResult(source, pattern, index.get(), matcher.toMatchResult());
                    }
                };
            }
        };
        return RegExMatchResultStream.from(StreamSupport.stream(iterable.spliterator(), false));
    }
    public static String template(String str, Func1 replacer) {
        return template((CharSequence)str, replacer);
    }
    public static String template(CharSequence str, Func1 replacer) {
        if (str == null)
            return null;
        if (str == null || (str.length() == 0))
            return "";
        
        StringBuffer buffer = new StringBuffer();
        val flags   = 0;
        val pattern = Pattern.compile("\\$[a-zA-Z0-9_]++", flags);
        val matcher = pattern.matcher(str);
        while (matcher.find()) {
            val group       = matcher.group();
            val name        = group.substring(1, group.length());
            val replacement = String.valueOf(replacer.apply(name));
            matcher.appendReplacement(buffer, replacement);
        }
        matcher.appendTail(buffer);
        return buffer.toString();
    }
    
}