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

org.apache.brooklyn.util.text.Strings Maven / Gradle / Ivy

Go to download

Utility classes and methods developed for Brooklyn but not dependendent on Brooklyn or much else

There is a newer version: 1.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.brooklyn.util.text;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import javax.annotation.Nullable;

import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.time.Time;

import com.google.common.base.CharMatcher;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;

public class Strings {

    /** The empty {@link String}. */
    public static final String EMPTY = "";

    /**
     * Checks if the given string is null or is an empty string.
     * Useful for pre-String.isEmpty.  And useful for StringBuilder etc.
     *
     * @param s the String to check
     * @return true if empty or null, false otherwise.
     *
     * @see #isNonEmpty(CharSequence)
     * @see #isBlank(CharSequence)
     * @see #isNonBlank(CharSequence)
     */
    public static boolean isEmpty(CharSequence s) {
        // Note guava has com.google.common.base.Strings.isNullOrEmpty(String),
        // but that is just for String rather than CharSequence
        return s == null || s.length()==0;
    }

    /**
     * Checks if the given string is empty or only consists of whitespace.
     *
     * @param s the String to check
     * @return true if blank, empty or null, false otherwise.
     *
     * @see #isEmpty(CharSequence)
     * @see #isNonEmpty(CharSequence)
     * @see #isNonBlank(CharSequence)
     */
    public static boolean isBlank(CharSequence s) {
        return isEmpty(s) || CharMatcher.WHITESPACE.matchesAllOf(s);
    }

    /**
     * The inverse of {@link #isEmpty(CharSequence)}.
     *
     * @param s the String to check
     * @return true if non empty, false otherwise.
     *
     * @see #isEmpty(CharSequence)
     * @see #isBlank(CharSequence)
     * @see #isNonBlank(CharSequence)
     */
    public static boolean isNonEmpty(CharSequence s) {
        return !isEmpty(s);
    }

    /**
     * The inverse of {@link #isBlank(CharSequence)}.
     *
     * @param s the String to check
     * @return true if non blank, false otherwise.
     *
     * @see #isEmpty(CharSequence)
     * @see #isNonEmpty(CharSequence)
     * @see #isBlank(CharSequence)
     */
    public static boolean isNonBlank(CharSequence s) {
        return !isBlank(s);
    }

    /** @return a {@link Maybe} object which is absent if the argument {@link #isBlank(CharSequence)} */
    public static  Maybe maybeNonBlank(T s) {
        if (isNonBlank(s)) return Maybe.of(s);
        return Maybe.absent();
    }

    /** throws IllegalArgument if string not empty; cf. guava Preconditions.checkXxxx */
    public static void checkNonEmpty(CharSequence s) {
        if (s==null) throw new IllegalArgumentException("String must not be null");
        if (s.length()==0) throw new IllegalArgumentException("String must not be empty");
    }
    /** throws IllegalArgument if string not empty; cf. guava Preconditions.checkXxxx */
    public static void checkNonEmpty(CharSequence s, String message) {
        if (isEmpty(s)) throw new IllegalArgumentException(message);
    }

    /**
     * Removes suffix from the end of the string. Returns string if it does not end with suffix.
     */
    public static String removeFromEnd(String string, String suffix) {
        if (isEmpty(string)) {
            return string;
        } else if (!isEmpty(suffix) && string.endsWith(suffix)) {
            return string.substring(0, string.length() - suffix.length());
        } else {
            return string;
        }
    }

    /**
     * As removeFromEnd, but repeats until all such suffixes are gone
     */
    public static String removeAllFromEnd(String string, String... suffixes) {
        if (isEmpty(string)) return string;
        int index = string.length();
        boolean anotherLoopNeeded = true;
        while (anotherLoopNeeded) {
            if (isEmpty(string)) return string;
            anotherLoopNeeded = false;
            for (String suffix : suffixes)
                if (!isEmpty(suffix) && string.startsWith(suffix, index - suffix.length())) {
                    index -= suffix.length();
                    anotherLoopNeeded = true;
                    break;
                }
        }
        return string.substring(0, index);
    }

    /**
     * Removes prefix from the beginning of string. Returns string if it does not begin with prefix.
     */
    public static String removeFromStart(String string, String prefix) {
        if (isEmpty(string)) {
            return string;
        } else if (!isEmpty(prefix) && string.startsWith(prefix)) {
            return string.substring(prefix.length());
        } else {
            return string;
        }
    }

    /**
     * As {@link #removeFromStart(String, String)}, repeating until all such prefixes are gone.
     */
    public static String removeAllFromStart(String string, String... prefixes) {
        int index = 0;
        boolean anotherLoopNeeded = true;
        while (anotherLoopNeeded) {
            if (isEmpty(string)) return string;
            anotherLoopNeeded = false;
            for (String prefix : prefixes) {
                if (!isEmpty(prefix) && string.startsWith(prefix, index)) {
                    index += prefix.length();
                    anotherLoopNeeded = true;
                    break;
                }
            }
        }
        return string.substring(index);
    }
    
    /**
     * Removes everything after the marker, optionally also removing the marker.
     * If marker not found the string is unchanged.
     */
    public static String removeAfter(String string, String marker, boolean includeMarker) {
        int i = string.indexOf(marker);
        if (i==-1) return string;
        return string.substring(0, i + (includeMarker ? marker.length() : 0));
    }
    
    /**
     * Removes everything before the marker, optionally also removing the marker.
     * If marker not found the string is unchanged.
     */
    public static String removeBefore(String string, String marker, boolean includeMarker) {
        int i = string.indexOf(marker);
        if (i==-1) return string;
        return string.substring(i + (includeMarker ? 0 : marker.length()));
    }
    
    /** convenience for {@link com.google.common.base.Joiner} */
    public static String join(Iterable list, String separator) {
        if (list==null) return null;
        boolean app = false;
        StringBuilder out = new StringBuilder();
        for (Object s: list) {
            if (app) out.append(separator);
            out.append(s);
            app = true;
        }
        return out.toString();
    }
    /** convenience for {@link com.google.common.base.Joiner} */
    public static String join(Object[] list, String separator) {
        boolean app = false;
        StringBuilder out = new StringBuilder();
        for (Object s: list) {
            if (app) out.append(separator);
            out.append(s);
            app = true;
        }
        return out.toString();
    }

    /** convenience for joining lines together */
    public static String lines(String ...lines) {
        if (lines==null) return null;
        return lines(Arrays.asList(lines));
    }
    
    /** convenience for joining lines together */
    public static String lines(Iterable lines) {
        if (lines==null) return null;
        return Joiner.on("\n").join(lines);
    }

    public static String removeLines(String multiline, Predicate patternToRemove) {
        if (multiline==null) return null;
        return lines(Iterables.filter(Arrays.asList(multiline.split("\n")), Predicates.not(patternToRemove)));
    }

    /** NON-REGEX - replaces all key->value entries from the replacement map in source (non-regex) */
    @SuppressWarnings("rawtypes")
    public static String replaceAll(String source, Map replacements) {
        for (Object rr: replacements.entrySet()) {
            Map.Entry r = (Map.Entry)rr;
            source = replaceAllNonRegex(source, ""+r.getKey(), ""+r.getValue());
        }
        return source;
    }

    /** NON-REGEX replaceAll - see the better, explicitly named {@link #replaceAllNonRegex(String, String, String)}. */
    public static String replaceAll(String source, String pattern, String replacement) {
        return replaceAllNonRegex(source, pattern, replacement);
    }

    /** 
     * Replaces all instances in source, of the given pattern, with the given replacement
     * (not interpreting any arguments as regular expressions).
     * 

* This is actually the same as the very ambiguous {@link String#replace(CharSequence, CharSequence)}, * which does replace all, but not using regex like the similarly ambiguous {@link String#replaceAll(String, String)} as. * Alternatively see {@link #replaceAllRegex(String, String, String)}. */ public static String replaceAllNonRegex(String source, String pattern, String replacement) { if (source==null) return source; StringBuilder result = new StringBuilder(source.length()); for (int i=0; i() { @Override public boolean apply(@Nullable Character input) { return input != null && Character.isJavaIdentifierPart(input); } }); /** * Returns a valid Java identifier name based on the input. * * Removes certain characterss (like apostrophe), replaces one or more invalid * characterss with {@literal _}, and prepends {@literal _} if the first character * is only valid as an identifier part (not start). *

* The result is usually unique to s, though this isn't guaranteed, for example if * all characters are invalid. For a unique identifier use {@link #makeValidUniqueJavaName(String)}. * * @see #makeValidUniqueJavaName(String) */ public static String makeValidJavaName(String s) { if (s==null) return "__null"; if (s.length()==0) return "__empty"; String name = IS_JAVA_IDENTIFIER_PART.negate().collapseFrom(CharMatcher.is('\'').removeFrom(s), '_'); if (!Character.isJavaIdentifierStart(s.charAt(0))) return "_" + name; return name; } /** * Returns a unique valid java identifier name based on the input. * * Translated as per {@link #makeValidJavaName(String)} but with {@link String#hashCode()} * appended where necessary to guarantee uniqueness. * * @see #makeValidJavaName(String) */ public static String makeValidUniqueJavaName(String s) { String name = makeValidJavaName(s); if (isEmpty(s) || IS_JAVA_IDENTIFIER_PART.matchesAllOf(s) || CharMatcher.is('\'').matchesNoneOf(s)) { return name; } else { return name + "_" + s.hashCode(); } } /** @see {@link Identifiers#makeRandomId(int)} */ public static String makeRandomId(int l) { return Identifiers.makeRandomId(l); } /** pads the string with 0's at the left up to len; no padding if i longer than len */ public static String makeZeroPaddedString(int i, int len) { return makePaddedString(""+i, len, "0", ""); } /** pads the string with "pad" at the left up to len; no padding if base longer than len */ public static String makePaddedString(String base, int len, String left_pad, String right_pad) { String s = ""+(base==null ? "" : base); while (s.length()0 || prec<=1) s="0"; else { s="0.0"; while (s.length()> 63) == 0) ? 1 : -1; // int e = (int)((bits >> 52) & 0x7ffL); // long m = (e == 0) ? // (bits & 0xfffffffffffffL) << 1 : // (bits & 0xfffffffffffffL) | 0x10000000000000L; // //s*m*2^(e-1075); int log = (int)Math.floor(Math.log10(x)); int numFractionDigits = (log>=prec ? 0 : prec-log-1); if (numFractionDigits>0) { //need decimal digits if (skipDecimalThreshhold>0) { int checkFractionDigits = 0; double multiplier = 1; while (checkFractionDigits < numFractionDigits) { if (Math.abs(x - Math.rint(x*multiplier)/multiplier)0 && s.length()>maxlen) { //too long: double signif = x/Math.pow(10,log); if (s.indexOf(getDefaultDecimalSeparator())>=0) { //have a decimal point; either we are very small 0.000001 //or prec is larger than maxlen if (Math.abs(x)<1 && useEForSmallNumbers) { //very small-- use alternate notation s = makeRealString(signif, -1, prec, -1) + "E"+log; } else { //leave it alone, user error or E not wanted } } else { //no decimal point, integer part is too large, use alt notation s = makeRealString(signif, -1, prec, -1) + "E"+log; } } } if (leftPadLen>s.length()) return makePaddedString(s, leftPadLen, " ", ""); else return s; } /** creates a string from a real number, with specified accuracy (more iff it comes for free, ie integer-part); * switches to E notation if needed to fit within maxlen; can be padded left up too (not useful) * @param x number to use * @param maxlen maximum length for the numeric string, if possible (-1 to suppress) * @param prec number of digits accuracy desired (more kept for integers) * @param leftPadLen will add spaces at left if necessary to make string this long (-1 to suppress) [probably not usef] * @return such a string */ public static String makeRealStringNearZero(double x, int maxlen, int prec, int leftPadLen) { if (Math.abs(x)<0.0000000001) x=0; NumberFormat df = DecimalFormat.getInstance(); //df.setMaximumFractionDigits(maxlen); df.setMinimumFractionDigits(0); //df.setMaximumIntegerDigits(prec); df.setMinimumIntegerDigits(1); df.setGroupingUsed(false); String s; if (x==0) { if (prec<=1) s="0"; else { s="0.0"; while (s.length()> 63) == 0) ? 1 : -1; // int e = (int)((bits >> 52) & 0x7ffL); // long m = (e == 0) ? // (bits & 0xfffffffffffffL) << 1 : // (bits & 0xfffffffffffffL) | 0x10000000000000L; // //s*m*2^(e-1075); int log = (int)Math.floor(Math.log10(x)); int scale = (log>=prec ? 0 : prec-log-1); if (scale>0) { //need decimal digits double scale10 = Math.pow(10, scale); x = Math.rint(x*scale10)/scale10; df.setMinimumFractionDigits(scale); df.setMaximumFractionDigits(scale); } else { //x = Math.rint(x); df.setMaximumFractionDigits(0); } s = df.format(x); if (maxlen>0 && s.length()>maxlen) { //too long: double signif = x/Math.pow(10,log); if (s.indexOf('.')>=0) { //have a decimal point; either we are very small 0.000001 //or prec is larger than maxlen if (Math.abs(x)<1) { //very small-- use alternate notation s = makeRealString(signif, -1, prec, -1) + "E"+log; } else { //leave it alone, user error } } else { //no decimal point, integer part is too large, use alt notation s = makeRealString(signif, -1, prec, -1) + "E"+log; } } } if (leftPadLen>s.length()) return makePaddedString(s, leftPadLen, " ", ""); else return s; } /** returns the first word (whitespace delimited text), or null if there is none (input null or all whitespace) */ public static String getFirstWord(String s) { if (s==null) return null; int start = 0; while (start= s.length()) return null; while (end= 0) { if (!Character.isWhitespace(s.charAt(end))) break; end--; } int start = end; if (start < 0) return null; while (start >= 0) { if (Character.isWhitespace(s.charAt(start))) break; start--; } return s.substring(start+1, end+1); } /** returns the first word after the given phrase, or null if no such phrase; * if the character immediately after the phrase is not whitespace, the non-whitespace * sequence starting with that character will be returned */ public static String getFirstWordAfter(String context, String phrase) { if (context==null || phrase==null) return null; int index = context.indexOf(phrase); if (index<0) return null; return getFirstWord(context.substring(index + phrase.length())); } /** * searches in context for the given phrase, and returns the untrimmed remainder of the first line * on which the phrase is found */ public static String getRemainderOfLineAfter(String context, String phrase) { if (context == null || phrase == null) return null; int index = context.indexOf(phrase); if (index < 0) return null; int lineEndIndex = context.indexOf("\n", index); if (lineEndIndex <= 0) { return context.substring(index + phrase.length()); } else { return context.substring(index + phrase.length(), lineEndIndex); } } /** @deprecated use {@link Time#makeTimeStringRounded(long)} */ @Deprecated public static String makeTimeString(long utcMillis) { return Time.makeTimeStringRounded(utcMillis); } /** returns e.g. { "prefix01", ..., "prefix96" }; * see more functional NumericRangeGlobExpander for "prefix{01-96}" */ public static String[] makeArray(String prefix, int count) { String[] result = new String[count]; int len = (""+count).length(); for (int i=1; i<=count; i++) result[i-1] = prefix + makePaddedString("", len, "0", ""+i); return result; } public static String[] combineArrays(String[] ...arrays) { int totalLen = 0; for (String[] array : arrays) { if (array!=null) totalLen += array.length; } String[] result = new String[totalLen]; int i=0; for (String[] array : arrays) { if (array!=null) for (String s : array) { result[i++] = s; } } return result; } public static String toInitialCapOnly(String value) { if (value==null || value.length()==0) return value; return value.substring(0, 1).toUpperCase(Locale.ENGLISH) + value.substring(1).toLowerCase(Locale.ENGLISH); } public static String reverse(String name) { return new StringBuffer(name).reverse().toString(); } public static boolean isLowerCase(String s) { return s.toLowerCase().equals(s); } public static String makeRepeated(char c, int length) { StringBuilder result = new StringBuilder(length); for (int i = 0; i < length; i++) { result.append(c); } return result.toString(); } public static String trim(String s) { if (s==null) return null; return s.trim(); } public static String trimEnd(String s) { if (s==null) return null; return ("a"+s).trim().substring(1); } /** returns up to maxlen characters from the start of s */ public static String maxlen(String s, int maxlen) { return maxlenWithEllipsis(s, maxlen, ""); } /** as {@link #maxlenWithEllipsis(String, int, String) with "..." as the ellipsis */ public static String maxlenWithEllipsis(String s, int maxlen) { return maxlenWithEllipsis(s, maxlen, "..."); } /** as {@link #maxlenWithEllipsis(String, int) but replacing the last few chars with the given ellipsis */ public static String maxlenWithEllipsis(String s, int maxlen, String ellipsis) { if (s==null) return null; if (ellipsis==null) ellipsis=""; if (s.length()<=maxlen) return s; return s.substring(0, Math.max(maxlen-ellipsis.length(), 0))+ellipsis; } /** returns toString of the object if it is not null, otherwise null */ public static String toString(Object o) { return toStringWithValueForNull(o, null); } /** returns toString of the object if it is not null, otherwise the given value */ public static String toStringWithValueForNull(Object o, String valueIfNull) { if (o==null) return valueIfNull; return o.toString(); } public static boolean containsLiteralIgnoreCase(CharSequence input, CharSequence fragment) { if (input==null) return false; if (isEmpty(fragment)) return true; int lastValidStartPos = input.length()-fragment.length(); char f0u = Character.toUpperCase(fragment.charAt(0)); char f0l = Character.toLowerCase(fragment.charAt(0)); i: for (int i=0; i<=lastValidStartPos; i++) { char ii = input.charAt(i); if (ii==f0l || ii==f0u) { for (int j=1; j toStringSupplier(Object src) { return Suppliers.compose(Functions.toStringFunction(), Suppliers.ofInstance(src)); } /** wraps a call to {@link String#format(String, Object...)} in a toString, i.e. using %s syntax, * useful for places where we want deferred evaluation * (e.g. as message to {@link Preconditions} to skip concatenation when not needed) */ public static FormattedString format(String pattern, Object... args) { return new FormattedString(pattern, args); } /** returns "s" if the argument is not 1, empty string otherwise; useful when constructing plurals */ public static String s(int count) { return count==1 ? "" : "s"; } /** as {@link #s(int)} based on size of argument */ public static String s(@Nullable Map x) { return s(x==null ? 0 : x.size()); } /** as {@link #s(int)} based on size of argument */ public static String s(Iterable x) { if (x==null) return s(0); return s(x.iterator()); } /** as {@link #s(int)} based on size of argument */ public static String s(Iterator x) { int count = 0; if (x==null || !x.hasNext()) {} else { x.next(); count++; if (x.hasNext()) count++; } return s(count); } /** returns "ies" if the argument is not 1, "y" otherwise; useful when constructing plurals */ public static String ies(int count) { return count==1 ? "y" : "ies"; } /** as {@link #ies(int)} based on size of argument */ public static String ies(@Nullable Map x) { return ies(x==null ? 0 : x.size()); } /** as {@link #ies(int)} based on size of argument */ public static String ies(Iterable x) { if (x==null) return ies(0); return ies(x.iterator()); } /** as {@link #ies(int)} based on size of argument */ public static String ies(Iterator x) { int count = 0; if (x==null || !x.hasNext()) {} else { x.next(); count++; if (x.hasNext()) count++; } return ies(count); } /** converts a map of any objects to a map of strings, using {@link Object#toString()}, * with the second argument used where a value (or key) is null */ public static Map toStringMap(Map map, String valueIfNull) { if (map==null) return null; Map result = MutableMap.of(); for (Map.Entry e: map.entrySet()) { result.put(toStringWithValueForNull(e.getKey(), valueIfNull), toStringWithValueForNull(e.getValue(), valueIfNull)); } return result; } /** converts a list of any objects to a list of strings, using {@link Object#toString()}, * with the second argument used where an entry is null */ public static List toStringList(List list, String valueIfNull) { if (list==null) return null; List result = MutableList.of(); for (Object v: list) result.add(toStringWithValueForNull(v, valueIfNull)); return result; } /** * Tries to convert v to a list of strings. *

* If v is null then the method returns an empty immutable list. * If v is {@link Iterable} or an Object[] then toString is called on its elements. * If v is a {@link String} then a singleton list containing v is returned. * Otherwise the method throws an {@link IllegalArgumentException}. */ public static List toStringList(Object v) { if (v == null) return ImmutableList.of(); List result = Lists.newArrayList(); if (v instanceof Iterable) { for (Object o : (Iterable) v) { result.add(o.toString()); } } else if (v instanceof Object[]) { for (int i = 0; i < ((Object[]) v).length; i++) { result.add(((Object[]) v)[i].toString()); } } else if (v instanceof String) { result.add((String) v); } else { throw new IllegalArgumentException("Invalid type for List: " + v + " of type " + v.getClass()); } return result; } /** returns base repeated count times */ public static String repeat(String base, int count) { if (base==null) return null; StringBuilder result = new StringBuilder(); for (int i=0; i lengthComparator() { return Ordering.natural().onResultOf(StringFunctions.length()).compound(Ordering.natural()).nullsFirst(); } public static boolean isMultiLine(String s) { if (s==null) return false; if (s.indexOf('\n')>=0 || s.indexOf('\r')>=0) return true; return false; } public static String getFirstLine(String s) { int idx = s.indexOf('\n'); if (idx==-1) return s; return s.substring(0, idx); } /** looks for first section of text in following the prefix and, if present, before the suffix; * null if the prefix is not present in the string, and everything after the prefix if suffix is not present in the string; * if either prefix or suffix is null, it is treated as the start/end of the string */ public static String getFragmentBetween(String input, String prefix, String suffix) { if (input==null) return null; int index; if (prefix!=null) { index = input.indexOf(prefix); if (index==-1) return null; input = input.substring(index + prefix.length()); } if (suffix!=null) { index = input.indexOf(suffix); if (index>=0) input = input.substring(0, index); } return input; } public static int getWordCount(String phrase, boolean respectQuotes) { if (phrase==null) return 0; phrase = phrase.trim(); if (respectQuotes) return new QuotedStringTokenizer(phrase).remainderAsList().size(); else return Collections.list(new StringTokenizer(phrase)).size(); } public static char getDecimalSeparator(Locale locale) { DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); return dfs.getDecimalSeparator(); } public static char getDefaultDecimalSeparator() { return getDecimalSeparator(Locale.getDefault()); } /** replaces each sequence of whitespace in the first string with the replacement in the second string */ public static String collapseWhitespace(String x, String whitespaceReplacement) { if (x==null) return null; return replaceAllRegex(x, "\\s+", whitespaceReplacement); } public static String toLowerCase(String value) { if (value==null || value.length()==0) return value; return value.toLowerCase(Locale.ENGLISH); } /** * @return null if var is null or empty string, otherwise return var */ public static String emptyToNull(String var) { if (isNonEmpty(var)) { return var; } else { return null; } } /** Returns canonicalized string from the given object, made "unique" by: *

  • putting sets into the toString order *
  • appending a hash code if it's longer than the max (and the max is bigger than 0) */ public static String toUniqueString(Object x, int optionalMax) { if (x instanceof Iterable && !(x instanceof List)) { // unsorted collections should have a canonical order imposed MutableList result = MutableList.of(); for (Object xi: (Iterable)x) { result.add(toUniqueString(xi, optionalMax)); } Collections.sort(result); x = result.toString(); } if (x==null) return "{null}"; String xs = x.toString(); if (xs.length()<=optionalMax || optionalMax<=0) return xs; return maxlenWithEllipsis(xs, optionalMax-8)+"/"+Integer.toHexString(xs.hashCode()); } /** * Parses a string as comma separated values and returns a list of the entries with whitespace removed. * @param csv The "comma" separated values. * @param separatorRegex Regex of separatorRegex. */ public static List parseCsv(String csv, String separatorRegex) { List result = MutableList.of(); final String input = csv.trim(); if ("".equals(input)) { return result; } for (String value: input.split(separatorRegex)) { result.add(value.trim()); } return result; } public static List parseCsv(String csv) { return parseCsv(csv, ","); } public static int countOccurrences(String phrase, char target) { if (phrase == null) return 0; int result = 0; for (int i = 0; i < phrase.length(); i++) { if (target == phrase.charAt(i)) { result++; } } return result; } }




  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy