/*************************************************************************
* Copyright 2009-2015 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
************************************************************************/
package com.eucalyptus.util;
import java.util.Collections;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
/**
* Utility functions for strings.
*/
public class Strings {
/**
* Null safe string conversion
*
* @param object The object to convert to a String
* @return The object as a String or null if null
*/
public static String toString( @Nullable final Object object ) {
return Objects.toString( object, null );
}
/**
* Remove optional prefix from the given text.
*
* @param text The text to trim
* @return The trimmed text or null if text was null
*/
public static String trimPrefix( @Nonnull final String prefix,
@Nullable final String text ) {
if ( text != null && text.startsWith( prefix ) ) {
return text.substring( prefix.length( ) );
} else {
return text;
}
}
/**
* Remove optional suffix from the given text.
*
* @param text The text to trim
* @return The trimmed text or null if text was null
*/
public static String trimSuffix( @Nonnull final String suffix,
@Nullable final String text ) {
if ( text != null && text.endsWith( suffix ) ) {
return text.substring( 0, text.length( ) - suffix.length( ) );
} else {
return text;
}
}
/**
* Get a Function for trimming a String.
*
* <P>The returned function will pass through null values.</P>
*
* @return The trimming function
* @see String#trim()
*/
public static CompatFunction<String,String> trim() {
return StringFunctions.TRIM;
}
/**
* Truncate the given text to the specified length.
*
* @param text The text to trim
* @param length The maximum length
* @return The trimmed text
*/
public static String truncate( @Nonnull final String text, final int length ) {
return text.length( ) <= length ?
text :
text.substring( 0, length );
}
/**
* Get the substring of text that precedes the match, empty if not found.
*
* @param match The boundary string to search for
* @param text The text to process
* @return The substring, null if text was null
*/
public static String substringBefore( @Nonnull final String match,
@Nullable final String text ) {
if ( text != null ) {
final int index = text.indexOf( match );
if ( index < 0 ) {
return "";
} else {
return text.substring( 0, index );
}
} else {
return null;
}
}
/**
* Get a function returning the substring of text that precedes the match, empty if not found.
*
* @param match The boundary string to search for
* @return The substring function, returns null if text was null
*/
public static CompatFunction<String,String> substringBefore( @Nonnull final String match ) {
return new CompatFunction<String,String>( ) {
@Nullable
@Override
public String apply( @Nullable final String text ) {
return substringBefore( match, text );
}
};
}
/**
* Get the substring of text that follows the match, empty if not found.
*
* @param match The boundary string to search for
* @param text The text to process
* @return The substring, null if text was null
*/
public static String substringAfter( @Nonnull final String match,
@Nullable final String text ) {
if ( text != null ) {
final int index = text.indexOf( match );
if ( index < 0 ) {
return "";
} else {
return text.substring( index + match.length( ) );
}
} else {
return null;
}
}
/**
* Get a function returning the substring of text that follows the match, empty if not found.
*
* @param match The boundary string to search for
* @return The substring function, returns null if text was null
*/
public static CompatFunction<String,String> substringAfter( @Nonnull final String match ) {
return new CompatFunction<String,String>( ) {
@Nullable
@Override
public String apply( @Nullable final String text ) {
return substringAfter( match, text );
}
};
}
/**
* Get a Function for upper casing a String.
*
* <P>The returned function will pass through null values.</P>
*
* @return The upper casing function
* @see String#toUpperCase()
*/
public static CompatFunction<String,String> upper() {
return StringFunctions.UPPER;
}
/**
* Get a Function for lower casing a String.
*
* <P>The returned function will pass through null values.</P>
*
* @return The upper casing function
* @see String#toLowerCase() ()
*/
public static CompatFunction<String,String> lower() {
return StringFunctions.LOWER;
}
/**
* Get a Predicate for matching the start of a String.
*
* @param prefix The prefix to match
* @return The predicate
* @see String#startsWith(String)
*/
public static Predicate<String> startsWith( final String prefix ) {
return new Predicate<String>() {
@Override
public boolean apply( @Nullable final String text ) {
return text != null && text.startsWith( prefix );
}
};
}
/**
* Get a Predicate for matching the start of a String.
*
* @param text The text to perform a prefix match against
* @return The predicate
* @see String#startsWith(String)
*/
public static Predicate<String> isPrefixOf( final String text ) {
return text == null ?
Predicates.<String>alwaysFalse() :
new Predicate<String>() {
@Override
public boolean apply( @Nullable final String prefix ) {
return prefix != null && text.startsWith( prefix );
}
};
}
/**
* Get a Predicate for matching the end of a String.
*
* @param suffix The suffix to match
* @return The predicate
* @see String#endsWith(String)
*/
public static Predicate<String> endsWith( final String suffix ) {
return new Predicate<String>() {
@Override
public boolean apply( @Nullable final String text ) {
return text != null && text.endsWith( suffix );
}
};
}
/**
* Get a Predicate for matching the end of a String.
*
* @param text The text to perform a suffix match against
* @return The predicate
* @see String#endsWith(String)
*/
public static Predicate<String> isSuffixOf( final String text ) {
return text == null ?
Predicates.<String>alwaysFalse() :
new Predicate<String>() {
@Override
public boolean apply( @Nullable final String prefix ) {
return prefix != null && text.endsWith( prefix );
}
};
}
/**
* Get a Function that appends the given text to it's parameter.
*
* @param suffix The suffix to append.
* @return The function
*/
public static CompatFunction<String,String> append( final String suffix ) {
return new CompatFunction<String, String>( ) {
@Nullable
@Override
public String apply( @Nullable final String text ) {
return text == null ?
suffix :
text + suffix;
}
};
}
/**
* Get a Function that prepends the given text to it's parameter.
*
* @param prefix The prefix to prepend.
* @return The function
*/
public static CompatFunction<String,String> prepend( final String prefix ) {
return new CompatFunction<String, String>( ) {
@Nullable
@Override
public String apply( @Nullable final String text ) {
return text == null ?
prefix :
prefix + text;
}
};
}
public static CompatFunction<String,CompatFunction<String,String>> join( ) {
return new CompatFunction<String,CompatFunction<String,String>>( ) {
@Nullable
@Override
public CompatFunction<String,String> apply( @Nullable final String prefix ) {
return prepend( prefix );
}
};
}
/**
*
* @see java.util.regex.Matcher#quoteReplacement(String)
*/
public static CompatFunction<String,String> regexReplace(
final Pattern pattern,
final String replacement,
final String defaultValue
) {
return new CompatFunction<String,String>( ) {
@Nullable
@Override
public String apply( @Nullable final String text ) {
if ( text == null ) {
return defaultValue;
} else {
final Matcher matcher = pattern.matcher( text );
return matcher.matches( ) ?
matcher.replaceFirst( replacement ) :
defaultValue;
}
}
};
}
/**
* Convert an object to a string.
*
* <P>The returned function will pass through null values.</P>
*
* @return The toString function
* @see #toString(Object)
*/
public static CompatFunction<Object,String> toStringFunction() {
return StringerFunctions.TOSTRING;
}
/**
* Get a CharSequence that is the concatenation of the given sequences.
*
* @param sequences The sequences to concatenate
* @return The new sequence
*/
public static CharSequence concat( final Iterable<? extends CharSequence> sequences ) {
return concat( sequences, 0, length( sequences ) );
}
/**
* Get a CharSequence that is the concatenation of the given sequences.
*
* @param sequences The sequences to concatenate
* @param start The start of the sequence
* @param end The end of the sequence
* @return The new sequence
*/
public static CharSequence concat( final Iterable<? extends CharSequence> sequences, final int start, final int end ){
final int sequencesLength = end - start;
return new CharSequence( ) {
@Override
public int length( ) {
return sequencesLength;
}
@Override
public char charAt( final int index ) {
if ( index < 0 || index >= length( ) ) {
throw new IndexOutOfBoundsException( String.valueOf( index ) );
}
int adjustedIndex = start + index;
for ( final CharSequence sequence : sequences ) {
if ( adjustedIndex < sequence.length( ) ) {
return sequence.charAt( adjustedIndex );
} else {
adjustedIndex -= sequence.length( );
}
}
throw new IndexOutOfBoundsException( String.valueOf( index ) );
}
@Override
public CharSequence subSequence( final int start, final int end ) {
return concat( Collections.singleton( this ), start, end );
}
@Nonnull
@Override
public String toString( ) {
return new StringBuilder( this ).toString( );
}
};
}
private static int length( final Iterable<? extends CharSequence> sequences ) {
int length = 0;
for ( final CharSequence sequence : sequences ) {
length += sequence.length( );
}
return length;
}
private enum StringFunctions implements CompatFunction<String,String> {
LOWER {
@Override
public String apply( final String text ) {
return text == null ? null : text.toLowerCase();
}
},
UPPER {
@Override
public String apply( final String text ) {
return text == null ? null : text.toUpperCase();
}
},
TRIM {
@Override
public String apply( final String text ) {
return text == null ? null : text.trim();
}
}
}
private enum StringerFunctions implements CompatFunction<Object,String> {
TOSTRING {
@Override
public String apply( final Object object ) {
return Strings.toString( object );
}
}
}
}