Change StringUtils arguments from String to CharSequence.

Details: Working with (trunk) StringUtils (SU) I see the following emerge: 

- In SVN already and continuing: Change StringUtils arguments from String to CharSequence (CS).

- This leads to replacing calls to String.substring(int[,int]) with calls to CharSequence.subSequence(int)

- This leads to creating a CharSequenceUtils class (in SVN now, more on this new class below) and CharSequenceUtils.subSequence(CharSequence,int) to avoid changing "str.substring(start)" over and over to "str.subSequence(start, str.length())". For examples, see new versions of capitalize and uncapitalize.

- We end up using a toString() on CharSequence to return a String from StringUtil when working with a CharSequence.

So we have StringUtils using CharSequence inputs as much as possible instead of String, which is nice. 

The CharSequence method subSequence returns a CharSequence; though the Javadoc states "Returns a new CharSequence that is a subsequence of this sequence.", this does not guaranteed the return value to be the same kind of CharSequence as the receiver). Since we are after all in a class called StringUtil, calling toString() is a must.

I propose that we create when possible the methods that are now StringUtils CharSequence methods into CharSequenceUtils and let StringUtil call CharSequenceUtils and then do its toString() and other String specific logic. Later we could have other CharSequence type of utils (for CharBuffer, StringBuiler, StringBuffer, etc) that use the 'primitives' from CharSequenceUtils.
This means that for methods that are based solely on methods that are now in CharSequence, these can be moved to CharSequenceUtils without effort (all is* methods only call CharSequence#length() and charAt() for example and are now typed as CS, still in SU). 

We can leave @deprecateds method in SU as a nicety to avoid too much porting pain: First change the package to lang3 then you can 'optimize' by changing call sites from SU to CSU.

As a start, I put in SVN a CharSequenceUtils (CSU) implementation for length() and subSequence().


git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@920543 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Gary D. Gregory 2010-03-08 22:15:08 +00:00
parent 9c0379850b
commit 7cfa8a9328
4 changed files with 407 additions and 212 deletions

View File

@ -0,0 +1,81 @@
/*
* 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.commons.lang3;
/**
* Null-safe CharSequence utility methods.
*
* @author Gary Gregory
*/
public class CharSequenceUtils {
/**
* Gets a CharSequence length or <code>0</code> if the CharSequence is
* <code>null</code>.
*
* @param cs
* a CharSequence or <code>null</code>
* @return CharSequence length or <code>0</code> if the CharSequence is
* <code>null</code>.
* @since 3.0
*/
public static int length(CharSequence cs) {
return cs == null ? 0 : cs.length();
}
/**
* <p>Reverses a CharSequence as per {@link StringBuilder#reverse()}.</p>
*
* <p>A <code>null</code> CharSequence returns <code>null</code>.</p>
*
* <pre>
* CharSequenceUtils.reverse(null) = null
* CharSequenceUtils.reverse("").toString() = ""
* CharSequenceUtils.reverse("bat").toString() = "tab"
* </pre>
*
* @param str the String to reverse, may be null
* @return the reversed String, <code>null</code> if null String input
*/
public static CharSequence reverse(CharSequence str) {
if (str == null) {
return null;
}
return new StringBuilder(str).reverse();
}
/**
* Returns a new <code>CharSequence</code> that is a subsequence of this
* sequence starting with the <code>char</code> value at the specified
* index. The length (in <code>char</code>s) of the returned sequence is
* <code>length() - start</code>, so if <code>start == end</code> then an
* empty sequence is returned. </p>
*
* @param start
* the start index, inclusive
*
* @return the specified subsequence
*
* @throws IndexOutOfBoundsException
* if <code>start</code> is negative or if <code>start</code> is
* greater than <code>length()</code>
* @since 3.0
*/
public static CharSequence subSequence(CharSequence cs, int start) {
return cs == null ? null : cs.subSequence(start, cs.length());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,92 @@
/*
* 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.commons.lang3;
import java.nio.CharBuffer;
import junit.framework.Assert;
import junit.framework.TestCase;
/**
* Tests CharSequenceUtils
*
* @author Gary Gregory
*/
public class CharSequenceUtilsTest extends TestCase {
public void testLength_CharBuffer() {
Assert.assertEquals(0, CharSequenceUtils.length(CharBuffer.wrap("")));
Assert.assertEquals(1, CharSequenceUtils.length(CharBuffer.wrap("A")));
Assert.assertEquals(1, CharSequenceUtils.length(CharBuffer.wrap(" ")));
Assert.assertEquals(8, CharSequenceUtils.length(CharBuffer.wrap("ABCDEFGH")));
}
public void testLength_String() {
Assert.assertEquals(0, CharSequenceUtils.length(null));
Assert.assertEquals(0, CharSequenceUtils.length(""));
Assert.assertEquals(1, CharSequenceUtils.length("A"));
Assert.assertEquals(1, CharSequenceUtils.length(" "));
Assert.assertEquals(8, CharSequenceUtils.length("ABCDEFGH"));
}
public void testLength_StringBuffer() {
Assert.assertEquals(0, CharSequenceUtils.length(new StringBuffer("")));
Assert.assertEquals(1, CharSequenceUtils.length(new StringBuffer("A")));
Assert.assertEquals(1, CharSequenceUtils.length(new StringBuffer(" ")));
Assert.assertEquals(8, CharSequenceUtils.length(new StringBuffer("ABCDEFGH")));
}
public void testLength_StringBuilder() {
Assert.assertEquals(0, CharSequenceUtils.length(new StringBuilder("")));
Assert.assertEquals(1, CharSequenceUtils.length(new StringBuilder("A")));
Assert.assertEquals(1, CharSequenceUtils.length(new StringBuilder(" ")));
Assert.assertEquals(8, CharSequenceUtils.length(new StringBuilder("ABCDEFGH")));
}
public void testSubSequence() {
//
// null input
//
Assert.assertEquals(null, CharSequenceUtils.subSequence(null, -1));
Assert.assertEquals(null, CharSequenceUtils.subSequence(null, 0));
Assert.assertEquals(null, CharSequenceUtils.subSequence(null, 1));
//
// non-null input
//
Assert.assertEquals(StringUtils.EMPTY, CharSequenceUtils.subSequence(StringUtils.EMPTY, 0));
Assert.assertEquals("012", CharSequenceUtils.subSequence("012", 0));
Assert.assertEquals("12", CharSequenceUtils.subSequence("012", 1));
Assert.assertEquals("2", CharSequenceUtils.subSequence("012", 2));
Assert.assertEquals(StringUtils.EMPTY, CharSequenceUtils.subSequence("012", 3));
//
// Exception expected
//
try {
Assert.assertEquals(null, CharSequenceUtils.subSequence(StringUtils.EMPTY, -1));
Assert.fail("Expected " + IndexOutOfBoundsException.class.getName());
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
Assert.assertEquals(null, CharSequenceUtils.subSequence(StringUtils.EMPTY, 1));
Assert.fail("Expected " + IndexOutOfBoundsException.class.getName());
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
}

View File

@ -118,13 +118,26 @@ public void testCaseFunctions() {
assertEquals("capitalize(empty-string) failed",
"", StringUtils.capitalize("") );
assertEquals("capitalize(single-char-string) failed",
"X", StringUtils.capitalize("x") );
"X", StringUtils.capitalize("x") );
assertEquals("capitalize(single-char-string) failed",
"X", StringUtils.capitalize(new StringBuilder("x")) );
assertEquals("capitalize(single-char-string) failed",
"X", StringUtils.capitalize(new StringBuffer("x")) );
assertEquals("capitalize(single-char-string) failed",
"X", StringUtils.capitalize(CharBuffer.wrap("x")) );
assertEquals("uncapitalize(String) failed",
FOO_UNCAP, StringUtils.uncapitalize(FOO_CAP) );
assertEquals("uncapitalize(empty-string) failed",
"", StringUtils.uncapitalize("") );
assertEquals("uncapitalize(single-char-string) failed",
"x", StringUtils.uncapitalize("X") );
"x", StringUtils.uncapitalize("X") );
assertEquals("uncapitalize(single-char-string) failed",
"x", StringUtils.uncapitalize(new StringBuilder("X")) );
assertEquals("uncapitalize(single-char-string) failed",
"x", StringUtils.uncapitalize(new StringBuffer("X")) );
assertEquals("uncapitalize(single-char-string) failed",
"x", StringUtils.uncapitalize(CharBuffer.wrap("X")) );
// reflection type of tests: Sentences.
assertEquals("uncapitalize(capitalize(String)) failed",