org.metawidget.util.simple.StringUtils Maven / Gradle / Ivy
// Metawidget
//
// This file is dual licensed under both the LGPL
// (http://www.gnu.org/licenses/lgpl-2.1.html) and the EPL
// (http://www.eclipse.org/org/documents/epl-v10.php). As a
// recipient of Metawidget, you may choose to receive it under either
// the LGPL or the EPL.
//
// Commercial licenses are also available. See http://metawidget.org
// for details.
package org.metawidget.util.simple;
import java.util.Comparator;
/**
* Utilities for working with Strings.
*
* @author Richard Kennard
*/
public final class StringUtils {
//
// Public statics
//
/**
* Forward slash character.
*
* For environments that use the fully qualified class name (eg. SwingMetawidget) as part of the
* path, we must use '/' not '.' as the separator.
*/
public static final char SEPARATOR_FORWARD_SLASH_CHAR = '/';
public static final String SEPARATOR_FORWARD_SLASH = String.valueOf( SEPARATOR_FORWARD_SLASH_CHAR );
public static final char SEPARATOR_DOT_CHAR = '.';
public static final String SEPARATOR_DOT = String.valueOf( SEPARATOR_DOT_CHAR );
public static final char SEPARATOR_COMMA_CHAR = ',';
public static final String SEPARATOR_COMMA = String.valueOf( SEPARATOR_COMMA_CHAR );
public static final char SEPARATOR_COLON_CHAR = ':';
public static final String SEPARATOR_COLON = String.valueOf( SEPARATOR_COLON_CHAR );
public static final String RESOURCE_KEY_NOT_FOUND_PREFIX = "???";
public static final String RESOURCE_KEY_NOT_FOUND_SUFFIX = "???";
/**
* Decapitalize without using java.beans
, as that package is not available on all
* target platforms.
*
* Following the rules defined in java.beans.Introspector: "This normally means
* converting the first character from upper case to lower case, but in the (unusual) special
* case when there is more than one character and both the first and second characters are upper
* case, we leave it alone. Thus 'FooBah' becomes 'fooBah' and 'X' becomes 'x', but 'URL' stays
* as 'URL'"
*/
public static String decapitalize( String in ) {
if ( in.length() == 0 ) {
return in;
}
// Nothing to do?
char firstChar = in.charAt( 0 );
if ( Character.isLowerCase( firstChar ) ) {
return in;
}
// Second letter uppercase?
if ( in.length() > 1 && Character.isUpperCase( in.charAt( 1 ) ) ) {
return in;
}
return Character.toLowerCase( firstChar ) + in.substring( 1 );
}
/**
* Capitalize by uppercasing the first letter of the given String (e.g. from
* 'fooBarBaz' to 'FooBarBaz').
*
* The rules for capitalizing are not clearly defined in java.beans.Introspector, but
* we try to make capitalize
the inverse of decapitalize
(this
* includes the 'second character' clause). For example, in Eclipse if you define a property
* 'aB123' and then 'generate getters' Eclipse will generate a method called 'getaB123'
* not 'getAB123'. See: https://community.jboss.org/thread/203202?start=0&tstart=0
*/
public static String capitalize( String in ) {
if ( in.length() == 0 ) {
return in;
}
// Second letter uppercase?
if ( in.length() > 1 && Character.isUpperCase( in.charAt( 1 ) ) ) {
return in;
}
return Character.toUpperCase( in.charAt( 0 ) ) + in.substring( 1 );
}
/**
* Converts the given string from camel case.
*
* For example, converts fooBar1
into Foo Bar 1
. Used primarily to
* convert property names and paths into human-readable UI labels.
*/
public static String uncamelCase( String camelCase ) {
return uncamelCase( camelCase, ' ' );
}
/**
* Converts the given string from camel case.
*
* For example, converts fooBar1
into Foo Bar 1
. Used primarily to
* convert property names and paths into human-readable UI labels.
*/
public static String uncamelCase( String camelCase, char separator ) {
// Nothing to do?
if ( camelCase == null ) {
return null;
}
int length = camelCase.length();
StringBuilder builder = new StringBuilder( length );
boolean first = true;
char lastChar = separator;
char[] chars = camelCase.toCharArray();
for ( int loop = 0; loop < length; loop++ ) {
char c = chars[loop];
if ( first ) {
builder.append( Character.toUpperCase( c ) );
first = false;
} else if ( Character.isUpperCase( c ) && ( !Character.isUpperCase( lastChar ) || ( loop < chars.length - 1 && chars[loop + 1] != separator && !Character.isUpperCase( chars[loop + 1] ) ) ) ) {
if ( lastChar != separator ) {
builder.append( separator );
}
// Don't do: if ( loop + 1 < length && !Character.isUpperCase( chars[loop + 1] ) )
// buffer.append( Character.toLowerCase( c ) );
//
// It's ambiguous if we should lowercase the letter following a space, but in
// general it looks nicer most of the time not to. The exception is 'joining' words
// such as 'of' in 'Date of Birth'
builder.append( c );
} else if ( Character.isDigit( c ) && Character.isLetter( lastChar ) && lastChar != separator ) {
builder.append( separator );
builder.append( c );
} else {
builder.append( c );
}
lastChar = c;
}
return builder.toString();
}
/**
* Converts the given String to camel case.
*
* The first letter following a separator is capitalized, as per Java convention. Non
* alpha numeric characters are also stripped. However no attempt is made to de
* capitalize the first name, because that gets very ambiguous with names like 'URL', 'ID' etc.
*
* Used primarily to convert property paths into ids.
*/
public static String camelCase( String text ) {
return camelCase( text, ' ' );
}
/**
* Converts the given String to camel case.
*
* The first letter following a separator is capitalized, as per Java convention. Non
* alpha numeric characters are also stripped. The start of the String is decapitalized.
*
* Used primarily to convert property paths into ids.
*/
public static String camelCase( String text, char separator ) {
StringBuilder builder = new StringBuilder( text.length() );
// Convert separators to camel case
boolean lastWasSeparator = false;
char[] chars = StringUtils.decapitalize( text ).toCharArray();
for ( int loop = 0, length = chars.length; loop < length; loop++ ) {
char c = chars[loop];
if ( c == separator ) {
lastWasSeparator = true;
continue;
}
if ( lastWasSeparator ) {
builder.append( Character.toUpperCase( c ) );
lastWasSeparator = false;
continue;
}
builder.append( c );
}
return builder.toString();
}
/**
* Version of String.valueOf
that fails 'quietly' for null
Strings and
* returns an empty String rather than a String saying null
.
*/
public static String quietValueOf( Object object ) {
if ( object == null ) {
return "";
}
return object.toString();
}
/**
* Returns the portion of the overall string that comes before the given string. If the given
* string is not found in the overall string, returns the entire string.
*/
public static String substringBefore( String text, String before ) {
int indexOf = text.indexOf( before );
if ( indexOf == -1 ) {
return text;
}
return text.substring( 0, indexOf );
}
/**
* Returns the portion of the overall string that comes after the first occurance of the given
* string. If the given string is not found in the overall string, returns the entire string.
*/
public static String substringAfter( String text, String after ) {
int indexOf = text.indexOf( after );
if ( indexOf == -1 ) {
return text;
}
return text.substring( indexOf + after.length() );
}
/**
* Returns the portion of the overall string that comes after the last occurance of the given
* string. If the given string is not found in the overall string, returns the entire string.
*/
public static String substringAfterLast( String text, char after ) {
int indexOf = text.lastIndexOf( after );
if ( indexOf == -1 ) {
return text;
}
return text.substring( indexOf + 1 );
}
/**
* Returns the portion of the overall string that comes after the last occurance of the given
* string. If the given string is not found in the overall string, returns the entire string.
*/
public static String substringAfterLast( String text, String after ) {
int indexOf = text.lastIndexOf( after );
if ( indexOf == -1 ) {
return text;
}
return text.substring( indexOf + after.length() );
}
/**
* Comparator that orders String
objects as by compareToIgnoreCase
.
*
* Like String.CASE_INSENSITIVE_COMPARATOR but sorts case sensitively within
* case insensitive groups, thereby ensuring comparing 'Foo' to 'foo' does not equal 0. This is
* important if the Comparator is used within, say, a TreeMap. Otherwise the TreeMap will
* 'collapse' case insensitive keys.
*/
public static final Comparator CASE_INSENSITIVE_COMPARATOR = new Comparator() {
public int compare( String lhs, String rhs ) {
int compareTo = lhs.compareToIgnoreCase( rhs );
if ( compareTo == 0 ) {
compareTo = lhs.compareTo( rhs );
}
return compareTo;
}
};
//
// Private constructor
//
private StringUtils() {
// Can never be called
}
}