Repository: incubator-brooklyn Updated Branches: refs/heads/master e3b0389ff -> cd504e226
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/NaturalOrderComparator.java ---------------------------------------------------------------------- diff --cc brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/NaturalOrderComparator.java index 0000000,be53acc..d76244a mode 000000,100644..100644 --- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/NaturalOrderComparator.java +++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/NaturalOrderComparator.java @@@ -1,0 -1,165 +1,179 @@@ + /* + * 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. + * + + BROOKLYN NOTE: This is based on code from Pierre-Luc Paour, + adapted for the Brooklyn project in accordance with the original terms below. + Main changes are the package and edits for more recent Java compatibility. + + -- + + NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java. + Copyright (C) 2003 by Pierre-Luc Paour <[email protected]> + + Based on the C version by Martin Pool, of which this is more or less a straight conversion. + Copyright (C) 2000 by Martin Pool <[email protected]> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + */ + package org.apache.brooklyn.util.text; + + import java.util.Comparator; + -/** comparator which takes two strings and puts them in an order with special rules for numbers to be placed in numeric order; ++/** comparator which takes two strings and puts them in an order ++ * with special rules for numbers (whole number) to be placed in numeric order; + * e.g. "10">"9", including when those numbers occur in the midst of equal text; e.g. "a10" > "a9"; + * but not if the text differs; e.g. "a10" < "b9" + * <p> + * class is thread-safe. nulls not supported. (to support nulls, wrap in guava: + * <code>Ordering.from(NaturalOrderComparator.INSTANCE).nullsFirst()</code>) ++ * <p> ++ * NOTE: decimals are treated like a word-split, not a decimal point, i.e. 1.9 < 1.10 + */ + public class NaturalOrderComparator implements Comparator<String> { + + public static final NaturalOrderComparator INSTANCE = new NaturalOrderComparator(); + + int compareRight(String a, String b) + { + int bias = 0; + int ia = 0; + int ib = 0; + + // The longest run of digits wins. That aside, the greatest + // value wins, but we can't know that it will until we've scanned + // both numbers to know that they have the same magnitude, so we + // remember it in BIAS. + for (;; ia++, ib++) { + char ca = charAt(a, ia); + char cb = charAt(b, ib); + + if (!Character.isDigit(ca) + && !Character.isDigit(cb)) { + return bias; + } else if (!Character.isDigit(ca)) { + return -1; + } else if (!Character.isDigit(cb)) { + return +1; + } else if (ca < cb) { + if (bias == 0) { + bias = -1; + } + } else if (ca > cb) { + if (bias == 0) + bias = +1; + } else if (ca == 0 && cb == 0) { + return bias; + } + } + } + + public int compare(String a, String b) { + + int ia = 0, ib = 0; + int nza = 0, nzb = 0; + char ca, cb; + int result; + + while (true) { + // only count the number of zeroes leading the last number compared + nza = nzb = 0; + + ca = charAt(a, ia); cb = charAt(b, ib); + + // skip over leading spaces or zeros + while (Character.isSpaceChar(ca) || ca == '0') { + if (ca == '0') { + nza++; + } else { + // only count consecutive zeroes + nza = 0; + } + + ca = charAt(a, ++ia); + } + + while (Character.isSpaceChar(cb) || cb == '0') { + if (cb == '0') { + nzb++; + } else { + // only count consecutive zeroes + nzb = 0; + } + + cb = charAt(b, ++ib); + } + + // process run of digits + if (Character.isDigit(ca) && Character.isDigit(cb)) { + if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) { + return result; + } ++ } else if ((Character.isDigit(ca) || nza>0) && (Character.isDigit(cb) || nzb>0)) { ++ // both sides are numbers, but at least one is a sequence of zeros ++ if (nza==0) { ++ // b=0, a>0 ++ return 1; ++ } ++ if (nzb==0) { ++ // inverse ++ return -1; ++ } ++ // both sides were zero, continue to next check below + } + + if (ca == 0 && cb == 0) { + // The strings compare the same. Perhaps the caller + // will want to call strcmp to break the tie. + return nza - nzb; + } + + if (ca < cb) { + return -1; + } else if (ca > cb) { + return +1; + } + + ++ia; ++ib; + } + } + + static char charAt(String s, int i) { + if (i >= s.length()) { + return 0; + } else { + return s.charAt(i); + } + } + + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java ---------------------------------------------------------------------- diff --cc brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java index 0000000,626a312..d092abb mode 000000,100644..100644 --- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java +++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java @@@ -1,0 -1,945 +1,919 @@@ + /* + * 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.Supplier; + import com.google.common.base.Suppliers; + 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 <T extends CharSequence> Maybe<T> 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; + } + } + - /** removes the first suffix in the list which is present at the end of string - * and returns that string; ignores subsequent suffixes if a matching one is found; - * returns the original string if no suffixes are at the end - * @deprecated since 0.7.0 use {@link #removeFromEnd(String, String)} or {@link #removeAllFromEnd(String, String...)} - */ - @Deprecated - public static String removeFromEnd(String string, String ...suffixes) { - if (isEmpty(string)) return string; - for (String suffix : suffixes) - if (suffix!=null && string.endsWith(suffix)) return string.substring(0, string.length() - suffix.length()); - 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; + } + } + - /** removes the first prefix in the list which is present at the start of string - * and returns that string; ignores subsequent prefixes if a matching one is found; - * returns the original string if no prefixes match - * @deprecated since 0.7.0 use {@link #removeFromStart(String, String)} - */ - @Deprecated - public static String removeFromStart(String string, String ...prefixes) { - if (isEmpty(string)) return string; - for (String prefix : prefixes) - if (prefix!=null && string.startsWith(prefix)) return string.substring(prefix.length()); - 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); + } + + /** convenience for {@link com.google.common.base.Joiner} */ + public static String join(Iterable<? extends Object> 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) { + return Joiner.on("\n").join(Arrays.asList(lines)); + } + + /** 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). + * <p> + * 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<source.length(); ) { + if (source.substring(i).startsWith(pattern)) { + result.append(replacement); + i += pattern.length(); + } else { + result.append(source.charAt(i)); + i++; + } + } + return result.toString(); + } + + /** REGEX replacement -- explicit method name for readability, doing same as {@link String#replaceAll(String, String)}. */ + public static String replaceAllRegex(String source, String pattern, String replacement) { + return source.replaceAll(pattern, replacement); + } + + /** Valid non alphanumeric characters for filenames. */ + public static final String VALID_NON_ALPHANUM_FILE_CHARS = "-_."; + + /** + * Returns a valid filename based on the input. + * + * A valid filename starts with the first alphanumeric character, then include + * all alphanumeric characters plus those in {@link #VALID_NON_ALPHANUM_FILE_CHARS}, + * with any runs of invalid characters being replaced by {@literal _}. + * + * @throws NullPointerException if the input string is null. + * @throws IllegalArgumentException if the input string is blank. + */ + public static String makeValidFilename(String s) { + Preconditions.checkNotNull(s, "Cannot make valid filename from null string"); + Preconditions.checkArgument(isNonBlank(s), "Cannot make valid filename from blank string"); + return CharMatcher.anyOf(VALID_NON_ALPHANUM_FILE_CHARS).or(CharMatcher.JAVA_LETTER_OR_DIGIT) + .negate() + .trimAndCollapseFrom(s, '_'); + } + + /** + * A {@link CharMatcher} that matches valid Java identifier characters. + * + * @see Character#isJavaIdentifierPart(char) + */ + public static final CharMatcher IS_JAVA_IDENTIFIER_PART = CharMatcher.forPredicate(new Predicate<Character>() { + @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). + * <p> + * 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()<len) s=left_pad+s+right_pad; + return s; + } + + public static void trimAll(String[] s) { + for (int i=0; i<s.length; i++) + s[i] = (s[i]==null ? "" : s[i].trim()); + } + + /** 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 makeRealString(double x, int maxlen, int prec, int leftPadLen) { + return makeRealString(x, maxlen, prec, leftPadLen, 0.00000000001, true); + } + /** 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] + * @param skipDecimalThreshhold if positive it will not add a decimal part if the fractional part is less than this threshhold + * (but for a value 3.00001 it would show zeroes, e.g. with 3 precision and positive threshhold <= 0.00001 it would show 3.00); + * if zero or negative then decimal digits are always shown + * @param useEForSmallNumbers whether to use E notation for numbers near zero (e.g. 0.001) + * @return such a string + */ + public static String makeRealString(double x, int maxlen, int prec, int leftPadLen, double skipDecimalThreshhold, boolean useEForSmallNumbers) { + if (x<0) return "-"+makeRealString(-x, maxlen, prec, leftPadLen); + 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 (skipDecimalThreshhold>0 || prec<=1) s="0"; + else { + s="0.0"; + while (s.length()<prec+1) s+="0"; + } + } else { + // long bits= Double.doubleToLongBits(x); + // int s = ((bits >> 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)<skipDecimalThreshhold) + break; + checkFractionDigits++; + multiplier*=10; + } + numFractionDigits = checkFractionDigits; + } + df.setMinimumFractionDigits(numFractionDigits); + df.setMaximumFractionDigits(numFractionDigits); + } 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(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()<prec+1) s+="0"; + } + } else { + // long bits= Double.doubleToLongBits(x); + // int s = ((bits >> 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()) { + if (!Character.isWhitespace(s.charAt(start))) + break; + start++; + } + int end = start; + if (end >= s.length()) + return null; + while (end<s.length()) { + if (Character.isWhitespace(s.charAt(end))) + break; + end++; + } + return s.substring(start, end); + } + + /** returns the last word (whitespace delimited text), or null if there is none (input null or all whitespace) */ + public static String getLastWord(String s) { + if (s==null) return null; + int end = s.length()-1; + 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 <b>untrimmed</b> 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<fragment.length(); j++) { + if (Character.toLowerCase(input.charAt(i+j))!=Character.toLowerCase(fragment.charAt(j))) + continue i; + } + return true; + } + } + return false; + } + + public static boolean containsLiteral(CharSequence input, CharSequence fragment) { + if (input==null) return false; + if (isEmpty(fragment)) return true; + int lastValidStartPos = input.length()-fragment.length(); + char f0 = fragment.charAt(0); + i: for (int i=0; i<=lastValidStartPos; i++) { + char ii = input.charAt(i); + if (ii==f0) { + for (int j=1; j<fragment.length(); j++) { + if (input.charAt(i+j)!=fragment.charAt(j)) + continue i; + } + return true; + } + } + return false; + } + + /** Returns a size string using metric suffixes from {@link ByteSizeStrings#metric()}, e.g. 23.5MB */ + public static String makeSizeString(long sizeInBytes) { + return ByteSizeStrings.metric().makeSizeString(sizeInBytes); + } + + /** Returns a size string using ISO suffixes from {@link ByteSizeStrings#iso()}, e.g. 23.5MiB */ + public static String makeISOSizeString(long sizeInBytes) { + return ByteSizeStrings.iso().makeSizeString(sizeInBytes); + } + + /** Returns a size string using Java suffixes from {@link ByteSizeStrings#java()}, e.g. 23m */ + public static String makeJavaSizeString(long sizeInBytes) { + return ByteSizeStrings.java().makeSizeString(sizeInBytes); + } + + /** returns a configurable shortener */ + public static StringShortener shortener() { + return new StringShortener(); + } + + public static Supplier<String> 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 the tostring, and returning "null" for nulls + * @deprecated since 0.7.0 use {@link #toStringMap(Map, String)} to remove ambiguity about how to handle null */ + // NB previously the javadoc here was wrong, said it returned null not "null" + @Deprecated + public static Map<String, String> toStringMap(Map<?,?> map) { + return toStringMap(map, "null"); + } + /** 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<String, String> toStringMap(Map<?,?> map, String valueIfNull) { + if (map==null) return null; + Map<String,String> result = MutableMap.<String, String>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<String> toStringList(List<?> list, String valueIfNull) { + if (list==null) return null; + List<String> result = MutableList.of(); + for (Object v: list) result.add(toStringWithValueForNull(v, valueIfNull)); + 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<count; i++) + result.append(base); + return result.toString(); + } + + /** returns comparator which compares based on length, with shorter ones first (and null before that); + * in event of a tie, it uses the toString order */ + public static Ordering<String> lengthComparator() { + return Ordering.<Integer>natural().onResultOf(StringFunctions.length()).compound(Ordering.<String>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: + * <li> putting sets into the toString order + * <li> 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<String> 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()); + } + + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java ---------------------------------------------------------------------- diff --cc brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java index 0000000,1d75297..1ce4015 mode 000000,100644..100644 --- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java +++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java @@@ -1,0 -1,58 +1,82 @@@ + /* + * 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.collections; + + import org.apache.brooklyn.util.collections.CollectionFunctionals; + import org.testng.Assert; + import org.testng.annotations.Test; + ++import com.google.common.base.Predicates; + import com.google.common.collect.ImmutableList; + import com.google.common.collect.ImmutableMap; + + public class CollectionFunctionalsTest { + + @Test + public void testListSize() { + Assert.assertTrue(CollectionFunctionals.sizeEquals(2).apply(ImmutableList.of("x", "y"))); + Assert.assertFalse(CollectionFunctionals.sizeEquals(2).apply(null)); + Assert.assertTrue(CollectionFunctionals.sizeEquals(0).apply(ImmutableList.of())); + Assert.assertFalse(CollectionFunctionals.sizeEquals(0).apply(null)); + } + + @Test + public void testMapSize() { + Assert.assertTrue(CollectionFunctionals.<String>mapSizeEquals(2).apply(ImmutableMap.of("x", "1", "y", "2"))); + Assert.assertFalse(CollectionFunctionals.<String>mapSizeEquals(2).apply(null)); + Assert.assertTrue(CollectionFunctionals.mapSizeEquals(0).apply(ImmutableMap.of())); + Assert.assertFalse(CollectionFunctionals.mapSizeEquals(0).apply(null)); + } + + @Test + public void testMapSizeOfNull() { + Assert.assertEquals(CollectionFunctionals.mapSize().apply(null), null); + Assert.assertEquals(CollectionFunctionals.mapSize(-1).apply(null), (Integer)(-1)); + } + + @Test + public void testFirstElement() { + Assert.assertEquals(CollectionFunctionals.firstElement().apply(null), null); + Assert.assertEquals(CollectionFunctionals.firstElement().apply(ImmutableList.of("a")), "a"); + Assert.assertEquals(CollectionFunctionals.firstElement().apply(ImmutableList.of("a", "b", "c")), "a"); + } ++ ++ @Test ++ public void testAllAndAny() { ++ Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply( ++ MutableList.of(1, 1, 1)), true); ++ Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply( ++ MutableList.<Integer>of()), true); ++ Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply( ++ MutableList.of(1, 0, 1)), false); ++ Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply( ++ MutableList.of(0, 0, 0)), false); ++ ++ Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply( ++ MutableList.of(1, 1, 1)), true); ++ Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply( ++ MutableList.<Integer>of()), false); ++ Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply( ++ MutableList.of(1, 0, 1)), true); ++ Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply( ++ MutableList.of(0, 0, 0)), false); ++ ++ } ++ + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/ComparableVersionTest.java ---------------------------------------------------------------------- diff --cc brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/ComparableVersionTest.java index 0000000,16b07b8..a730ade mode 000000,100644..100644 --- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/ComparableVersionTest.java +++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/ComparableVersionTest.java @@@ -1,0 -1,54 +1,63 @@@ + /* + * 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 org.apache.brooklyn.util.text.NaturalOrderComparator; + import org.testng.Assert; + import org.testng.annotations.Test; + + public class ComparableVersionTest { + - public static final NaturalOrderComparator noc = new NaturalOrderComparator(); - ++ ComparableVersion v = new ComparableVersion("10.5.8"); ++ ComparableVersion v_rc2 = new ComparableVersion("10.5.8-rc2"); ++ + @Test + public void testBasicOnes() { - Assert.assertEquals(0, noc.compare("a", "a")); - Assert.assertTrue(noc.compare("a", "b") < 0); - Assert.assertTrue(noc.compare("b", "a") > 0); ++ Assert.assertTrue(v.isGreaterThanAndNotEqualTo("10.5")); ++ Assert.assertTrue(v.isGreaterThanOrEqualTo("10.5.8")); ++ Assert.assertFalse(v.isGreaterThanAndNotEqualTo("10.5.8")); ++ ++ Assert.assertTrue(v.isLessThanAndNotEqualTo("10.6")); ++ Assert.assertTrue(v.isLessThanOrEqualTo("10.5.8")); ++ Assert.assertFalse(v.isLessThanAndNotEqualTo("10.5.8")); ++ ++ Assert.assertTrue(v.isLessThanAndNotEqualTo("10.5.8.1")); + - Assert.assertTrue(noc.compare("9", "10") < 0); - Assert.assertTrue(noc.compare("10", "9") > 0); ++ Assert.assertTrue(v_rc2.isLessThanAndNotEqualTo("10.5.8-rc3")) ; ++ Assert.assertTrue(v_rc2.isGreaterThanAndNotEqualTo("10.5.8-rc1")); + - Assert.assertTrue(noc.compare("b10", "a9") > 0); - Assert.assertTrue(noc.compare("b9", "a10") > 0); ++ Assert.assertTrue(v_rc2.isGreaterThanAndNotEqualTo("10.5.8-beta1")==v_rc2.isGreaterThanAndNotEqualTo("10.5.8-beta3")); + - Assert.assertTrue(noc.compare(" 9", "10") < 0); - Assert.assertTrue(noc.compare("10", " 9") > 0); ++ Assert.assertTrue(v.isInRange("[10.5,10.6)")); ++ Assert.assertFalse(v.isInRange("[10.5,10.5.8)")); ++ Assert.assertTrue(v.isInRange("[10.5,)")); ++ Assert.assertTrue(v.isInRange("[9,)")); ++ Assert.assertFalse(v.isInRange("(10.5.8,)")); ++ Assert.assertFalse(v.isInRange("[10.6,)")); ++ Assert.assertTrue(v.isInRange("[,11)")); ++ Assert.assertTrue(v.isInRange("[,]")); + } + - @Test - public void testVersionNumbers() { - Assert.assertEquals(0, noc.compare("10.5.8", "10.5.8")); - Assert.assertTrue(noc.compare("10.5", "9.9") > 0); - Assert.assertTrue(noc.compare("10.5.1", "10.5") > 0); - Assert.assertTrue(noc.compare("10.5.1", "10.6") < 0); - Assert.assertTrue(noc.compare("10.5.1-1", "10.5.1-0") > 0); - } ++ @Test(expectedExceptions={IllegalArgumentException.class}) ++ public void testError1() { v.isInRange("10.5"); } ++ @Test(expectedExceptions={IllegalArgumentException.class}) ++ public void testError2() { v.isInRange("[10.5"); } ++ @Test(expectedExceptions={IllegalArgumentException.class}) ++ public void testError3() { v.isInRange("[10.5]"); } + + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/NaturalOrderComparatorTest.java ---------------------------------------------------------------------- diff --cc brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/NaturalOrderComparatorTest.java index 0000000,3e99e58..3113c8e mode 000000,100644..100644 --- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/NaturalOrderComparatorTest.java +++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/NaturalOrderComparatorTest.java @@@ -1,0 -1,81 +1,90 @@@ + /* + * 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 org.apache.brooklyn.util.text.ComparableVersion; + import org.apache.brooklyn.util.text.NaturalOrderComparator; + import org.testng.Assert; + import org.testng.annotations.Test; + + public class NaturalOrderComparatorTest { + + public static final NaturalOrderComparator noc = new NaturalOrderComparator(); + + ComparableVersion v = new ComparableVersion("10.5.8"); + ComparableVersion v_rc2 = new ComparableVersion("10.5.8-rc2"); + + @Test - public void testBasicOnes() { - Assert.assertTrue(v.isGreaterThanAndNotEqualTo("10.5")); - Assert.assertTrue(v.isGreaterThanOrEqualTo("10.5.8")); - Assert.assertFalse(v.isGreaterThanAndNotEqualTo("10.5.8")); - - Assert.assertTrue(v.isLessThanAndNotEqualTo("10.6")); - Assert.assertTrue(v.isLessThanOrEqualTo("10.5.8")); - Assert.assertFalse(v.isLessThanAndNotEqualTo("10.5.8")); ++ public void testNoc() { ++ Assert.assertEquals(noc.compare("0a", "1"), -1); ++ ++ Assert.assertEquals(noc.compare("0", "1"), -1); ++ Assert.assertEquals(noc.compare("1", "10"), -1); ++ Assert.assertEquals(noc.compare("9", "10"), -1); ++ Assert.assertEquals(noc.compare("a", "b"), -1); ++ Assert.assertEquals(noc.compare("a9", "a10"), -1); + - Assert.assertTrue(v.isLessThanAndNotEqualTo("10.5.8.1")); ++ Assert.assertEquals(noc.compare("0.9", "0.91"), -1); ++ Assert.assertEquals(noc.compare("0.90", "0.91"), -1); ++ Assert.assertEquals(noc.compare("1.2.x", "1.09.x"), -1); ++ ++ Assert.assertEquals(noc.compare("0", "1a"), -1); ++ Assert.assertEquals(noc.compare("0a", "1"), -1); ++ } ++ ++ @Test ++ public void testBasicOnes() { ++ Assert.assertEquals(0, noc.compare("a", "a")); ++ Assert.assertTrue(noc.compare("a", "b") < 0); ++ Assert.assertTrue(noc.compare("b", "a") > 0); + - Assert.assertTrue(v_rc2.isLessThanAndNotEqualTo("10.5.8-rc3")) ; - Assert.assertTrue(v_rc2.isGreaterThanAndNotEqualTo("10.5.8-rc1")); ++ Assert.assertTrue(noc.compare("9", "10") < 0); ++ Assert.assertTrue(noc.compare("10", "9") > 0); + - Assert.assertTrue(v_rc2.isGreaterThanAndNotEqualTo("10.5.8-beta1")==v_rc2.isGreaterThanAndNotEqualTo("10.5.8-beta3")); ++ Assert.assertTrue(noc.compare("b10", "a9") > 0); ++ Assert.assertTrue(noc.compare("b9", "a10") > 0); + - Assert.assertTrue(v.isInRange("[10.5,10.6)")); - Assert.assertFalse(v.isInRange("[10.5,10.5.8)")); - Assert.assertTrue(v.isInRange("[10.5,)")); - Assert.assertTrue(v.isInRange("[9,)")); - Assert.assertFalse(v.isInRange("(10.5.8,)")); - Assert.assertFalse(v.isInRange("[10.6,)")); - Assert.assertTrue(v.isInRange("[,11)")); - Assert.assertTrue(v.isInRange("[,]")); ++ Assert.assertTrue(noc.compare(" 9", "10") < 0); ++ Assert.assertTrue(noc.compare("10", " 9") > 0); + } + - @Test(expectedExceptions={IllegalArgumentException.class}) - public void testError1() { v.isInRange("10.5"); } - @Test(expectedExceptions={IllegalArgumentException.class}) - public void testError2() { v.isInRange("[10.5"); } - @Test(expectedExceptions={IllegalArgumentException.class}) - public void testError3() { v.isInRange("[10.5]"); } ++ @Test ++ public void testVersionNumbers() { ++ Assert.assertEquals(0, noc.compare("10.5.8", "10.5.8")); ++ Assert.assertTrue(noc.compare("10.5", "9.9") > 0); ++ Assert.assertTrue(noc.compare("10.5.1", "10.5") > 0); ++ Assert.assertTrue(noc.compare("10.5.1", "10.6") < 0); ++ Assert.assertTrue(noc.compare("10.5.1-1", "10.5.1-0") > 0); ++ } + + @Test(groups="WIP", enabled=false) + public void testUnderscoreDoesNotChangeMeaningOfNumberInNoc() { + // why?? + Assert.assertTrue(noc.compare("0.0.0_SNAPSHOT", "0.0.1-SNAPSHOT-20141111114709760") < 0); + + Assert.assertTrue(v.isGreaterThanAndNotEqualTo(v_rc2.version)); + Assert.assertTrue(v_rc2.isLessThanAndNotEqualTo(v.version)); + } + + @Test(groups="WIP", enabled=false) + public void testUnderscoreDoesNotChangeMeaningOfNumberInOurWorld() { + Assert.assertTrue(new ComparableVersion("0.0.0_SNAPSHOT").isLessThanAndNotEqualTo("0.0.1-SNAPSHOT-20141111114709760")); + } + + } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/StringsTest.java ---------------------------------------------------------------------- diff --cc brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/StringsTest.java index 0000000,85ba659..a861a10 mode 000000,100644..100644 --- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/StringsTest.java +++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/StringsTest.java @@@ -1,0 -1,366 +1,362 @@@ + /* + * 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 static org.testng.Assert.assertEquals; + import static org.testng.Assert.assertFalse; + import static org.testng.Assert.assertTrue; + + import java.util.Arrays; + + import org.apache.brooklyn.test.FixedLocaleTest; + import org.apache.brooklyn.util.collections.MutableMap; + import org.apache.brooklyn.util.text.FormattedString; + import org.apache.brooklyn.util.text.StringFunctions; + import org.apache.brooklyn.util.text.Strings; + import org.testng.Assert; + import org.testng.annotations.Test; + + @Test + public class StringsTest extends FixedLocaleTest { + + public void isBlankOrEmpty() { + assertTrue(Strings.isEmpty(null)); + assertTrue(Strings.isEmpty("")); + assertFalse(Strings.isEmpty(" \t ")); + assertFalse(Strings.isEmpty("abc")); + assertFalse(Strings.isEmpty(" abc ")); + + assertFalse(Strings.isNonEmpty(null)); + assertFalse(Strings.isNonEmpty("")); + assertTrue(Strings.isNonEmpty(" \t ")); + assertTrue(Strings.isNonEmpty("abc")); + assertTrue(Strings.isNonEmpty(" abc ")); + + assertTrue(Strings.isBlank(null)); + assertTrue(Strings.isBlank("")); + assertTrue(Strings.isBlank(" \t ")); + assertFalse(Strings.isBlank("abc")); + assertFalse(Strings.isBlank(" abc ")); + + assertFalse(Strings.isNonBlank(null)); + assertFalse(Strings.isNonBlank("")); + assertFalse(Strings.isNonBlank(" \t ")); + assertTrue(Strings.isNonBlank("abc")); + assertTrue(Strings.isNonBlank(" abc ")); + } + + public void testMakeValidFilename() { + assertEquals("abcdef", Strings.makeValidFilename("abcdef")); + assertEquals("abc_def", Strings.makeValidFilename("abc$$$def")); + assertEquals("abc_def", Strings.makeValidFilename("$$$abc$$$def$$$")); + assertEquals("a_b_c", Strings.makeValidFilename("a b c")); + assertEquals("a.b.c", Strings.makeValidFilename("a.b.c")); + } + @Test(expectedExceptions = { NullPointerException.class }) + public void testMakeValidFilenameNull() { + Strings.makeValidFilename(null); + } + @Test(expectedExceptions = { IllegalArgumentException.class }) + public void testMakeValidFilenameEmpty() { + Strings.makeValidFilename(""); + } + @Test(expectedExceptions = { IllegalArgumentException.class }) + public void testMakeValidFilenameBlank() { + Strings.makeValidFilename(" \t "); + } + + public void makeValidJavaName() { + assertEquals(Strings.makeValidJavaName(null), "__null"); + assertEquals(Strings.makeValidJavaName(""), "__empty"); + assertEquals(Strings.makeValidJavaName("abcdef"), "abcdef"); + assertEquals(Strings.makeValidJavaName("a'b'c'd'e'f"), "abcdef"); + assertEquals(Strings.makeValidJavaName("12345"), "_12345"); + } + + public void makeValidUniqueJavaName() { + assertEquals(Strings.makeValidUniqueJavaName(null), "__null"); + assertEquals(Strings.makeValidUniqueJavaName(""), "__empty"); + assertEquals(Strings.makeValidUniqueJavaName("abcdef"), "abcdef"); + assertEquals(Strings.makeValidUniqueJavaName("12345"), "_12345"); + } + + public void testRemoveFromEnd() { + assertEquals(Strings.removeFromEnd("", "bar"), ""); + assertEquals(Strings.removeFromEnd(null, "bar"), null); + + assertEquals(Strings.removeFromEnd("foobar", "bar"), "foo"); + assertEquals(Strings.removeFromEnd("foo", "bar"), "foo"); - assertEquals(Strings.removeFromEnd("foobar", "foo", "bar"), "foo"); + // test they are applied in order - assertEquals(Strings.removeFromEnd("foobar", "ar", "bar", "b"), "foob"); + } + + public void testRemoveAllFromEnd() { + assertEquals(Strings.removeAllFromEnd("", "bar"), ""); + assertEquals(Strings.removeAllFromEnd(null, "bar"), null); + assertEquals(Strings.removeAllFromEnd("foo", ""), "foo"); + + assertEquals(Strings.removeAllFromEnd("foobar", "foo", "bar"), ""); + assertEquals(Strings.removeAllFromEnd("foobar", "ar", "car", "b", "o"), "f"); + // test they are applied in order + assertEquals(Strings.removeAllFromEnd("foobar", "ar", "car", "b", "ob"), "foo"); + assertEquals(Strings.removeAllFromEnd("foobar", "zz", "x"), "foobar"); + assertEquals(Strings.removeAllFromEnd("foobarbaz", "bar", "baz"), "foo"); + assertEquals(Strings.removeAllFromEnd("foobarbaz", "baz", "", "foo", "bar", "baz"), ""); + } + + public void testRemoveFromStart() { + assertEquals(Strings.removeFromStart("", "foo"), ""); + assertEquals(Strings.removeFromStart(null, "foo"), null); + + assertEquals(Strings.removeFromStart("foobar", "foo"), "bar"); + assertEquals(Strings.removeFromStart("foo", "bar"), "foo"); - assertEquals(Strings.removeFromStart("foobar", "foo", "bar"), "bar"); - assertEquals(Strings.removeFromStart("foobar", "ob", "fo", "foo", "o"), "obar"); + } + + public void testRemoveAllFromStart() { + assertEquals(Strings.removeAllFromStart("", "foo"), ""); + assertEquals(Strings.removeAllFromStart(null, "foo"), null); + assertEquals(Strings.removeAllFromStart("foo", ""), "foo"); + + assertEquals(Strings.removeAllFromStart("foobar", "foo"), "bar"); + assertEquals(Strings.removeAllFromStart("foo", "bar"), "foo"); + assertEquals(Strings.removeAllFromStart("foobar", "foo", "bar"), ""); + + assertEquals(Strings.removeAllFromStart("foobar", "fo", "ob", "o"), "ar"); + assertEquals(Strings.removeAllFromStart("foobar", "ob", "fo", "o"), "ar"); + // test they are applied in order, "ob" doesn't match because "o" eats the o + assertEquals(Strings.removeAllFromStart("foobar", "o", "fo", "ob"), "bar"); + assertEquals(Strings.removeAllFromStart("foobarbaz", "bar", "foo"), "baz"); + assertEquals(Strings.removeAllFromStart("foobarbaz", "baz", "bar", "foo"), ""); + } + + public void testRemoveFromStart2() { + assertEquals(Strings.removeFromStart("xyz", "x"), "yz"); + assertEquals(Strings.removeFromStart("xyz", "."), "xyz"); + assertEquals(Strings.removeFromStart("http://foo.com", "http://"), "foo.com"); + } + + public void testRemoveFromEnd2() { + assertEquals(Strings.removeFromEnd("xyz", "z"), "xy"); + assertEquals(Strings.removeFromEnd("xyz", "."), "xyz"); + assertEquals(Strings.removeFromEnd("http://foo.com/", "/"), "http://foo.com"); + } + + public void testReplaceAll() { + assertEquals(Strings.replaceAll("xyz", "x", ""), "yz"); + assertEquals(Strings.replaceAll("xyz", ".", ""), "xyz"); + assertEquals(Strings.replaceAll("http://foo.com/", "/", ""), "http:foo.com"); + assertEquals(Strings.replaceAll("http://foo.com/", "http:", "https:"), "https://foo.com/"); + } + + public void testReplaceAllNonRegex() { + assertEquals(Strings.replaceAllNonRegex("xyz", "x", ""), "yz"); + assertEquals(Strings.replaceAllNonRegex("xyz", ".", ""), "xyz"); + assertEquals(Strings.replaceAllNonRegex("http://foo.com/", "/", ""), "http:foo.com"); + assertEquals(Strings.replaceAllNonRegex("http://foo.com/", "http:", "https:"), "https://foo.com/"); + } + + public void testReplaceAllRegex() { + assertEquals(Strings.replaceAllRegex("xyz", "x", ""), "yz"); + assertEquals(Strings.replaceAllRegex("xyz", ".", ""), ""); + assertEquals(Strings.replaceAllRegex("http://foo.com/", "/", ""), "http:foo.com"); + assertEquals(Strings.replaceAllRegex("http://foo.com/", "http:", "https:"), "https://foo.com/"); + } + + public void testReplaceMap() { + assertEquals(Strings.replaceAll("xyz", MutableMap.builder().put("x","a").put("y","").build()), "az"); + } + + public void testContainsLiteral() { + assertTrue(Strings.containsLiteral("hello", "ell")); + assertTrue(Strings.containsLiteral("hello", "h")); + assertFalse(Strings.containsLiteral("hello", "H")); + assertFalse(Strings.containsLiteral("hello", "O")); + assertFalse(Strings.containsLiteral("hello", "x")); + assertFalse(Strings.containsLiteral("hello", "ELL")); + assertTrue(Strings.containsLiteral("hello", "hello")); + assertTrue(Strings.containsLiteral("hELlo", "ELl")); + assertFalse(Strings.containsLiteral("hello", "!")); + } + + public void testContainsLiteralIgnoreCase() { + assertTrue(Strings.containsLiteralIgnoreCase("hello", "ell")); + assertTrue(Strings.containsLiteralIgnoreCase("hello", "H")); + assertTrue(Strings.containsLiteralIgnoreCase("hello", "O")); + assertFalse(Strings.containsLiteralIgnoreCase("hello", "X")); + assertTrue(Strings.containsLiteralIgnoreCase("hello", "ELL")); + assertTrue(Strings.containsLiteralIgnoreCase("hello", "hello")); + assertTrue(Strings.containsLiteralIgnoreCase("hELlo", "Hello")); + assertFalse(Strings.containsLiteralIgnoreCase("hello", "!")); + } + + @Test + public void testDeferredFormat() { + ToStringCounter c = new ToStringCounter(); + FormattedString x = Strings.format("hello %s", c); + Assert.assertEquals(c.count, 0); + Assert.assertEquals(x.toString(), "hello world"); + Assert.assertEquals(c.count, 1); + } + + @Test + public void testToStringSupplier() { + ToStringCounter c = new ToStringCounter(true); + Assert.assertEquals(Strings.toStringSupplier(c).get(), "world1"); + FormattedString x = Strings.format("hello %s", c); + Assert.assertEquals(x.toString(), "hello world2"); + Assert.assertEquals(x.toString(), "hello world3"); + } + + private static class ToStringCounter { + private int count = 0; + private boolean appendCount = false; + private ToStringCounter() {} + private ToStringCounter(boolean append) { this.appendCount = append; } + @Override + public String toString() { + count++; + return "world"+(appendCount?""+count:""); + } + } + + @Test + public void testFormatter() { + Assert.assertEquals(StringFunctions.formatter("hello %s").apply("world"), "hello world"); + Assert.assertEquals(StringFunctions.formatterForArray("%s %s").apply(new String[] { "hello", "world" }), "hello world"); + } + + @Test + public void testJoiner() { + Assert.assertEquals(StringFunctions.joiner(" ").apply(Arrays.asList("hello", "world")), "hello world"); + Assert.assertEquals(StringFunctions.joinerForArray(" ").apply(new String[] { "hello", "world" }), "hello world"); + } + + @Test + public void testSurround() { + Assert.assertEquals(StringFunctions.surround("hello ", " world").apply("new"), "hello new world"); + } + + @Test + public void testFirstWord() { + Assert.assertEquals(Strings.getFirstWord("hello world"), "hello"); + Assert.assertEquals(Strings.getFirstWord(" hello world"), "hello"); + Assert.assertEquals(Strings.getFirstWord(" hello "), "hello"); + Assert.assertEquals(Strings.getFirstWord("hello"), "hello"); + Assert.assertEquals(Strings.getFirstWord(" "), null); + Assert.assertEquals(Strings.getFirstWord(""), null); + Assert.assertEquals(Strings.getFirstWord(null), null); + } + + @Test + public void testLastWord() { + Assert.assertEquals(Strings.getLastWord("hello world"), "world"); + Assert.assertEquals(Strings.getLastWord(" hello world "), "world"); + Assert.assertEquals(Strings.getLastWord(" hello "), "hello"); + Assert.assertEquals(Strings.getLastWord("hello"), "hello"); + Assert.assertEquals(Strings.getLastWord(" "), null); + Assert.assertEquals(Strings.getLastWord(""), null); + Assert.assertEquals(Strings.getLastWord(null), null); + } + + @Test + public void testFirstWordAfter() { + Assert.assertEquals(Strings.getFirstWordAfter("hello world", "hello"), "world"); + Assert.assertEquals(Strings.getFirstWordAfter(" hello world", "hello"), "world"); + Assert.assertEquals(Strings.getFirstWordAfter(" hello world: is not enough", "world:"), "is"); + Assert.assertEquals(Strings.getFirstWordAfter(" hello world: is not enough", "world"), ":"); + Assert.assertEquals(Strings.getFirstWordAfter(" hello ", "hello"), null); + Assert.assertEquals(Strings.getFirstWordAfter("hello", "hello"), null); + Assert.assertEquals(Strings.getFirstWordAfter(" ", "x"), null); + Assert.assertEquals(Strings.getFirstWordAfter("", "x"), null); + Assert.assertEquals(Strings.getFirstWordAfter(null, "x"), null); + } + + @Test + public void testFragmentBetween() { + Assert.assertEquals("ooba", Strings.getFragmentBetween("foobar", "f", "r")); + Assert.assertEquals("oobar", Strings.getFragmentBetween("foobar", "f", "z")); + Assert.assertEquals("oobar", Strings.getFragmentBetween("foobar", "f", null)); + Assert.assertEquals("oba", Strings.getFragmentBetween("foobar", "o", "r")); + Assert.assertEquals("\nba", Strings.getFragmentBetween("foo\nbar", "foo", "r")); + Assert.assertEquals("fooba", Strings.getFragmentBetween("foobar", null, "r")); + Assert.assertEquals(null, Strings.getFragmentBetween("foobar", "z", "r")); + } + + @Test + public void testWordCount() { + Assert.assertEquals(Strings.getWordCount("hello", true), 1); + Assert.assertEquals(Strings.getWordCount("hello world", true), 2); + Assert.assertEquals(Strings.getWordCount("hello\nworld", true), 2); + Assert.assertEquals(Strings.getWordCount("hello world \nit is me!\n", true), 5); + Assert.assertEquals(Strings.getWordCount("", true), 0); + Assert.assertEquals(Strings.getWordCount(null, true), 0); + Assert.assertEquals(Strings.getWordCount("\"hello world\" ", true), 1); + Assert.assertEquals(Strings.getWordCount("\"hello world\" ", false), 2); + Assert.assertEquals(Strings.getWordCount("hello world \nit's me!\n", true), 3); + Assert.assertEquals(Strings.getWordCount("hello world \nit's me!\n", false), 4); + } + + @Test + public void testMakeRealString() { + // less precision = less length + Assert.assertEquals(Strings.makeRealString(1.23456d, 4, 2, 0), "1.2"); + // precision trumps length, and rounds + Assert.assertEquals(Strings.makeRealString(1.23456d, 4, 5, 0), "1.2346"); + // uses E notation when needed + Assert.assertEquals(Strings.makeRealString(123456, 2, 2, 0), "1.2E5"); + // and works with negatives + Assert.assertEquals(Strings.makeRealString(-123456, 2, 2, 0), "-1.2E5"); + // and very small negatives + Assert.assertEquals(Strings.makeRealString(-0.000000000123456, 2, 2, 0), "-1.2E-10"); + // and 0 + Assert.assertEquals(Strings.makeRealString(0.0d, 4, 2, 0), "0"); + // skips E notation and gives extra precision when it's free + Assert.assertEquals(Strings.makeRealString(123456, 8, 2, 0), "123456"); + } + + @Test + public void testCollapseWhitespace() { + Assert.assertEquals(Strings.collapseWhitespace(" x\n y\n", ""), "xy"); + Assert.assertEquals(Strings.collapseWhitespace(" x\n y\n", " "), " x y "); + Assert.assertEquals(Strings.collapseWhitespace(" x\n y\n", "\n").trim(), "x\ny"); + } + + @Test + public void testMaxlen() { + Assert.assertEquals(Strings.maxlen("hello world", 5), "hello"); + Assert.assertEquals(Strings.maxlenWithEllipsis("hello world", 9), "hello ..."); + Assert.assertEquals(Strings.maxlenWithEllipsis("hello world", 7, "--"), "hello--"); + } + + @Test + public void testGetRemainderOfLineAfter() { + // Basic test (also tests start is trimmed) + Assert.assertEquals(Strings.getRemainderOfLineAfter("the message is hello", "is"), " hello"); + Assert.assertEquals(Strings.getRemainderOfLineAfter("the message is is hello", "is"), " is hello"); + // Trim spaces from end + Assert.assertEquals(Strings.getRemainderOfLineAfter("the message is is hello ", "is"), " is hello "); + // Trim non-matching lines from start + Assert.assertEquals(Strings.getRemainderOfLineAfter("one\ntwo\nthree\nthe message is is hello ", "is"), " is hello "); + // Trim lines from end + Assert.assertEquals(Strings.getRemainderOfLineAfter("one\ntwo\nthree\nthe message is is hello \nfour\nfive\nsix\nis not seven", "is"), " is hello "); + // Play nicely with null / non-match + Assert.assertEquals(Strings.getRemainderOfLineAfter(null, "is"), null); + Assert.assertEquals(Strings.getRemainderOfLineAfter("the message is hello", null), null); + Assert.assertEquals(Strings.getRemainderOfLineAfter("the message is hello", "foo"), null); + } + }
