/* * Copyright (c) 2013-2014 the original author or authors * * Licensed 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 io.werval.util; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.io.UncheckedIOException; import java.util.Arrays; import java.util.Iterator; import static java.util.Objects.requireNonNull; import static io.werval.util.IllegalArguments.ensureGreaterOrEqual; /** * Utilities to work with Strings. */ public final class Strings { /** * Empty string. */ public static final String EMPTY = ""; /** * Space. */ public static final String SPACE = " "; /** * Tabulation. */ public static final String TAB = "\t"; /** * New line. */ public static final String NEWLINE = "\n"; /** * …. */ public static final String ETC = "…"; /** * Empty {@literal char} array. */ public static final char[] EMPTY_CHAR_ARRAY = EMPTY.toCharArray(); /** * Check if a String is null or empty. * * @param value String * * @return TRUE if the String is null or empty, otherwise return FALSE */ public static boolean isEmpty( String value ) { return value == null || value.length() == 0; } /** * Check if a String is not null nor empty. * * @param value String * * @return TRUE if the String is not null nor empty, otherwise return FALSE */ public static boolean hasText( String value ) { return value != null && value.length() > 0; } /** * Null a String if it is empty. * * @param value String * * @return {@literal null} if the String is {@literal null} or empty, the original String otherwise */ public static String hasTextOrNull( String value ) { return value == null || value.length() == 0 ? null : value; } /** * Join strings. * * @param strings Strings to join * * @return Joined String */ public static String join( String[] strings ) { return join( Arrays.asList( strings ) ); } /** * Join strings with a given delimiter. * * @param strings Strings to join * @param delimiter Delimiter * * @return Joined String */ public static String join( String[] strings, String delimiter ) { return join( Arrays.asList( strings ), delimiter ); } /** * Join strings. * * @param strings Strings to join * * @return Joined String */ public static String join( Iterable<? extends CharSequence> strings ) { return join( strings, EMPTY ); } /** * Join strings with a given delimiter. * * @param strings Strings to join * @param delimiter Delimiter * * @return Joined String */ public static String join( Iterable<? extends CharSequence> strings, String delimiter ) { int capacity = 0; int delimLength = delimiter.length(); Iterator<? extends CharSequence> iter = strings.iterator(); if( iter.hasNext() ) { capacity += iter.next().length() + delimLength; } StringBuilder buffer = new StringBuilder( capacity ); iter = strings.iterator(); if( iter.hasNext() ) { buffer.append( iter.next() ); while( iter.hasNext() ) { buffer.append( delimiter ); buffer.append( iter.next() ); } } return buffer.toString(); } /** * Right pad a given string with spaces. * * If the given String length is greater than {@literal length}, the String is returned as is. * * @param length Total length of the padded String * @param string String to pad * * @return Right padded String * * @throws IllegalArgumentException if {@literal length} is negative */ public static String rightPad( int length, String string ) { return rightPad( length, string, ' ' ); } /** * Right pad a given string. * * If the given String length is greater than {@literal length}, the String is returned as is. * * @param length Total length of the padded String * @param string String to pad * @param pad Character to use for padding * * @return Right padded String * * @throws IllegalArgumentException if {@literal length} is negative */ public static String rightPad( int length, String string, char pad ) { ensureGreaterOrEqual( "length", length, 0 ); if( isEmpty( string ) ) { return repeat( pad, length ); } if( string.length() > length ) { return string; } return string + repeat( pad, length - string.length() ); } /** * Left pad a given string with spaces. * * If the given String length is greater than {@literal length}, the String is returned as is. * * @param length Total length of the padded String * @param string String to pad * * @return Left padded String * * @throws IllegalArgumentException if {@literal length} is negative */ public static String leftPad( int length, String string ) { return leftPad( length, string, ' ' ); } /** * Left pad a given string. * * If the given String length is greater than {@literal length}, the String is returned as is. * * @param length Total length of the padded String * @param string String to pad * @param pad Character to use for padding * * @return Left padded String * * @throws IllegalArgumentException if {@literal length} is negative */ public static String leftPad( int length, String string, char pad ) { ensureGreaterOrEqual( "length", length, 0 ); if( isEmpty( string ) ) { return repeat( pad, length ); } if( string.length() > length ) { return string; } return repeat( pad, length - string.length() ) + string; } /** * Repeat a character. * * @param character Character to repeat * @param times Times to repeat * * @return A String of {@literal times} length that contains only {@literal character}. */ public static String repeat( char character, int times ) { StringBuilder sb = new StringBuilder(); for( int index = 0; index < times; index++ ) { sb.append( character ); } return sb.toString(); } /** * Repeat a String. * * @param characters Characters to repeat * @param times Times to repeat * * @return A String of {@literal characters.length()*times} length that contains repeated {@literal characters}. */ public static String repeat( CharSequence characters, int times ) { StringBuilder sb = new StringBuilder(); for( int index = 0; index < times; index++ ) { sb.append( characters ); } return sb.toString(); } /** * Indent a String with two spaces levels. * * @param input String to indent * @param level Indent levels count * * @return Indented String */ public static String indentTwoSpaces( String input, int level ) { try { return indentTwoSpaces( new StringReader( input ), level ); } catch( IOException ex ) { throw new UncheckedIOException( ex ); } } /** * Indent a String with tabulation levels. * * @param input String to indent * @param level Indent levels count * * @return Indented String */ public static String indentTab( String input, int level ) { try { return indentTab( new StringReader( input ), level ); } catch( IOException ex ) { throw new UncheckedIOException( ex ); } } /** * Indent a String. * * @param input String to indent * @param level Indent levels count * @param tab Level String * * @return Indented String */ public static String indent( String input, int level, String tab ) { try { return indent( new StringReader( input ), level, tab, EMPTY ); } catch( IOException ex ) { throw new UncheckedIOException( ex ); } } /** * Indent a String using a prefix. * * @param input String to indent * @param level Indent levels count * @param tab Level String * @param prefix Prefix * * @return Indented String */ public static String indent( String input, int level, String tab, String prefix ) { try { return indent( new StringReader( input ), level, tab, prefix ); } catch( IOException ex ) { throw new UncheckedIOException( ex ); } } /** * Read a String and indent it with two spaces levels. * * @param input Reader input * @param level Indent levels count * * @return Indented String * * @throws IOException If unable to read input */ public static String indentTwoSpaces( Reader input, int level ) throws IOException { return indent( input, level, " " ); } /** * Read a String and indent it with tabulation levels. * * @param input Reader input * @param level Indent levels count * * @return Indented String * * @throws IOException If unable to read input */ public static String indentTab( Reader input, int level ) throws IOException { return indent( input, level, TAB ); } /** * Read a String and indent it. * * @param input Reader input * @param level Indent levels count * @param tab Level String * * @return Indented String * * @throws IOException If unable to read input */ public static String indent( Reader input, int level, String tab ) throws IOException { return indent( input, level, tab, EMPTY ); } /** * Read a String and indent it using a prefix. * * @param input Reader input * @param level Indent levels count * @param tab Level String * @param prefix Prefix * * @return Indented String * * @throws IOException If unable to read input */ public static String indent( Reader input, int level, String tab, String prefix ) throws IOException { BufferedReader reader = new BufferedReader( input ); StringBuilder output = new StringBuilder(); try { String eachLine = reader.readLine(); if( !isEmpty( eachLine ) ) { appendIndent( output, level, tab ).append( prefix ).append( eachLine ); while( ( eachLine = reader.readLine() ) != null ) { output.append( NEWLINE ); if( !isEmpty( eachLine ) ) { appendIndent( output, level, tab ).append( prefix ).append( eachLine ); } } } return output.toString(); } finally { Closeables.closeSilently( reader ); } } private static StringBuilder appendIndent( StringBuilder output, int level, String tab ) { for( int indent = 0; indent < level; indent++ ) { output.append( tab ); } return output; } public static String withTrail( String input, String trail ) { return input.endsWith( trail ) ? input : input + trail; } public static String withHead( String input, String head ) { return input.startsWith( head ) ? input : head + input; } public static String withoutTrail( String input, String trail ) { return input.endsWith( trail ) ? input.substring( 0, input.length() - trail.length() ) : input; } public static String withoutHead( String input, String head ) { return input.startsWith( head ) ? input.substring( head.length() ) : input; } public static int indexOfNth( String string, int count, String substring ) { requireNonNull( string, "String" ); ensureGreaterOrEqual( "N", count, 1 ); requireNonNull( substring, "Sub String" ); int index = -1; int nextFromIndex = 0; for( int loop = 0; loop < count; loop++ ) { int loopIndex = string.indexOf( substring, nextFromIndex ); if( loopIndex < 0 ) { return -1; } index = loopIndex; nextFromIndex = loopIndex + substring.length(); } return index; } public static int lastIndexOfNth( String string, int count, String substring ) { requireNonNull( string, "String" ); ensureGreaterOrEqual( "N", count, 1 ); requireNonNull( substring, "Sub String" ); String inner = string; int index = -1; for( int loop = 0; loop < count; loop++ ) { index = inner.lastIndexOf( substring ); if( index < 0 ) { break; } inner = inner.substring( 0, index ); } return index; } private Strings() { } }