/* * Copyright (c) 2011 Stiftung Deutsches Elektronen-Synchrotron, * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY. * * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS. * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND * NON-INFRINGEMENT. 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. SHOULD THE SOFTWARE PROVE DEFECTIVE * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION, * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM */ package org.csstudio.domain.common.strings; import java.util.regex.Pattern; import javax.annotation.Nonnull; //import com.google.common.base.Function; //import com.google.common.collect.Collections2; //import com.google.common.collect.Lists; /** * String utilities (that are not present in the com.google.common collections). * * @author bknerr * @since 17.05.2011 */ public final class Strings { /** * Don't instantiate. */ private Strings() { // Empty } /** * Splits a source string on a comma, ignoring commas within quotes. * * Note, uneven numbers of quotes break the regex such that the first separator before the first * quote is not considered. * * @param source a string of comma separated entries * @return an iterable of strings, and an empty list if the string is blank */ @Nonnull public static String[] splitOnCommaIgnoreInQuotes(@Nonnull final String source) { return source.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); } // @Nonnull // public static Collection<String> splitIgnoreWithinQuotes(@Nonnull final String source, // @Nonnull final char sep) { // return splitIgnore(source, sep, '\"'); // } // /** // * Splits a string into substring on a separating character. Ignores those separators in // * within the ignore char (typically a quote '"') and those separators following on each other. // * Empty strings are filtered (any output string !isEmpty). // * // * @param source // * @param sep // * @param ignore // * @return // */ // @Nonnull // public static Collection<String> splitIgnore(@Nonnull final String source, // @Nonnull final char sep, // @Nonnull final char ign) { // // final List<String> result = Lists.newArrayList(); // final Matcher matcher = Pattern.compile(createRegEx(sep, ign)).matcher(source); // while (matcher.find()) { // final String cand = matcher.group(); // if (!cand.isEmpty()) { // result.add(cand); // } // } // return result; // } // /** // * Splits a string into substring on a separating character. Ignores those separators in // * within the ignore char (typically a quote '"') and those separators following on each other. // * Result strings are trimmed, which may result in empty strings. // * // * @param source // * @param sep // * @param trim // * @return // */ // @Nonnull // public static Collection<String> splitIgnoreWithinQuotesTrimmed(@Nonnull final String source, // @Nonnull final char sep, // @Nonnull final char trim) { // // return Collections2.transform(splitIgnoreWithinQuotes(source, sep), // new Function<String, String>() { // @Override // @Nonnull // public String apply(@Nonnull final String input) { // return Strings.trim(input, trim); // } // }); // } /** * Trims the source string of the given trim char. * * @param source * @param char * @return */ @Nonnull public static String trim(@Nonnull final String source, final char trim) { final String trimQuoted = Pattern.quote(String.valueOf(trim)); final String sourceWOLeadingChars = source.replaceAll("^" + trimQuoted + "+", ""); final String sourceWOLeadingAndTrailingChars = sourceWOLeadingChars.replaceAll(trimQuoted + "+$", ""); return sourceWOLeadingAndTrailingChars; } /** * Returns the size of the string measured in bytes. * Computes the number of unicode 'code points', which is equivalent to * {@link String#getBytes()}.length, but without having to copy the string to a byte array. * * @param s the string * @return the number of bytes of the unicode characters */ public static int getSizeInBytes(@Nonnull final String s) { // if (com.google.common.base.Strings.isNullOrEmpty(s)) { // return 0; // } return s.codePointCount(0, s.length()); } /** * Creates the magic regex that finds the fields of the source string. * Note that their order matters! * sep=, ign=X : ([^X,]*X[^X]+X[^X,]*) | ([^,X]*X[^,X]*)(?=[^X]*) | ([^X,]+) */ @Nonnull private static String createRegEx(@Nonnull final char sep, @Nonnull final char ign) { final String qSep = Pattern.quote(String.valueOf(sep)); final String qIgn = Pattern.quote(String.valueOf(ign)); return createRegExDoubleIgnore(qSep, qIgn) + "|" + createRegExSingleIgnoreWithLookAhead(qSep, qIgn) + "|" + createRegExWithoutSepsOrIgnore(qSep, qIgn); } /** * Regex matching anything between qSeps(,) with two qIgn(X): ,(abcXab,c,Xabc), OR ,(Xa,X), OR ,(abcXmX), */ @Nonnull private static String createRegExDoubleIgnore(@Nonnull final String qSep, @Nonnull final String qIgn) { return "([^" + qSep + qIgn + "]*" + qIgn + "[^" + qIgn + "]+" + qIgn + "[^" + qSep + qIgn + "]*)"; } /** * Regex matching anything between qSeps(,) with ONE qIgn(X) when there isn't any other qIgn later on: ,(abcXab),abaa,aa */ @Nonnull private static String createRegExSingleIgnoreWithLookAhead(@Nonnull final String qSep, @Nonnull final String qIgn) { return "([^" + qSep + qIgn + "]*" + qIgn + "[^" + qSep + qIgn + "]*)(?=[^" + qIgn + "]*)"; } /** * Regex matching anything between qSeps and qIgns: ,(abc), OR x(foo)x OR X(aa), OR ,(aa)X */ @Nonnull private static String createRegExWithoutSepsOrIgnore(@Nonnull final String qSep, @Nonnull final String qIgn) { return "([^" + qSep + qIgn + "]+)"; } /** * Checks if a String is whitespace, empty ("") or null. <code> StringUtils.isBlank(null) = true StringUtils.isBlank("") = true StringUtils.isBlank(" ") = true StringUtils.isBlank("bob") = false StringUtils.isBlank(" bob ") = false </code> * @param s * @return true if the string is null, empty, or does only contain whitespaces */ public static boolean isBlank(final String s) { return s == null || s.trim().equals(""); } }