From 05afb9f1ddaf432a86aab24b097c9f121bd056d1 Mon Sep 17 00:00:00 2001 From: Pete Royle Date: Fri, 9 Sep 2011 03:33:31 -0400 Subject: [PATCH] Make string helpers available in all templates --- .../compiler/CustomTemplateCompiler.java | 26 +- .../jboss/seam/render/util/StringUtils.java | 1247 +++++++++++++++++ .../seam/render/test/BundledHelpersTest.java | 71 + 3 files changed, 1339 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/jboss/seam/render/util/StringUtils.java create mode 100644 src/test/java/org/jboss/seam/render/test/BundledHelpersTest.java diff --git a/src/main/java/org/jboss/seam/render/template/compiler/CustomTemplateCompiler.java b/src/main/java/org/jboss/seam/render/template/compiler/CustomTemplateCompiler.java index 15e9859..2296d32 100644 --- a/src/main/java/org/jboss/seam/render/template/compiler/CustomTemplateCompiler.java +++ b/src/main/java/org/jboss/seam/render/template/compiler/CustomTemplateCompiler.java @@ -4,6 +4,7 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import org.jboss.seam.render.util.StringUtils; import org.mvel2.CompileException; import org.mvel2.ParserContext; @@ -317,6 +318,17 @@ template, captureOrbInternal(), start, parserContext) return root; } + /** + * Imports the StringUtils class as the alias "render" so that all of the + * static String utility methods in that class are available to the template + * like @{render.uncapitalize(entityBeanName)}. + * + * @param context + */ + private void addBundledHelpers(ParserContext context) { + context.addImport("render", StringUtils.class); + } + // Parse Utilities Start Here private boolean isNext(char c) { @@ -367,6 +379,10 @@ private Node markTextNode(Node n) { public ParserContext getParserContext() { return parserContext; } + private void setParserContext(ParserContext context) { + addBundledHelpers(context); + this.parserContext = context; + } public static CompiledTemplate compileTemplate(String template) { return new CustomTemplateCompiler(template, true, ParserContext.create()).compile(); @@ -477,7 +493,7 @@ public CustomTemplateCompiler(char[] template, boolean codeCache) { public CustomTemplateCompiler(char[] template, boolean codeCache, ParserContext context) { this.length = (this.template = template).length; this.codeCache = codeCache; - this.parserContext = context; + setParserContext(context); } public CustomTemplateCompiler(CharSequence sequence) { @@ -492,7 +508,7 @@ public CustomTemplateCompiler(CharSequence sequence, boolean codeCache) { public CustomTemplateCompiler(CharSequence sequence, boolean codeCache, ParserContext context) { this.length = (this.template = sequence.toString().toCharArray()).length; this.codeCache = codeCache; - this.parserContext = context; + setParserContext(context); } public CustomTemplateCompiler(String template, Map> customNodes) { @@ -534,7 +550,7 @@ public CustomTemplateCompiler(String template, Map this.length = (this.template = template.toCharArray()).length; this.customNodes = customNodes; this.codeCache = codeCache; - this.parserContext = context; + setParserContext(context); } public CustomTemplateCompiler(char[] template, Map> customNodes, boolean codeCache, @@ -542,7 +558,7 @@ public CustomTemplateCompiler(char[] template, Map this.length = (this.template = template).length; this.customNodes = customNodes; this.codeCache = codeCache; - this.parserContext = context; + setParserContext(context); } public CustomTemplateCompiler(CharSequence sequence, Map> customNodes, @@ -551,6 +567,6 @@ public CustomTemplateCompiler(CharSequence sequence, MapOperations on {@link java.lang.String} that are + * null safe.

+ * + *
    + *
  • IsEmpty/IsBlank + * - checks if a String contains text
  • + *
  • Trim/Strip + * - removes leading and trailing whitespace
  • + *
  • Equals + * - compares two strings null-safe
  • + *
  • startsWith + * - check if a String starts with a prefix null-safe
  • + *
  • endsWith + * - check if a String ends with a suffix null-safe
  • + *
  • IndexOf/LastIndexOf/Contains + * - null-safe index-of checks + *
  • IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut + * - index-of any of a set of Strings
  • + *
  • ContainsOnly/ContainsNone/ContainsAny + * - does String contains only/none/any of these characters
  • + *
  • Substring/Left/Right/Mid + * - null-safe substring extractions
  • + *
  • SubstringBefore/SubstringAfter/SubstringBetween + * - substring extraction relative to other strings
  • + *
  • Split/Join + * - splits a String into an array of substrings and vice versa
  • + *
  • Remove/Delete + * - removes part of a String
  • + *
  • Replace/Overlay + * - Searches a String and replaces one String with another
  • + *
  • Chomp/Chop + * - removes the last part of a String
  • + *
  • LeftPad/RightPad/Center/Repeat + * - pads a String
  • + *
  • UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize + * - changes the case of a String
  • + *
  • CountMatches + * - counts the number of occurrences of one String in another
  • + *
  • IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable + * - checks the characters in a String
  • + *
  • DefaultString + * - protects against a null input String
  • + *
  • Reverse/ReverseDelimited + * - reverses a String
  • + *
  • Abbreviate + * - abbreviates a string using ellipsis
  • + *
  • Difference + * - compares Strings and reports on their differences
  • + *
  • LevensteinDistance + * - the number of changes needed to change one String into another
  • + *
+ * + *

The StringUtils class defines certain words related to + * String handling.

+ * + *
    + *
  • null - null
  • + *
  • empty - a zero-length string ("")
  • + *
  • space - the space character (' ', char 32)
  • + *
  • whitespace - the characters defined by {@link Character#isWhitespace(char)}
  • + *
  • trim - the characters <= 32 as in {@link String#trim()}
  • + *
+ * + *

StringUtils handles null input Strings quietly. + * That is to say that a null input will return null. + * Where a boolean or int is being returned + * details vary by method.

+ * + *

A side effect of the null handling is that a + * NullPointerException should be considered a bug in + * StringUtils (except for deprecated methods).

+ * + *

Methods in this class give sample code to explain their operation. + * The symbol * is used to indicate any input including null.

+ * + * @see java.lang.String + * @author Apache Jakarta Turbine + * @author Jon S. Stevens + * @author Daniel L. Rall + * @author Greg Coladonato + * @author Ed Korthof + * @author Rand McNeely + * @author Stephen Colebourne + * @author Fredrik Westermarck + * @author Holger Krauth + * @author Alexander Day Chaffee + * @author Henning P. Schmiedehausen + * @author Arun Mammen Thomas + * @author Gary Gregory + * @author Phil Steitz + * @author Al Chou + * @author Michael Davey + * @author Reuben Sivan + * @author Chris Hyzer + * @author Scott Johnson + * @since 1.0 + * @version $Id: StringUtils.java 635447 2008-03-10 06:27:09Z bayard $ + */ +public class StringUtils { + // Performance testing notes (JDK 1.4, Jul03, scolebourne) + // Whitespace: + // Character.isWhitespace() is faster than WHITESPACE.indexOf() + // where WHITESPACE is a string of all whitespace characters + // + // Character access: + // String.charAt(n) versus toCharArray(), then array[n] + // String.charAt(n) is about 15% worse for a 10K string + // They are about equal for a length 50 string + // String.charAt(n) is about 4 times better for a length 3 string + // String.charAt(n) is best bet overall + // + // Append: + // String.concat about twice as fast as StringBuffer.append + // (not sure who tested this) + + private static final String[] EMPTY_STRING_ARRAY = new String[] {}; + + /** + * The empty String "". + * @since 2.0 + */ + public static final String EMPTY = ""; + + /** + * Represents a failed index search. + * @since 2.1 + */ + public static final int INDEX_NOT_FOUND = -1; + + /** + *

The maximum size to which the padding constant(s) can expand.

+ */ + private static final int PAD_LIMIT = 8192; + + /** + *

StringUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * StringUtils.trim(" foo ");.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public StringUtils() { + super(); + } + + // Case conversion + //----------------------------------------------------------------------- + /** + *

Converts a String to upper case as per {@link String#toUpperCase()}.

+ * + *

A null input String returns null.

+ * + *
+     * StringUtils.upperCase(null)  = null
+     * StringUtils.upperCase("")    = ""
+     * StringUtils.upperCase("aBc") = "ABC"
+     * 
+ * + * @param str the String to upper case, may be null + * @return the upper cased String, null if null String input + */ + public static String upperCase(String str) { + if (str == null) { + return null; + } + return str.toUpperCase(); + } + + /** + *

Converts a String to lower case as per {@link String#toLowerCase()}.

+ * + *

A null input String returns null.

+ * + *
+     * StringUtils.lowerCase(null)  = null
+     * StringUtils.lowerCase("")    = ""
+     * StringUtils.lowerCase("aBc") = "abc"
+     * 
+ * + * @param str the String to lower case, may be null + * @return the lower cased String, null if null String input + */ + public static String lowerCase(String str) { + if (str == null) { + return null; + } + return str.toLowerCase(); + } + + // Empty checks + //----------------------------------------------------------------------- + /** + *

Checks if a String is empty ("") or null.

+ * + *
+     * StringUtils.isEmpty(null)      = true
+     * StringUtils.isEmpty("")        = true
+     * StringUtils.isEmpty(" ")       = false
+     * StringUtils.isEmpty("bob")     = false
+     * StringUtils.isEmpty("  bob  ") = false
+     * 
+ * + *

NOTE: This method changed in Lang version 2.0. + * It no longer trims the String. + * That functionality is available in isBlank().

+ * + * @param str the String to check, may be null + * @return true if the String is empty or null + */ + private static boolean isEmpty(String str) { + return str == null || str.length() == 0; + } + + /** + *

Removes control characters (char <= 32) from both + * ends of this String, handling null by returning + * null.

+ * + *

The String is trimmed using {@link String#trim()}. + * Trim removes start and end characters <= 32. + * To strip whitespace use {@link #strip(String)}.

+ * + *

To trim your choice of characters, use the + * {@link #strip(String, String)} methods.

+ * + *
+     * StringUtils.trim(null)          = null
+     * StringUtils.trim("")            = ""
+     * StringUtils.trim("     ")       = ""
+     * StringUtils.trim("abc")         = "abc"
+     * StringUtils.trim("    abc    ") = "abc"
+     * 
+ * + * @param str the String to be trimmed, may be null + * @return the trimmed string, null if null String input + */ + public static String trim(String str) { + return str == null ? null : str.trim(); + } + + /** + /** + /** + /** + // Splitting + //----------------------------------------------------------------------- + /** + *

Splits the provided text into an array, using whitespace as the + * separator. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

+ * + *

A null input String returns null.

+ * + *
+     * StringUtils.split(null)       = null
+     * StringUtils.split("")         = []
+     * StringUtils.split("abc def")  = ["abc", "def"]
+     * StringUtils.split("abc  def") = ["abc", "def"]
+     * StringUtils.split(" abc ")    = ["abc"]
+     * 
+ * + * @param str the String to parse, may be null + * @return an array of parsed Strings, null if null String input + */ + public static String[] split(String str) { + return split(str, null, -1); + } + + /** + *

Splits the provided text into an array, separators specified. + * This is an alternative to using StringTokenizer.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

+ * + *

A null input String returns null. + * A null separatorChars splits on whitespace.

+ * + *
+     * StringUtils.split(null, *)         = null
+     * StringUtils.split("", *)           = []
+     * StringUtils.split("abc def", null) = ["abc", "def"]
+     * StringUtils.split("abc def", " ")  = ["abc", "def"]
+     * StringUtils.split("abc  def", " ") = ["abc", "def"]
+     * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * null splits on whitespace + * @return an array of parsed Strings, null if null String input + */ + public static String[] split(String str, String separatorChars) { + return splitWorker(str, separatorChars, -1, false); + } + + /** + *

Splits the provided text into an array with a maximum length, + * separators specified.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as one separator.

+ * + *

A null input String returns null. + * A null separatorChars splits on whitespace.

+ * + *

If more than max delimited substrings are found, the last + * returned string includes all characters after the first max - 1 + * returned strings (including separator characters).

+ * + *
+     * StringUtils.split(null, *, *)            = null
+     * StringUtils.split("", *, *)              = []
+     * StringUtils.split("ab de fg", null, 0)   = ["ab", "cd", "ef"]
+     * StringUtils.split("ab   de fg", null, 0) = ["ab", "cd", "ef"]
+     * StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
+     * StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * null splits on whitespace + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit + * @return an array of parsed Strings, null if null String input + */ + private static String[] split(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, false); + } + + /** + * Performs the logic for the split and + * splitPreserveAllTokens methods that return a maximum array + * length. + * + * @param str the String to parse, may be null + * @param separatorChars the separate character + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @param preserveAllTokens if true, adjacent separators are + * treated as empty token separators; if false, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, null if null String input + */ + private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) { + // Performance tuned for 2.0 (JDK1.4) + // Direct code is quicker than StringTokenizer. + // Also, StringTokenizer uses isSpace() not isWhitespace() + + if (str == null) { + return null; + } + int len = str.length(); + if (len == 0) { + return EMPTY_STRING_ARRAY; + } + List list = new ArrayList(); + int sizePlus1 = 1; + int i = 0, start = 0; + boolean match = false; + boolean lastMatch = false; + if (separatorChars == null) { + // Null separator means use whitespace + while (i < len) { + if (Character.isWhitespace(str.charAt(i))) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else if (separatorChars.length() == 1) { + // Optimise 1 character case + char sep = separatorChars.charAt(0); + while (i < len) { + if (str.charAt(i) == sep) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else { + // standard case + while (i < len) { + if (separatorChars.indexOf(str.charAt(i)) >= 0) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } + if (match || (preserveAllTokens && lastMatch)) { + list.add(str.substring(start, i)); + } + return (String[]) list.toArray(new String[list.size()]); + } + + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No delimiter is added before or after the list. + * A null separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by + * empty strings.

+ * + *
+     * StringUtils.join(null, *)                = null
+     * StringUtils.join([], *)                  = ""
+     * StringUtils.join([null], *)              = ""
+     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
+     * StringUtils.join(["a", "b", "c"], null)  = "abc"
+     * StringUtils.join(["a", "b", "c"], "")    = "abc"
+     * StringUtils.join([null, "", "a"], ',')   = ",,a"
+     * 
+ * + * @param array the array of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, null if null array input + */ + public static String join(Object[] array, String separator) { + if (array == null) { + return null; + } + return join(array, separator, 0, array.length); + } + + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No delimiter is added before or after the list. + * A null separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by + * empty strings.

+ * + *
+     * StringUtils.join(null, *)                = null
+     * StringUtils.join([], *)                  = ""
+     * StringUtils.join([null], *)              = ""
+     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
+     * StringUtils.join(["a", "b", "c"], null)  = "abc"
+     * StringUtils.join(["a", "b", "c"], "")    = "abc"
+     * StringUtils.join([null, "", "a"], ',')   = ",,a"
+     * 
+ * + * @param array the array of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @param startIndex the first index to start joining from. It is + * an error to pass in an end index past the end of the array + * @param endIndex the index to stop joining from (exclusive). It is + * an error to pass in an end index past the end of the array + * @return the joined String, null if null array input + */ + private static String join(Object[] array, String separator, int startIndex, int endIndex) { + if (array == null) { + return null; + } + if (separator == null) { + separator = EMPTY; + } + + // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator)) + // (Assuming that all Strings are roughly equally long) + int bufSize = (endIndex - startIndex); + if (bufSize <= 0) { + return EMPTY; + } + + bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + + separator.length()); + + StringBuffer buf = new StringBuffer(bufSize); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { + buf.append(separator); + } + if (array[i] != null) { + buf.append(array[i]); + } + } + return buf.toString(); + } + + /** + *

Replaces all occurrences of a String within another String.

+ * + *

A null reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replace(null, *, *)        = null
+     * StringUtils.replace("", *, *)          = ""
+     * StringUtils.replace("any", null, *)    = "any"
+     * StringUtils.replace("any", *, null)    = "any"
+     * StringUtils.replace("any", "", *)      = "any"
+     * StringUtils.replace("aba", "a", null)  = "aba"
+     * StringUtils.replace("aba", "a", "")    = "b"
+     * StringUtils.replace("aba", "a", "z")   = "zbz"
+     * 
+ * + * @see #replace(String text, String searchString, String replacement, int max) + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null + * @return the text with any replacements processed, + * null if null String input + */ + public static String replace(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, -1); + } + + /** + *

Replaces a String with another String inside a larger String, + * for the first max values of the search String.

+ * + *

A null reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replace(null, *, *, *)         = null
+     * StringUtils.replace("", *, *, *)           = ""
+     * StringUtils.replace("any", null, *, *)     = "any"
+     * StringUtils.replace("any", *, null, *)     = "any"
+     * StringUtils.replace("any", "", *, *)       = "any"
+     * StringUtils.replace("any", *, *, 0)        = "any"
+     * StringUtils.replace("abaa", "a", null, -1) = "abaa"
+     * StringUtils.replace("abaa", "a", "", -1)   = "b"
+     * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
+     * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
+     * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
+     * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
+     * 
+ * + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null + * @param max maximum number of values to replace, or -1 if no maximum + * @return the text with any replacements processed, + * null if null String input + */ + private static String replace(String text, String searchString, String replacement, int max) { + if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) { + return text; + } + int start = 0; + int end = text.indexOf(searchString, start); + if (end == -1) { + return text; + } + int replLength = searchString.length(); + int increase = replacement.length() - replLength; + increase = (increase < 0 ? 0 : increase); + increase *= (max < 0 ? 16 : (max > 64 ? 64 : max)); + StringBuffer buf = new StringBuffer(text.length() + increase); + while (end != -1) { + buf.append(text.substring(start, end)).append(replacement); + start = end + replLength; + if (--max == 0) { + break; + } + end = text.indexOf(searchString, start); + } + buf.append(text.substring(start)); + return buf.toString(); + } + + /** + *

Returns padding using the specified delimiter repeated + * to a given length.

+ * + *
+     * StringUtils.padding(0, 'e')  = ""
+     * StringUtils.padding(3, 'e')  = "eee"
+     * StringUtils.padding(-2, 'e') = IndexOutOfBoundsException
+     * 
+ * + *

Note: this method doesn't not support padding with + * Unicode Supplementary Characters + * as they require a pair of chars to be represented. + * If you are needing to support full I18N of your applications + * consider using {@link #repeat(String, int)} instead. + *

+ * + * @param repeat number of times to repeat delim + * @param padChar character to repeat + * @return String with repeated character + * @throws IndexOutOfBoundsException if repeat < 0 + * @see #repeat(String, int) + */ + private static String padding(int repeat, char padChar) throws IndexOutOfBoundsException { + if (repeat < 0) { + throw new IndexOutOfBoundsException("Cannot pad a negative amount: " + repeat); + } + final char[] buf = new char[repeat]; + for (int i = 0; i < buf.length; i++) { + buf[i] = padChar; + } + return new String(buf); + } + + /** + *

Right pad a String with spaces (' ').

+ * + *

The String is padded to the size of size.

+ * + *
+     * StringUtils.rightPad(null, *)   = null
+     * StringUtils.rightPad("", 3)     = "   "
+     * StringUtils.rightPad("bat", 3)  = "bat"
+     * StringUtils.rightPad("bat", 5)  = "bat  "
+     * StringUtils.rightPad("bat", 1)  = "bat"
+     * StringUtils.rightPad("bat", -1) = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return right padded String or original String if no padding is necessary, + * null if null String input + */ + public static String rightPad(String str, int size) { + return rightPad(str, size, " "); + } + + + /** + *

Right pad a String with a specified character.

+ * + *

The String is padded to the size of size.

+ * + *
+     * StringUtils.rightPad(null, *, *)     = null
+     * StringUtils.rightPad("", 3, 'z')     = "zzz"
+     * StringUtils.rightPad("bat", 3, 'z')  = "bat"
+     * StringUtils.rightPad("bat", 5, 'z')  = "batzz"
+     * StringUtils.rightPad("bat", 1, 'z')  = "bat"
+     * StringUtils.rightPad("bat", -1, 'z') = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return right padded String or original String if no padding is necessary, + * null if null String input + * @since 2.0 + */ + private static String rightPad(String str, int size, char padChar) { + if (str == null) { + return null; + } + int pads = size - str.length(); + if (pads <= 0) { + return str; // returns original String when possible + } + if (pads > PAD_LIMIT) { + return rightPad(str, size, String.valueOf(padChar)); + } + return str.concat(padding(pads, padChar)); + } + + /** + *

Right pad a String with a specified String.

+ * + *

The String is padded to the size of size.

+ * + *
+     * StringUtils.rightPad(null, *, *)      = null
+     * StringUtils.rightPad("", 3, "z")      = "zzz"
+     * StringUtils.rightPad("bat", 3, "yz")  = "bat"
+     * StringUtils.rightPad("bat", 5, "yz")  = "batyz"
+     * StringUtils.rightPad("bat", 8, "yz")  = "batyzyzy"
+     * StringUtils.rightPad("bat", 1, "yz")  = "bat"
+     * StringUtils.rightPad("bat", -1, "yz") = "bat"
+     * StringUtils.rightPad("bat", 5, null)  = "bat  "
+     * StringUtils.rightPad("bat", 5, "")    = "bat  "
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return right padded String or original String if no padding is necessary, + * null if null String input + */ + public static String rightPad(String str, int size, String padStr) { + if (str == null) { + return null; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int padLen = padStr.length(); + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; // returns original String when possible + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return rightPad(str, size, padStr.charAt(0)); + } + + if (pads == padLen) { + return str.concat(padStr); + } else if (pads < padLen) { + return str.concat(padStr.substring(0, pads)); + } else { + char[] padding = new char[pads]; + char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return str.concat(new String(padding)); + } + } + + /** + *

Left pad a String with spaces (' ').

+ * + *

The String is padded to the size of size.

+ * + *
+     * StringUtils.leftPad(null, *)   = null
+     * StringUtils.leftPad("", 3)     = "   "
+     * StringUtils.leftPad("bat", 3)  = "bat"
+     * StringUtils.leftPad("bat", 5)  = "  bat"
+     * StringUtils.leftPad("bat", 1)  = "bat"
+     * StringUtils.leftPad("bat", -1) = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return left padded String or original String if no padding is necessary, + * null if null String input + */ + public static String leftPad(String str, int size) { + return leftPad(str, size, " "); + } + + + /** + *

Left pad a String with a specified character.

+ * + *

Pad to a size of size.

+ * + *
+     * StringUtils.leftPad(null, *, *)     = null
+     * StringUtils.leftPad("", 3, 'z')     = "zzz"
+     * StringUtils.leftPad("bat", 3, 'z')  = "bat"
+     * StringUtils.leftPad("bat", 5, 'z')  = "zzbat"
+     * StringUtils.leftPad("bat", 1, 'z')  = "bat"
+     * StringUtils.leftPad("bat", -1, 'z') = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return left padded String or original String if no padding is necessary, + * null if null String input + * @since 2.0 + */ + private static String leftPad(String str, int size, char padChar) { + if (str == null) { + return null; + } + int pads = size - str.length(); + if (pads <= 0) { + return str; // returns original String when possible + } + if (pads > PAD_LIMIT) { + return leftPad(str, size, String.valueOf(padChar)); + } + return padding(pads, padChar).concat(str); + } + + /** + *

Left pad a String with a specified String.

+ * + *

Pad to a size of size.

+ * + *
+     * StringUtils.leftPad(null, *, *)      = null
+     * StringUtils.leftPad("", 3, "z")      = "zzz"
+     * StringUtils.leftPad("bat", 3, "yz")  = "bat"
+     * StringUtils.leftPad("bat", 5, "yz")  = "yzbat"
+     * StringUtils.leftPad("bat", 8, "yz")  = "yzyzybat"
+     * StringUtils.leftPad("bat", 1, "yz")  = "bat"
+     * StringUtils.leftPad("bat", -1, "yz") = "bat"
+     * StringUtils.leftPad("bat", 5, null)  = "  bat"
+     * StringUtils.leftPad("bat", 5, "")    = "  bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return left padded String or original String if no padding is necessary, + * null if null String input + */ + public static String leftPad(String str, int size, String padStr) { + if (str == null) { + return null; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int padLen = padStr.length(); + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; // returns original String when possible + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return leftPad(str, size, padStr.charAt(0)); + } + + if (pads == padLen) { + return padStr.concat(str); + } else if (pads < padLen) { + return padStr.substring(0, pads).concat(str); + } else { + char[] padding = new char[pads]; + char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return new String(padding).concat(str); + } + } + + // Centering + //----------------------------------------------------------------------- + /** + *

Centers a String in a larger String of size size + * using the space character (' ').

+ * + *

If the size is less than the String length, the String is returned. + * A null String returns null. + * A negative size is treated as zero.

+ * + *

Equivalent to center(str, size, " ").

+ * + *
+     * StringUtils.center(null, *)   = null
+     * StringUtils.center("", 4)     = "    "
+     * StringUtils.center("ab", -1)  = "ab"
+     * StringUtils.center("ab", 4)   = " ab "
+     * StringUtils.center("abcd", 2) = "abcd"
+     * StringUtils.center("a", 4)    = " a  "
+     * 
+ * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @return centered String, null if null String input + */ + public static String center(String str, int size) { + return center(str, size, " "); + } + + /** + *

Centers a String in a larger String of size size. + * Uses a supplied String as the value to pad the String with.

+ * + *

If the size is less than the String length, the String is returned. + * A null String returns null. + * A negative size is treated as zero.

+ * + *
+     * StringUtils.center(null, *, *)     = null
+     * StringUtils.center("", 4, " ")     = "    "
+     * StringUtils.center("ab", -1, " ")  = "ab"
+     * StringUtils.center("ab", 4, " ")   = " ab"
+     * StringUtils.center("abcd", 2, " ") = "abcd"
+     * StringUtils.center("a", 4, " ")    = " a  "
+     * StringUtils.center("a", 4, "yz")   = "yayz"
+     * StringUtils.center("abc", 7, null) = "  abc  "
+     * StringUtils.center("abc", 7, "")   = "  abc  "
+     * 
+ * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @param padStr the String to pad the new String with, must not be null or empty + * @return centered String, null if null String input + * @throws IllegalArgumentException if padStr is null or empty + */ + public static String center(String str, int size, String padStr) { + if (str == null || size <= 0) { + return str; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; + } + str = leftPad(str, strLen + pads / 2, padStr); + str = rightPad(str, size, padStr); + return str; + } + + /** + *

Capitalizes a String changing the first letter to title case as + * per {@link Character#toTitleCase(char)}. No other letters are changed.

+ * + *

For a word based algorithm, see {@link WordUtils#capitalize(String)}. + * A null input String returns null.

+ * + *
+     * StringUtils.capitalize(null)  = null
+     * StringUtils.capitalize("")    = ""
+     * StringUtils.capitalize("cat") = "Cat"
+     * StringUtils.capitalize("cAt") = "CAt"
+     * 
+ * + * @param str the String to capitalize, may be null + * @return the capitalized String, null if null String input + * @see WordUtils#capitalize(String) + * @see #uncapitalize(String) + * @since 2.0 + */ + public static String capitalize(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + return new StringBuffer(strLen) + .append(Character.toTitleCase(str.charAt(0))) + .append(str.substring(1)) + .toString(); + } + + /** + *

Uncapitalizes a String changing the first letter to title case as + * per {@link Character#toLowerCase(char)}. No other letters are changed.

+ * + *

For a word based algorithm, see {@link WordUtils#uncapitalize(String)}. + * A null input String returns null.

+ * + *
+     * StringUtils.uncapitalize(null)  = null
+     * StringUtils.uncapitalize("")    = ""
+     * StringUtils.uncapitalize("Cat") = "cat"
+     * StringUtils.uncapitalize("CAT") = "cAT"
+     * 
+ * + * @param str the String to uncapitalize, may be null + * @return the uncapitalized String, null if null String input + * @see WordUtils#uncapitalize(String) + * @see #capitalize(String) + * @since 2.0 + */ + public static String uncapitalize(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + return new StringBuffer(strLen) + .append(Character.toLowerCase(str.charAt(0))) + .append(str.substring(1)) + .toString(); + } + + /** + *

Swaps the case of a String changing upper and title case to + * lower case, and lower case to upper case.

+ * + *
    + *
  • Upper case character converts to Lower case
  • + *
  • Title case character converts to Lower case
  • + *
  • Lower case character converts to Upper case
  • + *
+ * + *

For a word based algorithm, see {@link WordUtils#swapCase(String)}. + * A null input String returns null.

+ * + *
+     * StringUtils.swapCase(null)                 = null
+     * StringUtils.swapCase("")                   = ""
+     * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
+     * 
+ * + *

NOTE: This method changed in Lang version 2.0. + * It no longer performs a word based algorithm. + * If you only use ASCII, you will notice no change. + * That functionality is available in WordUtils.

+ * + * @param str the String to swap case, may be null + * @return the changed String, null if null String input + */ + public static String swapCase(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + StringBuffer buffer = new StringBuffer(strLen); + + char ch = 0; + for (int i = 0; i < strLen; i++) { + ch = str.charAt(i); + if (Character.isUpperCase(ch)) { + ch = Character.toLowerCase(ch); + } else if (Character.isTitleCase(ch)) { + ch = Character.toLowerCase(ch); + } else if (Character.isLowerCase(ch)) { + ch = Character.toUpperCase(ch); + } + buffer.append(ch); + } + return buffer.toString(); + } + + /** + // Defaults + //----------------------------------------------------------------------- + /** + *

Returns either the passed in String, or if the String is + * empty or null, the value of defaultStr.

+ * + *
+     * StringUtils.defaultIfEmpty(null, "NULL")  = "NULL"
+     * StringUtils.defaultIfEmpty("", "NULL")    = "NULL"
+     * StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
+     * 
+ * + * @see StringUtils#defaultString(String, String) + * @param str the String to check, may be null + * @param defaultStr the default String to return + * if the input is empty ("") or null, may be null + * @return the passed in String, or the default + */ + public static String defaultString(String str, String defaultStr) { + return StringUtils.isEmpty(str) ? defaultStr : str; + } + + // Reversing + //----------------------------------------------------------------------- + /** + *

Reverses a String as per {@link StringBuffer#reverse()}.

+ * + *

A null String returns null.

+ * + *
+     * StringUtils.reverse(null)  = null
+     * StringUtils.reverse("")    = ""
+     * StringUtils.reverse("bat") = "tab"
+     * 
+ * + * @param str the String to reverse, may be null + * @return the reversed String, null if null String input + */ + public static String reverse(String str) { + if (str == null) { + return null; + } + return new StringBuffer(str).reverse().toString(); + } + + // Abbreviating + //----------------------------------------------------------------------- + /** + *

Abbreviates a String using ellipses. This will turn + * "Now is the time for all good men" into "Now is the time for..."

+ * + *

Specifically: + *

    + *
  • If str is less than maxWidth characters + * long, return it.
  • + *
  • Else abbreviate it to (substring(str, 0, max-3) + "...").
  • + *
  • If maxWidth is less than 4, throw an + * IllegalArgumentException.
  • + *
  • In no case will it return a String of length greater than + * maxWidth.
  • + *
+ *

+ * + *
+     * StringUtils.abbreviate(null, *)      = null
+     * StringUtils.abbreviate("", 4)        = ""
+     * StringUtils.abbreviate("abcdefg", 6) = "abc..."
+     * StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
+     * StringUtils.abbreviate("abcdefg", 8) = "abcdefg"
+     * StringUtils.abbreviate("abcdefg", 4) = "a..."
+     * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException
+     * 
+ * + * @param str the String to check, may be null + * @param maxWidth maximum length of result String, must be at least 4 + * @return abbreviated String, null if null String input + * @throws IllegalArgumentException if the width is too small + * @since 2.0 + */ + public static String abbreviate(String str, int maxWidth) { + return abbreviate(str, 0, maxWidth); + } + + /** + *

Abbreviates a String using ellipses. This will turn + * "Now is the time for all good men" into "...is the time for..."

+ * + *

Works like abbreviate(String, int), but allows you to specify + * a "left edge" offset. Note that this left edge is not necessarily going to + * be the leftmost character in the result, or the first character following the + * ellipses, but it will appear somewhere in the result. + * + *

In no case will it return a String of length greater than + * maxWidth.

+ * + *
+     * StringUtils.abbreviate(null, *, *)                = null
+     * StringUtils.abbreviate("", 0, 4)                  = ""
+     * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 0, 10)  = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 1, 10)  = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 4, 10)  = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 5, 10)  = "...fghi..."
+     * StringUtils.abbreviate("abcdefghijklmno", 6, 10)  = "...ghij..."
+     * StringUtils.abbreviate("abcdefghijklmno", 8, 10)  = "...ijklmno"
+     * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
+     * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
+     * StringUtils.abbreviate("abcdefghij", 0, 3)        = IllegalArgumentException
+     * StringUtils.abbreviate("abcdefghij", 5, 6)        = IllegalArgumentException
+     * 
+ * + * @param str the String to check, may be null + * @param offset left edge of source String + * @param maxWidth maximum length of result String, must be at least 4 + * @return abbreviated String, null if null String input + * @throws IllegalArgumentException if the width is too small + * @since 2.0 + */ + public static String abbreviate(String str, int offset, int maxWidth) { + if (str == null) { + return null; + } + if (maxWidth < 4) { + throw new IllegalArgumentException("Minimum abbreviation width is 4"); + } + if (str.length() <= maxWidth) { + return str; + } + if (offset > str.length()) { + offset = str.length(); + } + if ((str.length() - offset) < (maxWidth - 3)) { + offset = str.length() - (maxWidth - 3); + } + if (offset <= 4) { + return str.substring(0, maxWidth - 3) + "..."; + } + if (maxWidth < 7) { + throw new IllegalArgumentException("Minimum abbreviation width with offset is 7"); + } + if ((offset + (maxWidth - 3)) < str.length()) { + return "..." + abbreviate(str.substring(offset), maxWidth - 3); + } + return "..." + str.substring(str.length() - (maxWidth - 3)); + } + + /** + + + /** + /** + + // ToString + //----------------------------------------------------------------------- + /** + *

Gets the toString of an Object returning + * an empty string ("") if null input.

+ * + *
+     * ObjectUtils.toString(null)         = ""
+     * ObjectUtils.toString("")           = ""
+     * ObjectUtils.toString("bat")        = "bat"
+     * ObjectUtils.toString(Boolean.TRUE) = "true"
+     * 
+ * + * @see StringUtils#defaultString(String) + * @see String#valueOf(Object) + * @param obj the Object to toString, may be null + * @return the passed in Object's toString, or nullStr if null input + * @since 2.0 + */ + public static String toString(Object obj) { + return obj == null ? "" : obj.toString(); + } +} diff --git a/src/test/java/org/jboss/seam/render/test/BundledHelpersTest.java b/src/test/java/org/jboss/seam/render/test/BundledHelpersTest.java new file mode 100644 index 0000000..1454d3b --- /dev/null +++ b/src/test/java/org/jboss/seam/render/test/BundledHelpersTest.java @@ -0,0 +1,71 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.seam.render.test; + +import org.jboss.seam.render.template.compiler.CustomTemplateCompiler; +import org.junit.Test; +import org.mvel2.templates.CompiledTemplate; +import org.mvel2.templates.TemplateRuntime; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +/** + * @author Pete Royle + */ +public class BundledHelpersTest { + + @Test + public void testStringUtils() throws Exception { + testBundledHelper("TheInpu...", "@{render.abbreviate('TheInputString', 10)}"); + testBundledHelper("...putSt...", "@{render.abbreviate('TheInputString', 5, 11)}"); + testBundledHelper("TheInputString", "@{render.capitalize('theInputString')}"); + testBundledHelper(" TheInputString ", "@{render.center('TheInputString', 20)}"); + testBundledHelper("TheInputString", "@{render.defaultString('TheInputString', 'BlahBlah')}"); + testBundledHelper("BlahBlah", "@{render.defaultString('', 'BlahBlah')}"); + testBundledHelper("BlahBlah", "@{render.defaultString(null, 'BlahBlah')}"); + testBundledHelper("The Input String", "@{render.join(new String[]{'The', 'Input', 'String'}, ' ')}"); + testBundledHelper(" TheInputString", "@{render.leftPad('TheInputString', 20)}"); + testBundledHelper("XXXXXXTheInputString", "@{render.leftPad('TheInputString', 20, 'X')}"); + testBundledHelper("theinputstring", "@{render.lowerCase('TheInputString')}"); + testBundledHelper("TheOutputString", "@{render.replace('TheInputString', 'In', 'Out')}"); + testBundledHelper("TheInputString ", "@{render.rightPad('TheInputString', 20)}"); + testBundledHelper("TheInputStringXXXXXX", "@{render.rightPad('TheInputString', 20, 'X')}"); + testBundledHelper(new String[]{"The", "Input", "String"}, "@{render.split('The Input String')}"); + testBundledHelper(new String[]{"TheI", "putStri", "g"}, "@{render.split('TheInputString', 'n')}"); + testBundledHelper("tHEiNPUTsTRING", "@{render.swapCase('TheInputString')}"); + testBundledHelper("TheInputString", "@{render.trim(' TheInputString ')}"); + testBundledHelper("theInputString", "@{render.uncapitalize('TheInputString')}"); + testBundledHelper("THEINPUTSTRING", "@{render.upperCase('TheInputString')}"); + } + + private void testBundledHelper(Object expectedResult, String mvelExpression) throws SecurityException, NoSuchMethodException { + CompiledTemplate template = CustomTemplateCompiler.compileTemplate(mvelExpression); + Object result = TemplateRuntime.execute(template); + if (result instanceof Object[]) { + assertArrayEquals((Object[]) expectedResult, (Object[]) result); + } else { + assertEquals(expectedResult, result); + } + } + +}