com.numdata.oss.TextTools Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of numdata-commons Show documentation
Show all versions of numdata-commons Show documentation
Miscellaneous basic Java tools.
/*
* Copyright (c) 2017, Numdata BV, The Netherlands.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Numdata nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NUMDATA BV BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.numdata.oss;
import java.io.*;
import java.math.*;
import java.net.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;
import org.jetbrains.annotations.*;
/**
* This class is a text manipulation tool box.
*
* @author Sjoerd Bouwman
* @author Peter S. Heijnen
*/
public class TextTools
{
/**
* State used for whitespace by {@link #plainTextToHTML} method.
*
* @see #plainTextToHTML
*/
private static final int ASCII_HTML_SPACE = 0;
/**
* State used for body text by {@link #plainTextToHTML} method.
*
* @see #plainTextToHTML
*/
private static final int ASCII_HTML_TEXT = 1;
/**
* State used for HTML tags by {@link #plainTextToHTML} method.
*
* @see #plainTextToHTML
*/
private static final int ASCII_HTML_TAG = 2;
/**
* Digit characters used by {@link #escape} and {@link #toHexString}.
*
* @see #escape
* @see #toHexString
*/
private static final char[] UPPER_DIGITS =
{ '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
* Digit characters used by {@link #escape} and {@link #toHexString}.
*
* @see #escape
* @see #toHexString
*/
private static final char[] LOWER_DIGITS =
{ '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
* This implementation of {@link Format} produces no output. It may be used
* to selectively disable components of a {@link MessageFormat}.
*/
public static final Format EMPTY_FORMAT = new Format()
{
@Override
public StringBuffer format( final Object obj, @NotNull final StringBuffer toAppendTo, @NotNull final FieldPosition pos )
{
return toAppendTo;
}
@Override
public Object parseObject( final String source, @NotNull final ParsePosition pos )
{
return source;
}
};
/**
* Binary prefixes according to IEC 60027-2 A.2 and ISO/IEC 80000 standards
* (index = base 1024 exponent).
*/
private static final String[] IEC_BINARY_PREFIXES = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" };
/**
* Pattern describing which email addresses are valid.
*/
private static final Pattern VALID_EMAIL_ADDRESS_PATTERN = Pattern.compile( "[A-Za-z0-9._%+-]+@[A-Za-z0-9][A-Za-z0-9.-]+[A-Za-z]" );
/**
* Characters that are illegal for any position in a file name.
*/
public static final String ILLEGAL_FILENAME_CHARACTERS = "#%&$<>{}*?!/\\'`\":;@+|=";
/**
* Characters that are illegal as start of a file name.
*/
public static final String ILLEGAL_FILENAME_START_CHARACTERS = ILLEGAL_FILENAME_CHARACTERS + "-_.";
/**
* Maximum number of characters in a file name.
*/
public static final int MAXIMUM_FILENAME_LENGTH = 255;
/**
* Platform-specific line separator.
*/
@SuppressWarnings( "StaticNonFinalField" )
private static String lineSeparator = null;
/**
* Utility class is not supposed to be instantiated.
*/
private TextTools()
{
}
/**
* Appends a fixed-length string to an {@link Appendable}. Either fills the
* missing spaces with the specified character, or truncates the string.
*
* @param appendable {@link Appendable} destination.
* @param source String to fix.
* @param length Length of the string to return.
* @param rightAligned Align text to right of resulting string.
* @param fillChar Character to fill with.
*
* @throws IOException from {@link Appendable#append}.
*/
public static void appendFixed( @NotNull final Appendable appendable, @Nullable final CharSequence source, final int length, final boolean rightAligned, final char fillChar )
throws IOException
{
if ( length > 0 )
{
if ( ( source == null ) || ( source.length() == 0 ) )
{
for ( int i = 0; i < length; i++ )
{
appendable.append( fillChar );
}
}
else if ( source.length() < length )
{
if ( !rightAligned )
{
appendable.append( source );
}
for ( int i = source.length(); i < length; i++ )
{
appendable.append( fillChar );
}
if ( rightAligned )
{
appendable.append( source );
}
}
else
{
for ( int i = 0; i < length; i++ )
{
appendable.append( source.charAt( i ) );
}
}
}
}
/**
* Appends a fixed-length string to a {@link StringBuffer}. Either fills the
* missing spaces with spaces, or truncates the string.
*
* @param sb {@link StringBuffer} used as destination.
* @param source String to fix.
* @param length Length of the string to return.
*/
public static void appendFixed( @NotNull final StringBuffer sb, @Nullable final CharSequence source, final int length )
{
appendFixed( sb, source, length, false, ' ' );
}
/**
* Appends a fixed-length string to a {@link StringBuffer}. Either fills the
* missing spaces with the specified character, or truncates the string.
*
* @param sb {@link StringBuffer} used as destination.
* @param source String to fix.
* @param length Length of the string to return.
* @param rightAligned Align text to right of resulting string.
* @param fillChar Character to fill with.
*/
public static void appendFixed( @NotNull final StringBuffer sb, @Nullable final CharSequence source, final int length, final boolean rightAligned, final char fillChar )
{
try
{
appendFixed( (Appendable)sb, source, length, rightAligned, fillChar );
}
catch ( final IOException e )
{
/* impossible */
}
}
/**
* Appends a fixed-length string to a {@link StringBuilder}. Either fills
* the missing spaces with spaces, or truncates the string.
*
* @param sb {@link StringBuilder} used as destination.
* @param source String to fix.
* @param length Length of the string to return.
*/
public static void appendFixed( @NotNull final StringBuilder sb, @Nullable final CharSequence source, final int length )
{
appendFixed( sb, source, length, false, ' ' );
}
/**
* Appends a fixed-length string to a {@link StringBuilder}. Either fills
* the missing spaces with the specified character, or truncates the
* string.
*
* @param sb {@link StringBuilder} used as destination.
* @param source String to fix.
* @param length Length of the string to return.
* @param rightAligned Align text to right of resulting string.
* @param fillChar Character to fill with.
*/
public static void appendFixed( @NotNull final StringBuilder sb, @Nullable final CharSequence source, final int length, final boolean rightAligned, final char fillChar )
{
try
{
appendFixed( (Appendable)sb, source, length, rightAligned, fillChar );
}
catch ( final IOException e )
{
/* impossible */
}
}
/**
* Appends spaces to a {@link StringBuffer}.
*
* @param buffer {@link StringBuffer} used as destination.
* @param length Length of the string to return.
*/
public static void appendSpace( @NotNull final StringBuffer buffer, final int length )
{
appendFixed( buffer, null, length, false, ' ' );
}
/**
* Appends spaces to a {@link StringBuilder}.
*
* @param buffer {@link StringBuilder} used as destination.
* @param length Length of the string to return.
*/
public static void appendSpace( @NotNull final StringBuilder buffer, final int length )
{
appendFixed( buffer, null, length, false, ' ' );
}
/**
* Returns a fixed-length string filled with the specified character.
*
* @param length Length of the string to return.
* @param fillChar Character to fill with.
*
* @return Fixed-length {@link String}.
*/
public static String getFixed( final int length, final char fillChar )
{
return getFixed( null, length, false, fillChar );
}
/**
* Returns a fixed-length string. Either fills the missing spaces with the
* specified character, or truncates the source string.
*
* @param source String to fix.
* @param length Length of the string to return.
* @param rightAligned Align text to right of resulting string.
* @param fillChar Character to fill with.
*
* @return Fixed-length {@link String}.
*/
@NotNull
public static String getFixed( @Nullable final String source, final int length, final boolean rightAligned, final char fillChar )
{
final String result;
if ( ( source != null ) && ( source.length() >= length ) )
{
result = source.substring( 0, length );
}
else // source is absent or too short => need to add fill chars
{
final char[] chars = new char[ length ];
int pos = 0;
if ( source != null )
{
final int len = source.length();
if ( rightAligned )
{
while ( pos < ( length - len ) )
{
chars[ pos++ ] = fillChar;
}
}
for ( int i = 0; i < len; i++ )
{
chars[ pos++ ] = source.charAt( i );
}
}
while ( pos < length )
{
chars[ pos++ ] = fillChar;
}
result = new String( chars );
}
return result;
}
/**
* Trim whitespace from {@link StringBuffer}.
*
* @param sb {@link StringBuffer} to trim.
*
* @return {@code sb}.
*/
@NotNull
public static StringBuffer trim( @NotNull final StringBuffer sb )
{
final int length = sb.length();
int start = 0;
while ( ( start < length ) && Character.isWhitespace( sb.charAt( start ) ) )
{
start++;
}
int end = length;
while ( ( end > start ) && Character.isWhitespace( sb.charAt( end - 1 ) ) )
{
end--;
}
if ( start > 0 )
{
sb.delete( 0, start );
}
if ( end < length )
{
sb.setLength( end - start );
}
return sb;
}
/**
* Get trimmed sub-sequence of a character sequence.
*
* @param source Char sequence to get sub-sequence of.
* @param start Start index of sub-sequence (inclusive).
* @param end End index of sub-sequence (exclusive).
*
* @return Trimmed sub-sequence.
*/
@NotNull
public static CharSequence getTrimmedSubsequence( @NotNull final CharSequence source, final int start, final int end )
{
int trimmedStart = start;
int trimmedEnd = end;
if ( ( trimmedStart >= 0 ) && ( trimmedStart < trimmedEnd ) && ( trimmedEnd <= source.length() ) )
{
while ( ( trimmedStart < trimmedEnd ) && Character.isWhitespace( source.charAt( trimmedStart ) ) )
{
trimmedStart++;
}
while ( ( trimmedStart < trimmedEnd ) && Character.isWhitespace( source.charAt( trimmedEnd - 1 ) ) )
{
trimmedEnd--;
}
}
return source.subSequence( trimmedStart, trimmedEnd );
}
/**
* Get trimmed substring.
*
* @param source String to get substring of.
* @param start Start index of substring (inclusive).
* @param end End index of substring (exclusive).
*
* @return Trimmed substring.
*/
public static String getTrimmedSubstring( @NotNull final CharSequence source, final int start, final int end )
{
return (String)getTrimmedSubsequence( source, start, end );
}
/**
* Convert a plain text string to HTML formatted string. This is most useful
* for use in Swing tool tips (this allows you to use multi-line tool tips,
* for example).
*
* @param string Plain text string to convert.
*
* @return HTML-formatted text (includes '{@code <html>}' tag); {@code
* null} if {@code string} is {@code null}.
*/
@Nullable
@Contract( value = "null -> null; !null -> !null", pure = true )
public static String plainTextToHTML( @Nullable final String string )
{
final String result;
if ( ( string != null ) && !string.isEmpty() && !string.matches( "" ) )
{
final int len = string.length();
final StringBuilder resultBuffer = new StringBuilder( len + 14 );
final StringBuilder tokenBuffer = new StringBuilder( Math.min( 40, len / 2 ) );
resultBuffer.append( "" );
int state = ASCII_HTML_SPACE;
boolean inPreformattedSection = false;
for ( int pos = 0; pos < len; pos++ )
{
final char ch = string.charAt( pos );
switch ( state )
{
case ASCII_HTML_SPACE:
//noinspection fallthrough
case ASCII_HTML_TEXT:
if ( ch == '<' )
{
appendHtmlText( resultBuffer, tokenBuffer, inPreformattedSection );
tokenBuffer.setLength( 0 );
tokenBuffer.append( '<' );
state = ASCII_HTML_TAG;
}
else if ( !Character.isWhitespace( ch ) )
{
tokenBuffer.append( ch );
state = ASCII_HTML_TEXT;
}
else if ( state == ASCII_HTML_TEXT )
{
if ( inPreformattedSection )
{
tokenBuffer.append( ch );
}
else
{
tokenBuffer.append( ' ' );
state = ASCII_HTML_SPACE;
}
}
break;
case ASCII_HTML_TAG:
if ( ch == '>' )
{
tokenBuffer.append( '>' );
if ( inPreformattedSection )
{
if ( equals( "
", tokenBuffer ) )
{
inPreformattedSection = false;
resultBuffer.append( tokenBuffer );
tokenBuffer.setLength( 0 );
state = ASCII_HTML_TEXT;
}
else
{
state = ASCII_HTML_TEXT;
}
}
else
{
final int tokenLength = tokenBuffer.length();
if ( equals( "", tokenBuffer ) ) { inPreformattedSection = true; } else if ( tokenBuffer.charAt( tokenLength - 2 ) == '/' ) { tokenBuffer.delete( tokenLength - 2, tokenLength - 1 ); } resultBuffer.append( tokenBuffer ); tokenBuffer.setLength( 0 ); state = ASCII_HTML_TEXT; } } else if ( !Character.isWhitespace( ch ) ) { tokenBuffer.append( ch ); } break; } } appendHtmlText( resultBuffer, tokenBuffer, inPreformattedSection ); // if ( inPreFormattedSection ) // resultBuffer.append( "" ); // resultBuffer.append( "" ); result = resultBuffer.toString(); } else { result = string; } return result; } @SuppressWarnings( "JavaDoc" ) private static void appendHtmlText( final StringBuilder result, final StringBuilder tokenBuffer, final boolean inPreformattedSection ) { if ( !inPreformattedSection ) { while ( ( tokenBuffer.length() > 0 ) && Character.isWhitespace( tokenBuffer.charAt( tokenBuffer.length() - 1 ) ) ) { tokenBuffer.setLength( tokenBuffer.length() - 1 ); } while ( tokenBuffer.length() > 66 ) { final int breakAt = tokenBuffer.lastIndexOf( " ", 66 ); if ( ( breakAt < 0 ) || ( tokenBuffer.indexOf( " ", breakAt + 1 ) < 0 ) ) { break; } result.ensureCapacity( breakAt + 4 ); for ( int i = 0; i < breakAt; i++ ) { result.append( tokenBuffer.charAt( i ) ); } result.append( "
" ); tokenBuffer.delete( 0, breakAt + 1 ); } result.append( tokenBuffer ); } else { final int len = tokenBuffer.length(); result.ensureCapacity( len ); for ( int i = 0; i < len; i++ ) { final char c = tokenBuffer.charAt( i ); switch ( c ) { case '<': result.append( "<" ); break; case '>': result.append( ">" ); break; default: result.append( c ); } } } } /** * Compare if two {@link String}s are equal. Both arguments may be {@code * null}, and {@code null} is always less than non-{@code null}. * * @param s1 First string to compare. * @param s2 Second string to compare. * * @return {@code 0} if the strings both {@code null} or equal; a value less * than {@code 0} if {@code string1} is {@code null} or lexicographically * less than {@code string2}; and a value greater than {@code 0} if {@code * string1} is lexicographically greater than {@code string2} or {@code * string2} is {@code null}. */ @SuppressWarnings( "TypeMayBeWeakened" ) public static int compare( @Nullable final String s1, @Nullable final String s2 ) { return ( s1 == null ) ? ( s2 != null ) ? -1 : 0 : ( s2 != null ) ? s1.compareTo( s2 ) : 1; } /** * Test if two {@link CharSequence}s are equal. Both arguments may be {@code * null}. This will return {@code true} if both objects are {@code null} or * if both character sequences have the same content. * * @param cs1 First {@link CharSequence} (may be {@code null}). * @param cs2 Second {@link CharSequence} (may be {@code null}). * * @return {@code true} if the string objects are equal; {@code false} * otherwise. */ @Contract( value = "null, null -> true", pure = true ) public static boolean equals( @Nullable final CharSequence cs1, @Nullable final CharSequence cs2 ) { @SuppressWarnings( "ObjectEquality" ) boolean result = ( cs1 == cs2 ); if ( !result && ( cs1 != null ) && ( cs2 != null ) ) { int pos = cs1.length(); result = ( pos == cs2.length() ); while ( result && ( --pos >= 0 ) ) { result = ( cs2.charAt( pos ) == cs1.charAt( pos ) ); } } return result; } /** * Test if two {@link CharSequence}s are equal. Both arguments may be {@code * null}. This will return {@code true} if both objects are {@code null} or * if both character sequences have the same content. * * @param cs1 First {@link CharSequence} (may be {@code null}). * @param cs2 Second {@link CharSequence} (may be {@code null}). * * @return {@code true} if the string objects are equal; {@code false} * otherwise. */ @Contract( value = "null, null -> true", pure = true ) public static boolean equalsIgnoreCase( @Nullable final CharSequence cs1, @Nullable final CharSequence cs2 ) { @SuppressWarnings( "ObjectEquality" ) boolean result = ( cs1 == cs2 ); if ( !result && ( cs1 != null ) && ( cs2 != null ) ) { int pos = cs1.length(); result = ( pos == cs2.length() ); while ( result && ( --pos >= 0 ) ) { final char c1 = cs1.charAt( pos ); final char c2 = cs2.charAt( pos ); /* Read {@link String#equalsIgnoreCase} implementation for reason why we test lower AND upper case */ result = ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 ) ) && ( Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) ); } } return result; } /** * This method tests whether the specified string is {@code null}, is zero * length, or consists only of whitespace characters (as classified per * {@link Character#isWhitespace(char)}. * * @param string String to examine. * * @return {@code true} if the string is empty; {@code false} otherwise. */ @Contract( value = "null -> true", pure = true ) public static boolean isEmpty( @Nullable final CharSequence string ) { boolean result = ( string == null ); if ( !result ) { result = true; for ( int i = string.length(); --i >= 0; ) { if ( !Character.isWhitespace( string.charAt( i ) ) ) { result = false; break; } } } return result; } /** * This method tests whether the specified string is not {@code null}, has a * length larger than {@code 0}, and does not only contain whitespace * characters (identified by {@link Character#isWhitespace(char)}. * * @param string String to examine. * * @return {@code true} if the string is not empty; {@code false} if the * string is {@code null} or contains no non-whitespace characters. */ @Contract( value = "null -> false", pure = true ) public static boolean isNonEmpty( @Nullable final CharSequence string ) { return !isEmpty( string ); } /** * Tests if the specified string starts with the specified prefix * character. * * @param string String to examine. * @param prefix Prefix character to test for. * * @return {@code true} if the string starts with the specified prefix; * {@code false} if the string is {@code null} or does not start with the * specified prefix. */ @Contract( value = "null, _ -> false", pure = true ) public static boolean startsWith( @Nullable final CharSequence string, final char prefix ) { boolean result = ( string != null ); if ( result ) { final int length = string.length(); result = ( length > 0 ) && ( string.charAt( 0 ) == prefix ); } return result; } /** * Tests if the specified string starts with the specified prefix ignoring * character case. * * @param string String to examine. * @param prefix Prefix to test for. * * @return {@code true} if the string starts with the specified prefix; * {@code false} if either argument is {@code null} or the {@code string} * does not start with the specified prefix. */ @Contract( value = "null, _ -> false; _, null -> false", pure = true ) public static boolean startsWithIgnoreCase( @Nullable final String string, @Nullable final String prefix ) { return ( string != null ) && ( prefix != null ) && string.regionMatches( true, 0, prefix, 0, prefix.length() ); } /** * Tests if the specified string ends with the specified suffix character. * * @param string String to examine. * @param suffix Suffix character to test for. * * @return {@code true} if the string ends with the specified suffix; {@code * false} if the string is {@code null} or does not end with the specified * suffix. */ @Contract( value = "null, _ -> false", pure = true ) public static boolean endsWith( @Nullable final CharSequence string, final char suffix ) { boolean result = ( string != null ); if ( result ) { final int length = string.length(); result = ( length > 0 ) && ( string.charAt( length - 1 ) == suffix ); } return result; } /** * Tests if the specified string ends with the specified suffix ignoring * character case. * * @param string String to examine. * @param suffix Suffix to test for. * * @return {@code true} if the string ends with the specified suffix; {@code * false} if either argument is {@code null} or the {@code string} does not * end with the specified suffix. */ @Contract( value = "null, _ -> false; _, null -> false", pure = true ) public static boolean endsWithIgnoreCase( @Nullable final String string, @Nullable final String suffix ) { return ( string != null ) && ( suffix != null ) && string.regionMatches( true, string.length() - suffix.length(), suffix, 0, suffix.length() ); } /** * Add escapes to a string (e.g. to use in SQL queries) and appends the * result to the specified string buffer. * * This method escapes characters for use in a string or character * literal, using the syntax defined in the Java Language Specification. See * {@link #escape(Appendable, char)} for details. * * @param buffer {@link Appendable} destination. * @param source String to escape. * * @throws IOException from {@link Appendable#append}. * @see #unescape */ public static void escape( @NotNull final Appendable buffer, @NotNull final CharSequence source ) throws IOException { final int len = source.length(); for ( int index = 0; index < len; index++ ) { escape( buffer, source.charAt( index ) ); } } /** * Append character to an {@link Appendable}. Escape codes are added when * necessary. * * This method escapes characters for use in a string or character * literal, using the syntax defined in the Java Language Specification. The * following characters are escape: ISO control characters, characters not * in ISO-8859-1, single quote {@code '\''}, double quote {@code '"'} and * backslash {@code '\\'}. * * @param buffer {@link Appendable} destination. * @param ch Character to append. * * @throws IOException from {@link Appendable#append}. */ public static void escape( @NotNull final Appendable buffer, final char ch ) throws IOException { switch ( ch ) { case '\0': buffer.append( '\\' ); buffer.append( '0' ); break; case '\b': buffer.append( '\\' ); buffer.append( 'b' ); break; case '\f': buffer.append( '\\' ); buffer.append( 'f' ); break; case '\n': buffer.append( '\\' ); buffer.append( 'n' ); break; case '\r': buffer.append( '\\' ); buffer.append( 'r' ); break; case '\t': buffer.append( '\\' ); buffer.append( 't' ); break; case '\\': //noinspection fallthrough case '\'': //noinspection fallthrough case '"': buffer.append( '\\' ); buffer.append( ch ); break; default: if ( ch > '\u00FF' || Character.isISOControl( ch ) ) { final char[] upperDigits = UPPER_DIGITS; buffer.append( '\\' ); buffer.append( 'u' ); buffer.append( upperDigits[ ( ch >> 12 ) & 0x0F ] ); buffer.append( upperDigits[ ( ch >> 8 ) & 0x0F ] ); buffer.append( upperDigits[ ( ch >> 4 ) & 0x0F ] ); buffer.append( upperDigits[ (int)ch & 0x0F ] ); } else { buffer.append( ch ); } } } /** * Add escapes to a string (e.g. to use in SQL queries) and appends the * result to the specified string buffer. * * @param sb String buffer destination. * @param source String to escape. * * @see #unescape */ public static void escape( @NotNull final StringBuffer sb, @NotNull final CharSequence source ) { final int len = source.length(); for ( int index = 0; index < len; index++ ) { escape( sb, source.charAt( index ) ); } } /** * Append character to string. Escape codes are added when necessary. * * This method escapes characters for use in a string or character * literal, using the syntax defined in the Java Language Specification. See * {@link #escape(Appendable, char)} for details. * * @param sb String buffer destination. * @param c Character to append. */ public static void escape( @NotNull final StringBuffer sb, final char c ) { try { escape( (Appendable)sb, c ); } catch ( final Exception e ) { /* impossible */ } } /** * Add escapes to a string (e.g. to use in SQL queries) and appends the * result to the specified string builder. * * This method escapes characters for use in a string or character * literal, using the syntax defined in the Java Language Specification. See * {@link #escape(Appendable, char)} for details. * * @param sb String builder destination. * @param source String to escape. * * @see #unescape */ public static void escape( @NotNull final StringBuilder sb, @NotNull final CharSequence source ) { final int len = source.length(); for ( int index = 0; index < len; index++ ) { escape( sb, source.charAt( index ) ); } } /** * Append character to string. Escape codes are added when necessary. * * This method escapes characters for use in a string or character * literal, using the syntax defined in the Java Language Specification. See * {@link #escape(Appendable, char)} for details. * * @param sb String builder destination. * @param ch Character to append. */ public static void escape( @NotNull final StringBuilder sb, final char ch ) { try { escape( (Appendable)sb, ch ); } catch ( final Exception e ) { /* impossible */ } } /** * Get localized currency format. * * @param locale Locale to get currency format of. * @param symbol Currency symbol to use. * * @return {@link NumberFormat} representing the localized currency format. */ public static DecimalFormat getCurrencyFormat( @NotNull final Locale locale, @NotNull final String symbol ) { final DecimalFormat format = (DecimalFormat)NumberFormat.getCurrencyInstance( locale ); format.setRoundingMode( RoundingMode.HALF_UP ); final DecimalFormatSymbols symbols = format.getDecimalFormatSymbols(); symbols.setCurrencySymbol( symbol ); symbols.setInternationalCurrencySymbol( symbol ); format.setDecimalFormatSymbols( symbols ); return format; } /** * Get localized currency format. * * @param locale Locale to get currency format of. * @param currency Currency to use. * * @return {@link NumberFormat} representing the localized currency format. */ public static DecimalFormat getCurrencyFormat( @NotNull final Locale locale, @NotNull final Currency currency ) { final DecimalFormat result = (DecimalFormat)NumberFormat.getCurrencyInstance( locale ); result.setRoundingMode( RoundingMode.HALF_UP ); result.setCurrency( currency ); final int fractionDigits = currency.getDefaultFractionDigits(); if ( fractionDigits >= 0 ) { result.setMinimumFractionDigits( fractionDigits ); result.setMaximumFractionDigits( fractionDigits ); } return result; } /** * Get localized date (not including time) format. * * @param locale Locale to use for localized formatting. * * @return {@link DateFormat} representing the localized date format. */ public static SimpleDateFormat getDateFormat( @NotNull final Locale locale ) { return (SimpleDateFormat)DateFormat.getDateInstance( DateFormat.SHORT, locale ); } /** * Get localized date/time format. * * @param locale Locale to use for localized formatting. * * @return {@link DateFormat} representing the localized date/time format. */ public static SimpleDateFormat getDateTimeFormat( @NotNull final Locale locale ) { return (SimpleDateFormat)DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT, locale ); } /** * Get line separator for the current platform. * * @return Platform-specific line separator. */ public static String getLineSeparator() { String result = lineSeparator; if ( result == null ) { final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter( sw ); pw.println(); result = sw.toString(); lineSeparator = result; } return result; } /** * Get string with list of objects separated by the specified separator * character. {@code null}-elements are removed from the result. * * @param list List/array of objects. * @param separator Separator character between objects. * * @return String with list of objects (may be empty, never {@code null}). */ @NotNull public static String getList( @Nullable final Iterable> list, final char separator ) { StringBuilder sb = null; String first = null; if ( list != null ) { for ( final Object object : list ) { if ( object != null ) { final String string = String.valueOf( object ); if ( sb == null ) { if ( first == null ) { first = string; } else { sb = new StringBuilder( first.length() + 1 + string.length() ); sb.append( first ); sb.append( separator ); sb.append( string ); } } else { sb.ensureCapacity( sb.length() + 1 + string.length() ); sb.append( separator ); sb.append( string ); } } } } return ( sb != null ) ? sb.toString() : ( first != null ) ? first : ""; } /** * Get string with list of objects separated by the specified separator. * {@code null}-elements are removed from the result. * * @param list List/array of objects. * @param separator Separator character between objects. * * @return String with list of objects (may be empty, never {@code null}). */ @NotNull public static String getList( @Nullable final Iterable> list, @NotNull final CharSequence separator ) { StringBuilder sb = null; String first = null; if ( list != null ) { for ( final Object object : list ) { if ( object != null ) { final String string = String.valueOf( object ); if ( sb == null ) { if ( first == null ) { first = string; } else { sb = new StringBuilder( first.length() + separator.length() + string.length() ); sb.append( first ); sb.append( separator ); sb.append( string ); } } else { sb.ensureCapacity( sb.length() + separator.length() + string.length() ); sb.append( separator ); sb.append( string ); } } } } return ( sb != null ) ? sb.toString() : ( first != null ) ? first : ""; } /** * Get list of value names for the given enumeration type. * * @param enumClass Enumeration type to get names of. * * @return Names of enumeration values. */ @NotNull public static List
-
*
*
- Has an acceptable length (1 to 255) * *
- Contains no ISO control characters (see {@link * Character#isISOControl}) * *
- Contains no characters with a known possible special meaning:
*
*
* *
* *! exclamation mark * *" quotation mark * *# number sign * *$ dollar sign * *% percent sign * *& ampersand * *' apostrophe * ** asterisk * *+ plus sign * */ slash * *: colon * *; semicolon * *< less-than * *= equals to * *> greater-than * *? question mark * *@ at sign * *\ backslash * *` grave accent * *{ left curly brace * *| vertical bar * *} right curly brace
*
* - Does not start with a characters with a known possible special
* meaning:
*
*
* *
* *- hyphen * *. period * *_ underscore
*
* - Does not start or end with whitespace * *
* 00000: 04 d2 29 00 00 01 00 00 00 00 00 01 20 45 47 |..)......... EG| * 00010: 43 45 46 45 45 43 41 43 41 43 41 43 41 43 41 |CEFEECACACACACA| * 00020: 41 43 41 43 41 43 41 43 41 43 41 41 44 00 00 |ACACACACACAAD..| * 00030: 00 01 c0 0c 00 20 00 01 00 00 00 00 00 06 20 |..... ........ | * 00040: ac 22 22 e1 |."". | ** * @param out Target character stream. * @param src Data to dump. * @param srcIndex Index of first byte to dump. * @param length Number of bytes to dump. * * @throws IOException if an error occurs while accessing resources. */ @SuppressWarnings( "SpellCheckingInspection" ) public static void hexdump( @NotNull final Appendable out, @NotNull final byte[] src, final int srcIndex, final int length ) throws IOException { for ( int pointer = 0; pointer < length; pointer += 16 ) { appendHexString( out, pointer, 5, true ); out.append( ": " ); final int rowLength = Math.min( 16, length - pointer ); for ( int i = 0; i < 16; i++ ) { if ( i < rowLength ) { appendHexString( out, src[ srcIndex + pointer + i ], 2, true ); out.append( ' ' ); } else { out.append( " " ); } } out.append( " |" ); for ( int i = 0; i < 16; i++ ) { if ( i < rowLength ) { final byte b = src[ srcIndex + pointer + i ]; out.append( ( b < 31 ) ? '.' : (char)b ); } else { out.append( ' ' ); } } out.append( "|\n" ); } } /** * Get duration string. * * @param millis Number of milliseconds. * @param seconds Whether to include seconds in the result. * @param milliseconds Whether to include milliseconds in the result. * * @return Duration string. */ @NotNull public static String getDurationString( final long millis, final boolean seconds, final boolean milliseconds ) { final String result; if ( milliseconds ) { final int totalSeconds = (int)( millis / 1000L ); final int hours = totalSeconds / 3600; final int min = ( totalSeconds / 60 ) % 60; final int sec = totalSeconds % 60; result = hours + ":" + ( min / 10 ) + ( min % 10 ) + ':' + ( sec / 10 ) + ( sec % 10 ) + '.' + ( millis % 1000 ); } else if ( seconds ) { final int totalSeconds = Math.round( millis / 1000.0f ); final int hours = totalSeconds / 3600; final int min = ( totalSeconds / 60 ) % 60; final int sec = totalSeconds % 60; result = hours + ":" + ( min / 10 ) + ( min % 10 ) + ':' + ( sec / 10 ) + ( sec % 10 ); } else { final int totalMinutes = Math.round( millis / 60000.0f ); final int hours = totalMinutes / 60; final int min = totalMinutes % 60; result = hours + ":" + ( min / 10 ) + ( min % 10 ); } return result; } }
© 2015 - 2024 Weber Informatics LLC | Privacy Policy