You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ba...@apache.org on 2010/07/06 08:46:56 UTC

svn commit: r960814 - in /tomcat/taglibs/standard/trunk/impl/src: main/java/org/apache/taglibs/standard/functions/Functions.java test/java/org/apache/taglibs/standard/functions/TestFunctions.java

Author: bayard
Date: Tue Jul  6 06:46:55 2010
New Revision: 960814

URL: http://svn.apache.org/viewvc?rev=960814&view=rev
Log:
Patch from Jeremy Boynes (#49554):

"The implementation of Functions has checks for null parameters that are
 redundant given coercion required by the Engine. As discussed on the mailing
 list, these checks can be removed.

 Functions should have JavaDoc.

 Some function implementations, such as replace, are now available in the JRE
 allowing this class to be simplified. Also usage of StringBuffer can be
 replaced with StringBuilder avoiding unneeded synchronization."


Added:
    tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java   (with props)
Modified:
    tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/functions/Functions.java

Modified: tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/functions/Functions.java
URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/functions/Functions.java?rev=960814&r1=960813&r2=960814&view=diff
==============================================================================
--- tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/functions/Functions.java (original)
+++ tomcat/taglibs/standard/trunk/impl/src/main/java/org/apache/taglibs/standard/functions/Functions.java Tue Jul  6 06:46:55 2010
@@ -30,7 +30,13 @@ import org.apache.taglibs.standard.resou
 import org.apache.taglibs.standard.tag.common.core.Util;
 
 /**
- * <p>JSTL Functions</p>
+ * Static functions that extend the Expression Language with standardized behaviour
+ * commonly used by page authors.
+ *
+ * <strong>Implementation Note:</strong> When passing a String parameter, section
+ * 1.18.2 of the EL specification requires the container to coerce a null value to an
+ * empty string. These implementation assume such behaviour and do not check for null
+ * parameters. Passing a null will generally trigger a NullPointerException.
  * 
  * @author Pierre Delisle
  */
@@ -41,14 +47,22 @@ public class Functions {
     // String capitalization
 
     /**
-     * Converts all of the characters of the input string to upper case.
+     * Converts all of the characters of the input string to upper case according to the
+     * semantics of method <code>String#toUpperCase()</code>.
+     *
+     * @param input the input string on which the transformation to upper case is applied
+     * @return the input string transformed to upper case
      */
     public static String toUpperCase(String input) {
         return input.toUpperCase();
     }
 
     /**
-     * Converts all of the characters of the input string to lower case.
+     * Converts all of the characters of the input string to lower case according to the
+     * semantics of method <code>String#toLowerCase()</code>.
+     *
+     * @param input the input string on which the transformation to lower case is applied
+     * @return the input string transformed to lower case
      */
     public static String toLowerCase(String input) {
         return input.toLowerCase();
@@ -56,52 +70,105 @@ public class Functions {
     
     //*********************************************************************
     // Substring processing
-    
+
+    /**
+     * Returns the index (0-based) withing a string of the first occurrence of a specified
+     * substring according to the semantics of the method <code>String#indexOf()</code>.
+     *
+     * If <code>substring</code> is empty, this matches the beginning of the string and the
+     * value returned is 0.
+     *
+     * @param input the input string on which the function is applied
+     * @param substring the substring to search for in the input string
+     * @return the 0-based index of the first matching substring, or -1 if it does not occur
+     */
     public static int indexOf(String input, String substring) {
-        if (input == null) input = "";
-        if (substring == null) substring = "";
         return input.indexOf(substring);
-    }    
+    }
 
+    /**
+     * Tests if a string contains the specified substring.
+     *
+     * @param input the input string on which the function is applied
+     * @param substring the substring tested for
+     * @return true if the character sequence represented by the substring
+     * exists in the string
+     */
     public static boolean contains(String input, String substring) {
-        return indexOf(input, substring) != -1;
+        return input.contains(substring);
     }    
 
+    /**
+     * Tests if a string contains the specified substring in a case insensitive way.
+     * Equivalent to <code>fn:contains(fn:toUpperCase(string), fn:toUpperCase(substring))</code>.
+     *
+     * @param input the input string on which the function is applied
+     * @param substring the substring tested for
+     * @return true if the character sequence represented by the substring
+     * exists in the string
+     */
     public static boolean containsIgnoreCase(String input, String substring) {
-        if (input == null) input = "";
-        if (substring == null) substring = "";        
-        String inputUC = input.toUpperCase();
-        String substringUC = substring.toUpperCase();
-        return indexOf(inputUC, substringUC) != -1;
-    }    
+        return contains(input.toUpperCase(), substring.toUpperCase());
+    }
 
-    public static boolean startsWith(String input, String substring) {
-        if (input == null) input = "";
-        if (substring == null) substring = "";
-        return input.startsWith(substring);
+    /**
+     * Tests if a string starts with the specified prefix according to the semantics
+     * of <code>String#startsWith()</code>.
+     *
+     * @param input the input string on which the function is applied
+     * @param prefix the prefix to be matched
+     * @return true if the input string starts with the prefix
+     */
+    public static boolean startsWith(String input, String prefix) {
+        return input.startsWith(prefix);
     }    
         
-    public static boolean endsWith(String input, String substring) {
-        if (input == null) input = "";
-        if (substring == null) substring = "";
-        return input.endsWith(substring);
-    }  
-    
+    /**
+     * Tests if a string ends with the specified suffix according to the semantics
+     * of <code>String#endsWith()</code>.
+     *
+     * @param input the input string on which the function is applied
+     * @param suffix the suffix to be matched
+     * @return true if the input string ends with the suffix
+     */
+    public static boolean endsWith(String input, String suffix) {
+        return input.endsWith(suffix);
+    }
+
+    /**
+     * Returns a subset of a string according to the semantics of <code>String#substring()</code>
+     * with additional semantics as follows:
+     * <ul>
+     * <li>if <code>beginIndex < 0</code> its value is adjusted to 0</li>
+     * <li>if <code>endIndex < 0 or greater than the string length</code>,
+     * its value is adjusted to the length of the string</li>
+     * <li>if <code>endIndex < beginIndex</code>, an empty string is returned</li>
+     * </ul>
+     *
+     * @param input the input string on which the substring function is applied
+     * @param beginIndex the beginning index (0-based), inclusive
+     * @param endIndex the end index (0-based), exclusive
+     * @return a subset of string
+     */
     public static String substring(String input, int beginIndex, int endIndex) {
-        if (input == null) input = "";
-        if (beginIndex >= input.length()) return "";
         if (beginIndex < 0) beginIndex = 0;
         if (endIndex < 0 || endIndex > input.length()) endIndex = input.length();
         if (endIndex < beginIndex) return "";
         return input.substring(beginIndex, endIndex);
-    }    
-    
+    }
+
+    /**
+     * Returns a subset of a string following the first occurrence of a specific substring.
+     *
+     * If the substring is empty, it matches the beginning of the input string and the
+     * entire input string is returned. If the substring does not occur, an empty string is returned.
+     *
+     * @param input the input string on which the substring function is applied
+     * @param substring the substring that delimits the beginning of the subset
+     * of the input string to be returned
+     * @return a substring of the input string that starts at the first character after the specified substring
+     */
     public static String substringAfter(String input, String substring) {
-        if (input == null) input = "";
-        if (input.length() == 0) return "";
-        if (substring == null) substring = "";
-        if (substring.length() == 0) return input;
-        
         int index = input.indexOf(substring);
         if (index == -1) {
             return "";
@@ -110,12 +177,18 @@ public class Functions {
         }
     }    
         
+    /**
+     * Returns a subset of a string immediately before the first occurrence of a specific substring.
+     *
+     * If the substring is empty, it matches the beginning of the input string and an empty string is returned.
+     * If the substring does not occur, an empty string is returned.
+     *
+     * @param input the input string on which the substring function is applied
+     * @param substring the substring that delimits the beginning of the subset
+     * of the input string to be returned
+     * @return a substring of the input string that starts at the first character after the specified substring
+     */
     public static String substringBefore(String input, String substring) {
-        if (input == null) input = "";
-        if (input.length() == 0) return "";
-        if (substring == null) substring = "";
-        if (substring.length() == 0) return "";
-
         int index = input.indexOf(substring);
         if (index == -1) {
             return "";
@@ -126,75 +199,124 @@ public class Functions {
 
     //*********************************************************************
     // Character replacement
-    
+
+    /**
+     * Escapes characters that could be interpreted as XML markup as defined by the <code>&lt;c:out&gt; action.
+     *
+     * @param input the string to escape
+     * @return escaped string
+     */
     public static String escapeXml(String input) {
-        if (input == null) return "";
         return Util.escapeXml(input);
     }
-    
+
+    /**
+     * removes whitespace from both ends of a string according to the semantics of <code>String#trim()</code>.
+     *
+     * @param input the input string to be trimmed
+     * @return the trimmed string
+     */
     public static String trim(String input) {
-        if (input == null) return "";
         return input.trim();
-    }    
+    }
 
-    public static String replace(
-    String input, 
-    String substringBefore,
-    String substringAfter) 
+    /**
+     * Returns a string resulting from replacing all occurrences of a "before" substring with an "after" substring.
+     * The string is processed once and not reprocessed for further replacements.
+     *
+     * @param input the string on which the replacement is to be applied
+     * @param before the substring to replace
+     * @param after the replacement substring
+     * @return a string with before replaced with after
+     */
+    public static String replace(String input, String before, String after)
     {
-        if (input == null) input = "";
-        if (input.length() == 0) return "";
-        if (substringBefore == null) substringBefore = "";
-        if (substringBefore.length() == 0) return input;
-                
-        StringBuffer buf = new StringBuffer(input.length());
-        int startIndex = 0;
-        int index;
-        while ((index = input.indexOf(substringBefore, startIndex)) != -1) {
-            buf.append(input.substring(startIndex, index)).append(substringAfter);
-            startIndex = index + substringBefore.length();
+        if (before.length() == 0) {
+            return input;
         }
-        return buf.append(input.substring(startIndex)).toString();
+        return input.replace(before, after);
     }
-    
-    public static String[] split(
-    String input, 
-    String delimiters) 
+
+    /**
+     * Splits a string into an array of substrings according to the semantics of <code>StringTokenizer</code>.
+     * If the input string is empty, a single element array containing an empty string is returned.
+     * If the delimiters are empty, a single element array containing the input string is returned.
+     *
+     * @param input the string to split
+     * @param delimiters characters used to split the string
+     * @return an array of strings
+     */
+    public static String[] split(String input, String delimiters)
     {
-        String[] array;
-        if (input == null) input = "";
-        if (input.length() == 0) {
-            array = new String[1];
-            array[0] = "";
-            return array;
+        if (input.length() == 0 || delimiters.length() == 0) {
+            return new String[] { input };
         }
-        
-        if (delimiters == null) delimiters = "";
 
         StringTokenizer tok = new StringTokenizer(input, delimiters);
-        int count = tok.countTokens();
-        array = new String[count];
+        String[] array = new String[tok.countTokens()];
         int i = 0;
         while (tok.hasMoreTokens()) {
             array[i++] = tok.nextToken();
         }
         return array;
-    }        
-        
+    }
+
+    /**
+     * Joins all elements of an array into a string.
+     *
+     * <strong>Implementation Note</strong>: The specification does not define what happens when
+     * elements in the array are null. For compatibility with previous implementations, the string
+     * "null" is used although EL conventions would suggest an empty string might be better.
+     *
+     * @param array an array of strings to be joined
+     * @param separator used to separate the joined strings
+     * @return all array elements joined into one string with the specified separator
+     */
+    public static String join(String[] array, String separator) {
+        if (array == null || array.length == 0) {
+            return "";
+        }
+        if (array.length == 1) {
+            return array[0] == null ? "null" : array[0];
+        }
+
+        StringBuilder buf = new StringBuilder();
+        buf.append(array[0]);
+        for (int i = 1; i < array.length; i++) {
+            buf.append(separator).append(array[i]);
+        }
+        return buf.toString();
+    }
+
     //*********************************************************************
     // Collections processing
-    
+
+    /**
+     * Returns the number of items in a collection or the number of characters in a string.
+     * The collection can be of any type supported for the <code>items</code> attribute of
+     * the <code>&lt;c:forEach&gt;</code> action.
+     *
+     * @param obj the collection or string whose length should be computed
+     * @return the length of the collection or string; 0 if obj is null
+     * @throws JspTagException if the type is not valid
+     */
     public static int length(Object obj) throws JspTagException {
-        if (obj == null) return 0;  
-        
-        if (obj instanceof String) return ((String)obj).length();
-        if (obj instanceof Collection) return ((Collection)obj).size();
-        if (obj instanceof Map) return ((Map)obj).size();
+        if (obj == null) {
+            return 0;
+        }
         
-        int count = 0;
+        if (obj instanceof String) {
+            return ((String) obj).length();
+        }
+        if (obj instanceof Collection) {
+            return ((Collection) obj).size();
+        }
+        if (obj instanceof Map) {
+            return ((Map) obj).size();
+        }
         if (obj instanceof Iterator) {
+            int count = 0;
             Iterator iter = (Iterator)obj;
-            count = 0;
             while (iter.hasNext()) {
                 count++;
                 iter.next();
@@ -203,30 +325,16 @@ public class Functions {
         }            
         if (obj instanceof Enumeration) {
             Enumeration enum_ = (Enumeration)obj;
-            count = 0;
+            int count = 0;
             while (enum_.hasMoreElements()) {
                 count++;
                 enum_.nextElement();
             }
             return count;
         }
-        try {
-            count = Array.getLength(obj);
-            return count;
-        } catch (IllegalArgumentException ex) {}
-        throw new JspTagException(Resources.getMessage("PARAM_BAD_VALUE"));        
-    }      
-
-    public static String join(String[] array, String separator) {
-        if (array == null) return "";         
-        if (separator == null) separator = "";
-        
-        StringBuffer buf = new StringBuffer();
-        for (int i=0; i<array.length; i++) {
-            buf.append(array[i]);
-            if (i < array.length-1) buf.append(separator);
+        if (obj.getClass().isArray()) {
+            return Array.getLength(obj);
         }
-        
-        return buf.toString();
-    }
+        throw new JspTagException(Resources.getMessage("PARAM_BAD_VALUE"));
+    }      
 }

Added: tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java
URL: http://svn.apache.org/viewvc/tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java?rev=960814&view=auto
==============================================================================
--- tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java (added)
+++ tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java Tue Jul  6 06:46:55 2010
@@ -0,0 +1,114 @@
+/*
+ * 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.taglibs.standard.functions;
+
+import org.apache.taglibs.standard.resources.Resources;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.servlet.jsp.JspTagException;
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.apache.taglibs.standard.functions.Functions.*;
+/**
+ */
+public class TestFunctions {
+
+    @Test
+    public void testSubstring() {
+        Assert.assertEquals("el", substring("Hello", 1, 3));
+        Assert.assertEquals("", substring("Hello", 10, 0));
+        Assert.assertEquals("He", substring("Hello", -1, 2));
+        Assert.assertEquals("Hello", substring("Hello", -4, -1));
+        Assert.assertEquals("ello", substring("Hello", 1, -1));
+        Assert.assertEquals("ello", substring("Hello", 1, 10));
+        Assert.assertEquals("", substring("Hello", 3, 1));
+        Assert.assertEquals("", substring("Hello", 10, 6));
+        Assert.assertEquals("Hello", substring("Hello", -1, -4));
+    }
+
+    @Test
+    public void testSubstringAfter() {
+        Assert.assertEquals("lo", substringAfter("Hello", "el"));
+        Assert.assertEquals("", substringAfter("", "el"));
+        Assert.assertEquals("Hello", substringAfter("Hello", ""));
+        Assert.assertEquals("", substringAfter("", "lx"));
+        Assert.assertEquals("lo All", substringAfter("Hello All", "l"));
+    }
+
+    @Test
+    public void testSubstringBefore() {
+        Assert.assertEquals("H", substringBefore("Hello", "el"));
+        Assert.assertEquals("", substringBefore("", "el"));
+        Assert.assertEquals("", substringBefore("Hello", ""));
+        Assert.assertEquals("", substringBefore("", "lx"));
+        Assert.assertEquals("He", substringBefore("Hello All", "l"));
+    }
+
+    @Test
+    public void testReplace() {
+        Assert.assertEquals("Hxxlo", replace("Hello", "el", "xx"));
+        Assert.assertEquals("Hexxxxo", replace("Hello", "l", "xx"));
+        Assert.assertEquals("", replace("", "l", "xx"));
+        Assert.assertEquals("Heo", replace("Hello", "l", ""));
+        Assert.assertEquals("Hello", replace("Hello", "", "xx"));
+        Assert.assertEquals("Hellllo", replace("Hello", "l", "ll"));
+        Assert.assertEquals("Hello", replace("Hello", "x", "ll"));
+    }
+
+    @Test
+    public void testSplit() {
+        Assert.assertArrayEquals(new String[]{"a", "b", "c"}, split("a:b:c", ":"));
+        Assert.assertArrayEquals(new String[]{"a", "b", "c"}, split("a:b/c", ":/"));
+        Assert.assertArrayEquals(new String[]{"a", "b", "c"}, split("a:b/c", "/:"));
+        Assert.assertArrayEquals(new String[]{"a", "b"}, split("a:b:", ":"));
+        Assert.assertArrayEquals(new String[]{"a:b:c"}, split("a:b:c", "x"));
+        Assert.assertArrayEquals(new String[]{""}, split("", ""));
+        Assert.assertArrayEquals(new String[]{""}, split("", ":"));
+        Assert.assertArrayEquals(new String[]{"Hello"}, split("Hello", ""));
+    }
+
+    @Test
+    public void testJoin() {
+        Assert.assertEquals("a:b:c", join(new String[]{"a", "b", "c"}, ":"));
+        Assert.assertEquals("abc", join(new String[]{"a", "b", "c"}, ""));
+        Assert.assertEquals("axxbxxc", join(new String[]{"a", "b", "c"}, "xx"));
+        Assert.assertEquals("", join(null, ""));
+        Assert.assertEquals("", join(new String[]{}, ":"));
+        Assert.assertEquals("a:null:c", join(new String[]{"a", null, "c"}, ":"));
+        Assert.assertEquals("a", join(new String[]{"a"}, ":"));
+        Assert.assertEquals("null", join(new String[]{null}, ":"));
+    }
+
+    @Test
+    public void testLength() throws Exception {
+        Assert.assertEquals(0, length(null));
+        Assert.assertEquals(0, length(""));
+        Assert.assertEquals(3, length(new int[]{1,2,3}));
+        Assert.assertEquals(3, length(Arrays.asList(1,2,3)));
+        Assert.assertEquals(3, length(Arrays.asList(1,2,3).iterator()));
+        Assert.assertEquals(3, length(Collections.enumeration(Arrays.asList(1,2,3))));
+        Assert.assertEquals(1, length(Collections.singletonMap("Hello", "World")));
+        try {
+            length(3);
+            Assert.fail();
+        } catch (JspTagException e) {
+            Assert.assertEquals(Resources.getMessage("PARAM_BAD_VALUE"), e.getMessage());
+        }
+    }
+}

Propchange: tomcat/taglibs/standard/trunk/impl/src/test/java/org/apache/taglibs/standard/functions/TestFunctions.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org