/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other 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 org.arakhne.afc.text;
import java.lang.ref.SoftReference;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.xtext.xbase.lib.Inline;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.vmutil.locale.Locale;
/**
* This class permits to manipulate texts.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("checkstyle:methodcount")
public final class TextUtil {
private static final ReentrantLock LOCK = new ReentrantLock();
private static SoftReference<Map<Character, String>> accentTransTbl;
private static SoftReference<Map<String, Integer>> htmlToJavaTransTbl;
private static SoftReference<Map<Character, String>> javaToHtmlTransTbl;
private TextUtil() {
//
}
/** Enforced version of the equality test on two strings with case ignoring.
* This enforced version supported <code>null</code> values
* given as parameters.
*
* @param firstText first text.
* @param secondText second text.
* @param isNullEmptyEquivalence indicates if the <code>null</code> value
* is assimilated to the empty string.
* @return <code>true</code> if a is equal to b; otherwise <code>false</code>.
*/
@Pure
public static boolean equalsIgnoreCase(String firstText, String secondText, boolean isNullEmptyEquivalence) {
final String aa = (firstText != null || !isNullEmptyEquivalence) ? firstText : ""; //$NON-NLS-1$
final String bb = (secondText != null || !isNullEmptyEquivalence) ? secondText : ""; //$NON-NLS-1$
if (aa == null) {
return bb == null;
}
if (bb == null) {
return false;
}
return aa.equalsIgnoreCase(bb);
}
/** Replies a base 26 encoding string for the given
* number.
*
* @param number the number to encode.
* @return the base 26 encoding.
* @since 4.0
*/
@Pure
@SuppressWarnings("checkstyle:magicnumber")
public static String encodeBase26(int number) {
final StringBuilder value = new StringBuilder();
int code = number;
do {
final int rest = code % 26;
value.insert(0, (char) ('A' + rest));
code = code / 26 - 1;
}
while (code >= 0);
return value.toString();
}
/** Replies the html-to-java's translation table.
*
* <p>This method read the translations from the
* resource file <code>HTML_TRANS_TBL</code>.
*
* @return the translation table or <code>null</code> if none was found.
* The translation table maps an HTML entity to its corresponding ISO chararacter code.
* @since 4.0
*/
@Pure
@SuppressWarnings("checkstyle:npathcomplexity")
public static Map<String, Integer> getHtmlToJavaTranslationTable() {
Map<String, Integer> map = null;
try {
LOCK.lock();
if (htmlToJavaTransTbl != null) {
map = htmlToJavaTransTbl.get();
}
} finally {
LOCK.unlock();
}
if (map != null) {
return map;
}
// Get the resource file
ResourceBundle resource = null;
try {
resource = ResourceBundle.getBundle(
TextUtil.class.getCanonicalName(),
java.util.Locale.getDefault());
} catch (MissingResourceException exep) {
return null;
}
// get the resource string
final String result;
try {
result = resource.getString("HTML_TRANS_TBL"); //$NON-NLS-1$
} catch (Exception e) {
return null;
}
map = new TreeMap<>();
final String[] pairs = result.split("(\\}\\{)|\\{|\\}"); //$NON-NLS-1$
Integer isoCode;
String entity;
String code;
for (int i = 1; (i + 1) < pairs.length; i += 2) {
try {
entity = pairs[i];
code = pairs[i + 1];
isoCode = Integer.valueOf(code);
if (isoCode != null) {
map.put(entity, isoCode);
}
} catch (Throwable exception) {
//
}
}
try {
LOCK.lock();
htmlToJavaTransTbl = new SoftReference<>(map);
} finally {
LOCK.unlock();
}
return map;
}
/** Replies the java-to-html's translation table.
*
* <p>This method read the translations from the
* resource file <code>HTML_TRANS_TBL</code>.
*
* @return the translation table or <code>null</code> if none was found.
* The translation table maps an ISO character code to its corresponding HTML entity.
* @since 4.0
*/
@Pure
@SuppressWarnings("checkstyle:npathcomplexity")
public static Map<Character, String> getJavaToHTMLTranslationTable() {
Map<Character, String> map = null;
try {
LOCK.lock();
if (javaToHtmlTransTbl != null) {
map = javaToHtmlTransTbl.get();
}
} finally {
LOCK.unlock();
}
if (map != null) {
return map;
}
// Get the resource file
ResourceBundle resource = null;
try {
resource = ResourceBundle.getBundle(
TextUtil.class.getCanonicalName(),
java.util.Locale.getDefault());
} catch (MissingResourceException exep) {
return null;
}
// get the resource string
final String result;
try {
result = resource.getString("HTML_TRANS_TBL"); //$NON-NLS-1$
} catch (Exception e) {
return null;
}
map = new TreeMap<>();
final String[] pairs = result.split("(\\}\\{)|\\{|\\}"); //$NON-NLS-1$
Integer isoCode;
String entity;
String code;
for (int i = 1; (i + 1) < pairs.length; i += 2) {
try {
entity = pairs[i];
code = pairs[i + 1];
isoCode = Integer.valueOf(code);
if (isoCode != null) {
map.put((char) isoCode.intValue(), entity);
}
} catch (Throwable exception) {
//
}
}
try {
LOCK.lock();
javaToHtmlTransTbl = new SoftReference<>(map);
} finally {
LOCK.unlock();
}
return map;
}
/** Parse the given HTML text and replace all
* the HTML entities by the corresponding unicode
* character.
*
* @param html is the HTML to convert.
* @return the unicode representation of the given html text.
* @since 4.0
* @see #toHTML(String)
*/
@Pure
@SuppressWarnings("checkstyle:magicnumber")
public static String parseHTML(String html) {
if (html == null) {
return null;
}
final Map<String, Integer> transTbl = getHtmlToJavaTranslationTable();
assert transTbl != null;
if (transTbl.isEmpty()) {
return html;
}
final Pattern pattern = Pattern.compile("[&](([a-zA-Z]+)|(#x?[0-9]+))[;]"); //$NON-NLS-1$
final Matcher matcher = pattern.matcher(html);
final StringBuilder result = new StringBuilder();
String entity;
Integer isoCode;
int lastIndex = 0;
while (matcher.find()) {
final int idx = matcher.start();
result.append(html.substring(lastIndex, idx));
lastIndex = matcher.end();
entity = matcher.group(1);
if (entity.startsWith("#x")) { //$NON-NLS-1$
try {
isoCode = Integer.valueOf(entity.substring(2), 16);
} catch (Throwable exception) {
isoCode = null;
}
} else if (entity.startsWith("#")) { //$NON-NLS-1$
try {
isoCode = Integer.valueOf(entity.substring(1));
} catch (Throwable exception) {
isoCode = null;
}
} else {
isoCode = transTbl.get(entity);
}
if (isoCode == null) {
result.append(matcher.group());
} else {
result.append((char) isoCode.intValue());
}
}
if (lastIndex < html.length()) {
result.append(html.substring(lastIndex));
}
return result.toString();
}
/** Translate all the special character from the given
* text to their equivalent HTML entities.
*
* @param text is the text to convert.
* @return the HTML text which is corresponding to the given text.
* @since 4.0
* @see #parseHTML(String)
*/
@Pure
public static String toHTML(String text) {
if (text == null) {
return null;
}
final Map<Character, String> transTbl = getJavaToHTMLTranslationTable();
assert transTbl != null;
if (transTbl.isEmpty()) {
return text;
}
final StringBuilder patternStr = new StringBuilder();
for (final Character c : transTbl.keySet()) {
if (patternStr.length() > 0) {
patternStr.append("|"); //$NON-NLS-1$
}
patternStr.append(Pattern.quote(c.toString()));
}
final Pattern pattern = Pattern.compile(patternStr.toString());
final Matcher matcher = pattern.matcher(text);
final StringBuilder result = new StringBuilder();
String character;
String entity;
int lastIndex = 0;
while (matcher.find()) {
final int idx = matcher.start();
result.append(text.substring(lastIndex, idx));
lastIndex = matcher.end();
character = matcher.group();
if (character.length() == 1) {
entity = transTbl.get(Character.valueOf(character.charAt(0)));
if (entity != null) {
entity = "&" + entity + ";"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
entity = character;
}
} else {
entity = character;
}
result.append(entity);
}
if (lastIndex < text.length()) {
result.append(text.substring(lastIndex));
}
return result.toString();
}
/** Format the text to be sure that each line is not
* more longer than the specified quantity of characters.
*
* @param text is the string to cut
* @param column is the column number that corresponds to the splitting point.
* @return the given {@code text} splitted in lines separated by <code>\n</code>.
*/
@Pure
public static String cutString(String text, int column) {
final StringBuilder buffer = new StringBuilder();
cutStringAlgo(text, new CutStringColumnCritera(column), new CutStringToString(buffer));
return buffer.toString();
}
/** Format the text to be sure that each line is not
* more longer than the specified quantity of characters.
*
* @param text is the string to cut
* @param column is the column number that corresponds to the splitting point.
* @return the given {@code text} splitted in lines separated by <code>\n</code>.
*/
@Pure
public static String[] cutStringAsArray(String text, int column) {
final List<String> list = new ArrayList<>();
cutStringAlgo(text, new CutStringColumnCritera(column), new CutStringToArray(list));
final String[] result = new String[list.size()];
list.toArray(result);
list.clear();
return result;
}
/** Format the text to be sure that each line is not
* more longer than the specified critera.
*
* @param text is the string to cut
* @param critera is the critera to respect.
* @param output is the given {@code text} splitted in lines separated by <code>\n</code>.
* @since 4.0
*/
public static void cutStringAsArray(String text, CutStringCritera critera, List<String> output) {
cutStringAlgo(text, critera, new CutStringToArray(output));
}
private static void cutStringAlgo(String text, CutStringCritera critera, CutStringAlgorithm algo) {
assert critera != null;
if (text == null || critera.getCritera() <= 0) {
return;
}
assert algo != null;
final StringBuilder line = new StringBuilder();
final String[] lines = text.split("[\\n\\r\r\n]"); //$NON-NLS-1$
long lineLength;
int maxLength;
for (int idxLine = 0; idxLine < lines.length; ++idxLine) {
final String[] words = lines[idxLine].split("[\t \n\r\f]+"); //$NON-NLS-1$
lineLength = 0;
for (int i = 0; i < words.length; ++i) {
final String word = words[i];
if (critera.isOverfull(lineLength, word)) {
if (line.length() > 0 || i > 0) {
algo.addLine(line.toString());
}
line.setLength(0);
line.append(word);
// Split the word
maxLength = critera.getCutIndexFor(line.toString());
while (maxLength > 0) {
algo.addLine(line.substring(0, maxLength));
line.delete(0, maxLength);
maxLength = critera.getCutIndexFor(line.toString());
}
// Append last part of the word
lineLength = critera.getLengthFor(line.toString());
} else {
if (line.length() > 0) {
line.append(' ');
lineLength += critera.getLengthFor(" "); //$NON-NLS-1$
}
// Append word
line.append(word);
lineLength += critera.getLengthFor(word);
}
}
if (line.length() > 0) {
algo.addLine(line.toString());
line.setLength(0);
}
}
}
/** Replies the character which follow the first '&'.
*
* @param text is the text to scan.
* @return the character that is following the first '&' or '<code>\0</code>'
*/
@Pure
public static char getMnemonicChar(String text) {
if (text != null) {
final int pos = text.indexOf('&');
if ((pos != -1) && (pos < text.length() - 1)) {
return text.charAt(pos + 1);
}
}
return '\0';
}
/** Remove the mnemonic char from the specified string.
*
* @param text is the text to scan.
* @return the given text without the mnemonic character.
*/
@Pure
public static String removeMnemonicChar(String text) {
if (text == null) {
return text;
}
return text.replaceFirst("&", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
/** Replies the accent's translation table.
*
* <p>This method read the translations from the
* resource file <code>ACCENT_TRANS_TBL</code>.
*
* @return the translation table or <code>null</code> if none was found.
*/
@Pure
@SuppressWarnings("checkstyle:npathcomplexity")
public static Map<Character, String> getAccentTranslationTable() {
Map<Character, String> map = null;
try {
LOCK.lock();
if (accentTransTbl != null) {
map = accentTransTbl.get();
}
} finally {
LOCK.unlock();
}
if (map != null) {
return map;
}
// Get the resource file
ResourceBundle resource = null;
try {
resource = ResourceBundle.getBundle(
TextUtil.class.getCanonicalName(),
java.util.Locale.getDefault());
} catch (MissingResourceException exep) {
return null;
}
// get the resource string
final String result;
try {
result = resource.getString("ACCENT_TRANS_TBL"); //$NON-NLS-1$
} catch (Exception e) {
return null;
}
map = new TreeMap<>();
final String[] pairs = result.split("(\\}\\{)|\\{|\\}"); //$NON-NLS-1$
for (final String pair : pairs) {
if (pair.length() > 1) {
map.put(pair.charAt(0), pair.substring(1));
}
}
try {
LOCK.lock();
accentTransTbl = new SoftReference<>(map);
} finally {
LOCK.unlock();
}
return map;
}
/** Remove the accents inside the specified string.
*
* @param text is the string into which the accents must be removed.
* @return the given string without the accents
*/
@Pure
public static String removeAccents(String text) {
final Map<Character, String> map = getAccentTranslationTable();
if ((map == null) || (map.isEmpty())) {
return text;
}
return removeAccents(text, map);
}
/** Remove the accents inside the specified string.
*
* @param text is the string into which the accents must be removed.
* @param map is the translation table from an accentuated character to an
* unaccentuated character.
* @return the given string without the accents
*/
@Pure
public static String removeAccents(String text, Map<Character, String> map) {
if (text == null) {
return text;
}
final StringBuilder buffer = new StringBuilder();
for (final char c : text.toCharArray()) {
final String trans = map.get(c);
if (trans != null) {
buffer.append(trans);
} else {
buffer.append(c);
}
}
return buffer.toString();
}
/** Split the given string according to brackets.
* The brackets are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>splitBrackets("{a}{b}{cd}")</code> returns the array
* <code>["a","b","cd"]</code></li>
* <li><code>splitBrackets("abcd")</code> returns the array
* <code>["abcd"]</code></li>
* <li><code>splitBrackets("a{bcd")</code> returns the array
* <code>["a","bcd"]</code></li>
* </ul>
*
* @param str is the strig with brackets.
* @return the groups of strings
*/
@Pure
@Inline(value = "textUtil.split('{', '}', $1)", imported = {TextUtil.class})
public static String[] splitBrackets(String str) {
return split('{', '}', str);
}
/** Split the given string according to the separators.
* The separators are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>split('{','}',"{a}{b}{cd}")</code> returns the array
* <code>["a","b","cd"]</code></li>
* <li><code>split('{','}',"abcd")</code> returns the array
* <code>["abcd"]</code></li>
* <li><code>split('{','}',"a{bcd")</code> returns the array
* <code>["a","bcd"]</code></li>
* </ul>
*
* @param leftSeparator is the left separator.
* @param rightSeparator is the right separator.
* @param str is the strig with brackets.
* @return the groups of strings
* @since 4.0
*/
@Pure
public static String[] split(char leftSeparator, char rightSeparator, String str) {
final SplitSeparatorToArrayAlgorithm algo = new SplitSeparatorToArrayAlgorithm();
splitSeparatorAlgorithm(leftSeparator, rightSeparator, str, algo);
return algo.toArray();
}
/** Split the given string according to brackets.
* The brackets are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>splitBrackets("{a}{b}{cd}")</code> returns the array
* <code>["a","b","cd"]</code></li>
* <li><code>splitBrackets("abcd")</code> returns the array
* <code>["abcd"]</code></li>
* <li><code>splitBrackets("a{bcd")</code> returns the array
* <code>["a","bcd"]</code></li>
* </ul>
*
* @param str is the elements enclosed by backets.
* @return the groups of strings
*/
@Pure
@Inline(value = "textUtil.splitAsList('{', '}', $1)", imported = {TextUtil.class})
public static List<String> splitBracketsAsList(String str) {
return splitAsList('{', '}', str);
}
/** Split the given string according to separators.
* The separators are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>split('{','}',"{a}{b}{cd}")</code> returns the array
* <code>["a","b","cd"]</code></li>
* <li><code>split('{','}',"abcd")</code> returns the array
* <code>["abcd"]</code></li>
* <li><code>split('{','}',"a{bcd")</code> returns the array
* <code>["a","bcd"]</code></li>
* </ul>
*
* @param leftSeparator is the left separator.
* @param rightSeparator is the right separator.
* @param str is the elements enclosed by backets.
* @return the groups of strings
*/
@Pure
public static List<String> splitAsList(char leftSeparator, char rightSeparator, String str) {
final List<String> list = new ArrayList<>();
splitSeparatorAlgorithm(
leftSeparator, rightSeparator, str,
new SplitSeparatorToListAlgorithm(list));
return list;
}
@SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"})
private static void splitSeparatorAlgorithm(
char leftSeparator, char rightSeparator,
String str, SplitSeparatorAlgorithm buffer) {
assert buffer != null;
if (str != null && str.length() > 0) {
final StringBuilder patternStr = new StringBuilder();
patternStr.append("([^\\"); //$NON-NLS-1$
patternStr.append(leftSeparator);
patternStr.append("\\"); //$NON-NLS-1$
patternStr.append(rightSeparator);
patternStr.append("]*)(\\"); //$NON-NLS-1$
patternStr.append(leftSeparator);
patternStr.append("|\\"); //$NON-NLS-1$
patternStr.append(rightSeparator);
patternStr.append(")"); //$NON-NLS-1$
final Pattern pattern = Pattern.compile(patternStr.toString());
final Matcher matcher = pattern.matcher(str);
// inclusive
int startOffset = 0;
// exclusive
int endOffset;
int depth = 0;
final StringBuilder token = new StringBuilder();
while (matcher.find()) {
final String previousText = matcher.group(1);
final String separator = matcher.group(2);
endOffset = startOffset + previousText.length();
if (startOffset < str.length() && endOffset > startOffset) {
token.append(str.substring(startOffset, endOffset));
}
if (separator.equals(Character.toString(leftSeparator))) {
if (depth > 0) {
token.append(separator);
} else if (token.length() > 0) {
final String s = token.toString().trim();
if (s.length() > 0) {
buffer.addToken(s);
}
token.setLength(0);
}
++depth;
} else if (separator.equals(Character.toString(rightSeparator))) {
if (depth == 0) {
token.append(separator);
} else {
--depth;
if (depth > 0) {
token.append(separator);
} else {
buffer.addToken(token.toString());
token.setLength(0);
}
}
} else {
throw new IllegalStateException();
}
startOffset = endOffset + separator.length();
}
if (startOffset < str.length()) {
token.append(str.substring(startOffset));
}
if (token.length() > 0) {
final String s = token.toString().trim();
if (s.length() > 0) {
buffer.addToken(s);
}
}
}
}
/** Split the given string according to brackets.
* The brackets are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>splitBrackets("{a}{b}{cd}")</code> returns the array
* <code>["a","b","cd"]</code></li>
* <li><code>splitBrackets("abcd")</code> returns the array
* <code>["abcd"]</code></li>
* <li><code>splitBrackets("a{bcd")</code> returns the array
* <code>["a","bcd"]</code></li>
* </ul>
*
* @param str is the elements enclosed by backets.
* @return the groups of strings
*/
@Pure
@Inline(value = "textUtil.splitAsUUIDs('{', '}', $1)", imported = {TextUtil.class})
public static List<UUID> splitBracketsAsUUIDs(String str) {
return splitAsUUIDs('{', '}', str);
}
/** Split the given string according to separators.
* The separators are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>split('{','}',"{a}{b}{cd}")</code> returns the array
* <code>["a","b","cd"]</code></li>
* <li><code>split('{','}',"abcd")</code> returns the array
* <code>["abcd"]</code></li>
* <li><code>split('{','}',"a{bcd")</code> returns the array
* <code>["a","bcd"]</code></li>
* </ul>
*
* @param leftSeparator is the left separator.
* @param rightSeparator is the right separator.
* @param str is the elements enclosed by backets.
* @return the groups of strings
* @since 4.0
*/
@Pure
public static List<UUID> splitAsUUIDs(char leftSeparator, char rightSeparator, String str) {
final List<UUID> list = new ArrayList<>();
splitSeparatorAlgorithm(leftSeparator, rightSeparator, str, new UUIDSplitSeparatorAlgorithm(list));
return list;
}
/** Merge the given strings with to brackets.
* The brackets are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>mergeBrackets("a","b","cd")</code> returns the string
* <code>"{a}{b}{cd}"</code></li>
* <li><code>mergeBrackets("a{bcd")</code> returns the string
* <code>"{a{bcd}"</code></li>
* </ul>
*
* @param <T> is the type of the parameters.
* @param strs is the array of strings.
* @return the string with bracketed strings.
*/
@Pure
@Inline(value = "TextUtil.join('{', '}', $1)", imported = {TextUtil.class})
public static <T> String mergeBrackets(@SuppressWarnings("unchecked") T... strs) {
return join('{', '}', strs);
}
/** Merge the given strings with to brackets.
* The brackets are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>mergeBrackets("a","b","cd")</code> returns the string
* <code>"{a}{b}{cd}"</code></li>
* <li><code>mergeBrackets("a{bcd")</code> returns the string
* <code>"{a{bcd}"</code></li>
* </ul>
*
* @param strs is the array of strings.
* @return the string with bracketed strings.
* @see #join(char, char, Iterable)
*/
@Pure
@Inline(value = "TextUtil.join('{', '}', $1)", imported = {TextUtil.class})
public static String mergeBrackets(Iterable<?> strs) {
return join('{', '}', strs);
}
/** Merge the given strings with to separators.
* The separators are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>merge('{','}',"a","b","cd")</code> returns the string
* <code>"{a}{b}{cd}"</code></li>
* <li><code>merge('{','}',"a{bcd")</code> returns the string
* <code>"{a{bcd}"</code></li>
* </ul>
*
* @param <T> is the type of the parameters.
* @param leftSeparator is the left separator to use.
* @param rightSeparator is the right separator to use.
* @param strs is the array of strings.
* @return the string with merged strings.
* @since 4.0
*/
@Pure
public static <T> String join(char leftSeparator, char rightSeparator, @SuppressWarnings("unchecked") T... strs) {
final StringBuilder buffer = new StringBuilder();
for (final Object s : strs) {
buffer.append(leftSeparator);
if (s != null) {
buffer.append(s.toString());
}
buffer.append(rightSeparator);
}
return buffer.toString();
}
/** Merge the given strings with to separators.
* The separators are used to delimit the groups
* of characters.
*
* <p>Examples:
* <ul>
* <li><code>merge('{','}',"a","b","cd")</code> returns the string
* <code>"{a}{b}{cd}"</code></li>
* <li><code>merge('{','}',"a{bcd")</code> returns the string
* <code>"{a{bcd}"</code></li>
* </ul>
*
* @param leftSeparator is the left separator to use.
* @param rightSeparator is the right separator to use.
* @param strs is the array of strings.
* @return the string with merged strings.
* @since 4.0
*/
@Pure
public static String join(char leftSeparator, char rightSeparator, Iterable<?> strs) {
final StringBuilder buffer = new StringBuilder();
for (final Object s : strs) {
buffer.append(leftSeparator);
if (s != null) {
buffer.append(s.toString());
}
buffer.append(rightSeparator);
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
*
* @param <T> is the type of the elements
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, Arrays.asList($2))", imported = {TextUtil.class, Arrays.class})
public static <T> String join(String joinText, @SuppressWarnings("unchecked") T... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, boolean... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, byte... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, char... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, short... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, int... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, long... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, float... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, double... elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
*
* @param joinText the text to use for joining.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, null, null, $2)", imported = {TextUtil.class})
public static String join(String joinText, Iterable<?> elements) {
return join(joinText, null, null, elements);
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param <T> is the type of the elements
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
@Inline(value = "TextUtil.join($1, $2, $3, Arrays.asList($4))", imported = {TextUtil.class, Arrays.class})
public static <T> String join(String joinText, String prefix, String postfix, @SuppressWarnings("unchecked") T... elements) {
return join(joinText, prefix, postfix, Arrays.asList(elements));
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, Iterable<?> elements) {
final StringBuilder buffer = new StringBuilder();
String txt;
for (final Object e : elements) {
if (e != null) {
txt = e.toString();
if (txt != null && txt.length() > 0) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(txt);
if (postfix != null) {
buffer.append(postfix);
}
}
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, boolean... elements) {
final StringBuilder buffer = new StringBuilder();
for (final boolean e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, byte... elements) {
final StringBuilder buffer = new StringBuilder();
for (final byte e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, char... elements) {
final StringBuilder buffer = new StringBuilder();
for (final char e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, short... elements) {
final StringBuilder buffer = new StringBuilder();
for (final short e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, int... elements) {
final StringBuilder buffer = new StringBuilder();
for (final int e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, long... elements) {
final StringBuilder buffer = new StringBuilder();
for (final long e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, float... elements) {
final StringBuilder buffer = new StringBuilder();
for (final float e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/** Join the elements of the given array with the given join text.
* The {@code prefix} and {@code postfix} values will be put
* just before and just after each element respectively.
*
* @param joinText the text to use for joining.
* @param prefix the text to put as prefix.
* @param postfix the text to put as postfix.
* @param elements the parts of text to join.
* @return the joining text
*/
@Pure
public static String join(String joinText, String prefix, String postfix, double... elements) {
final StringBuilder buffer = new StringBuilder();
for (final double e : elements) {
if (buffer.length() > 0) {
buffer.append(joinText);
}
if (prefix != null) {
buffer.append(prefix);
}
buffer.append(e);
if (postfix != null) {
buffer.append(postfix);
}
}
return buffer.toString();
}
/**
* Compares this <code>String</code> to another <code>String</code>,
* ignoring accent considerations. Two strings are considered equal
* ignoring accents if they are of the same length, and corresponding
* characters in the two strings are equal ignoring accents.
*
* <p>This method is equivalent to:
* <pre><code>
* TextUtil.removeAccents(s1,map).equals(TextUtil.removeAccents(s2,map));
* </code></pre>
*
* @param s1 is the first string to compare.
* @param s2 is the second string to compare.
* @param map is the translation table from an accentuated character to an
* unaccentuated character.
* @return <code>true</code> if the argument is not <code>null</code>
* and the <code>String</code>s are equal,
* ignoring case; <code>false</code> otherwise.
* @see #removeAccents(String, Map)
*/
@Pure
@Inline(value = "TextUtil.removeAccents($1, $3).equals(TextUtil.removeAccents($2, $3))", imported = {TextUtil.class})
public static boolean equalsIgnoreAccents(String s1, String s2, Map<Character, String> map) {
return removeAccents(s1, map).equals(removeAccents(s2, map));
}
/**
* Compares this <code>String</code> to another <code>String</code>,
* ignoring case and accent considerations. Two strings are considered equal
* ignoring case and accents if they are of the same length, and corresponding
* characters in the two strings are equal ignoring case and accents.
*
* <p>This method is equivalent to:
* <pre><code>
* TextUtil.removeAccents(s1,map).equalsIgnoreCase(TextUtil.removeAccents(s2,map));
* </code></pre>
*
* @param s1 is the first string to compare.
* @param s2 is the second string to compare.
* @param map is the translation table from an accentuated character to an
* unaccentuated character.
* @return <code>true</code> if the argument is not <code>null</code>
* and the <code>String</code>s are equal,
* ignoring case; <code>false</code> otherwise.
* @see #removeAccents(String, Map)
*/
@Pure
@Inline(value = "TextUtil.removeAccents($1, $3).equalsIgnoreCase(TextUtil.removeAccents($2, $3))",
imported = {TextUtil.class})
public static boolean equalsIgnoreCaseAccents(String s1, String s2, Map<Character, String> map) {
return removeAccents(s1, map).equalsIgnoreCase(removeAccents(s2, map));
}
/** Translate the specified string to lower case and remove the accents.
*
* @param text is the text to scan.
* @return the given string without the accents and lower cased
*/
@Pure
public static String toLowerCaseWithoutAccent(String text) {
final Map<Character, String> map = getAccentTranslationTable();
if ((map == null) || (map.isEmpty())) {
return text;
}
return toLowerCaseWithoutAccent(text, map);
}
/** Translate the specified string to lower case and remove the accents.
*
* @param text is the text to scan.
* @param map is the translation table from an accentuated character to an
* unaccentuated character.
* @return the given string without the accents and lower cased
*/
@Pure
public static String toLowerCaseWithoutAccent(String text, Map<Character, String> map) {
final StringBuilder buffer = new StringBuilder();
for (final char c : text.toCharArray()) {
final String trans = map.get(c);
if (trans != null) {
buffer.append(trans.toLowerCase());
} else {
buffer.append(Character.toLowerCase(c));
}
}
return buffer.toString();
}
/** Translate the specified string to upper case and remove the accents.
*
* @param text is the text to scan.
* @return the given string without the accents and upper cased
*/
@Pure
public static String toUpperCaseWithoutAccent(String text) {
final Map<Character, String> map = getAccentTranslationTable();
if ((map == null) || (map.isEmpty())) {
return text;
}
return toUpperCaseWithoutAccent(text, map);
}
/** Translate the specified string to upper case and remove the accents.
*
* @param text is the text to scan.
* @param map is the translation table from an accentuated character to an
* unaccentuated character.
* @return the given string without the accents and upper cased
*/
@Pure
public static String toUpperCaseWithoutAccent(String text, Map<Character, String> map) {
final StringBuilder buffer = new StringBuilder();
for (final char c : text.toCharArray()) {
final String trans = map.get(c);
if (trans != null) {
buffer.append(trans.toUpperCase());
} else {
buffer.append(Character.toUpperCase(c));
}
}
return buffer.toString();
}
/** Compute the better metric representing
* the given time amount and reply a string representation
* of the given amount with this selected unit.
*
* <p>This function try to use a greater metric unit.
*
* @param amount is the amount expressed in the given unit.
* @param unit is the unit of the given amount.
* @return a string representation of the given amount.
*/
@Pure
@SuppressWarnings({"checkstyle:magicnumber", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"})
public static String formatTime(double amount, TimeUnit unit) {
double amt;
double coef = 1.;
switch (unit) {
case DAYS:
coef = 86400.;
break;
case HOURS:
coef = 3600.;
break;
case MINUTES:
coef = 60.;
break;
case SECONDS:
break;
case MILLISECONDS:
coef = 1e-3;
break;
case MICROSECONDS:
coef = 1e-6;
break;
case NANOSECONDS:
coef = 1e-9;
break;
default:
throw new IllegalArgumentException();
}
// amount is in seconds
amt = amount * coef;
final StringBuilder text = new StringBuilder();
String centuries = ""; //$NON-NLS-1$
String years = ""; //$NON-NLS-1$
String days = ""; //$NON-NLS-1$
String hours = ""; //$NON-NLS-1$
String minutes = ""; //$NON-NLS-1$
String seconds = ""; //$NON-NLS-1$
long ah = 0;
long am = 0;
long as = 0;
int idx = 0;
if (amt >= 3153600000.) {
final long a = (long) Math.floor(amt / 3153600000.);
centuries = Locale.getString((a >= 2) ? "TIME_FORMAT_Cs" : "TIME_FORMAT_C", //$NON-NLS-1$ //$NON-NLS-2$
Long.toString(a));
amt -= a * 3153600000.;
text.append(centuries);
idx |= 32;
}
if (amt >= 31536000.) {
final long a = (long) Math.floor(amt / 31536000.);
years = Locale.getString((a >= 2) ? "TIME_FORMAT_Ys" : "TIME_FORMAT_Y", //$NON-NLS-1$ //$NON-NLS-2$
Long.toString(a));
amt -= a * 31536000.;
if (text.length() > 0) {
text.append(' ');
}
text.append(years);
idx |= 16;
}
if (amt >= 86400.) {
final long a = (long) Math.floor(amt / 86400.);
days = Locale.getString((a >= 2) ? "TIME_FORMAT_Ds" : "TIME_FORMAT_D", //$NON-NLS-1$ //$NON-NLS-2$
Long.toString(a));
amt -= a * 86400.;
if (text.length() > 0) {
text.append(' ');
}
text.append(days);
idx |= 8;
}
//-------------------
if (amt >= 3600.) {
ah = (long) Math.floor(amt / 3600.);
hours = Long.toString(ah);
if (ah < 10.) {
hours = "0" + hours; //$NON-NLS-1$
}
amt -= ah * 3600.;
idx |= 4;
}
if (amt >= 60.) {
am = (long) Math.floor(amt / 60.);
minutes = Long.toString(am);
if (am < 10.) {
minutes = "0" + minutes; //$NON-NLS-1$
}
amt -= am * 60.;
idx |= 2;
}
if (amt >= 0. || idx == 0) {
if (idx >= 8) {
as = (long) Math.floor(amt);
seconds = Long.toString(as);
} else {
final NumberFormat fmt = new DecimalFormat("#0.000"); //$NON-NLS-1$
seconds = fmt.format(amt);
}
idx |= 1;
}
if ((idx & 7) == 7) {
if (text.length() > 0) {
text.append(' ');
}
if (idx >= 8 && as > 0) {
if (as < 10.) {
seconds = "0" + seconds; //$NON-NLS-1$
}
} else if (idx < 8 && amt > 0. && amt < 10.) {
seconds = "0" + seconds; //$NON-NLS-1$
}
text.append(Locale.getString("TIME_FORMAT_HMS", hours, minutes, seconds)); //$NON-NLS-1$
} else {
if (ah > 0) {
if (text.length() > 0) {
text.append(' ');
}
text.append(Locale.getString((ah >= 2) ? "TIME_FORMAT_Hs" : "TIME_FORMAT_H", //$NON-NLS-1$ //$NON-NLS-2$
hours));
}
if (am > 0) {
if (text.length() > 0) {
text.append(' ');
}
text.append(Locale.getString((am >= 2) ? "TIME_FORMAT_Ms" : "TIME_FORMAT_M", //$NON-NLS-1$ //$NON-NLS-2$
minutes));
}
if (idx >= 8 && as > 0) {
if (text.length() > 0) {
text.append(' ');
}
text.append(Locale.getString((as >= 2) ? "TIME_FORMAT_Ss" : "TIME_FORMAT_S", //$NON-NLS-1$ //$NON-NLS-2$
seconds));
} else if (idx < 8 && amt > 0.) {
if (text.length() > 0) {
text.append(' ');
}
text.append(Locale.getString((amt >= 2.) ? "TIME_FORMAT_Ss" : "TIME_FORMAT_S", //$NON-NLS-1$ //$NON-NLS-2$
seconds));
}
}
return text.toString();
}
/** Format the given double value.
*
* @param amount the value to convert.
* @param decimalCount is the maximal count of decimal to put in the string.
* @return a string representation of the given value.
*/
@Pure
public static String formatDouble(double amount, int decimalCount) {
final int dc = (decimalCount < 0) ? 0 : decimalCount;
final StringBuilder str = new StringBuilder("#0"); //$NON-NLS-1$
if (dc > 0) {
str.append('.');
for (int i = 0; i < dc; ++i) {
str.append('0');
}
}
final DecimalFormat fmt = new DecimalFormat(str.toString());
return fmt.format(amount);
}
/** Format the given float value.
*
* @param amount the value to convert.
* @param decimalCount is the maximal count of decimal to put in the string.
* @return a string representation of the given value.
*/
@Pure
public static String formatFloat(float amount, int decimalCount) {
final int dc = (decimalCount < 0) ? 0 : decimalCount;
final StringBuilder str = new StringBuilder("#0"); //$NON-NLS-1$
if (dc > 0) {
str.append('.');
for (int i = 0; i < dc; ++i) {
str.append('0');
}
}
final DecimalFormat fmt = new DecimalFormat(str.toString());
return fmt.format(amount);
}
/** Compute the Levenshstein distance between two strings.
*
* <p>Null string is assimilated to the empty string.
*
* @param firstString first string.
* @param secondString second string.
* @return the Levenshstein distance.
* @see "https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance"
*/
public static int getLevenshteinDistance(String firstString, String secondString) {
final String s0 = firstString == null ? "" : firstString; //$NON-NLS-1$
final String s1 = secondString == null ? "" : secondString; //$NON-NLS-1$
final int len0 = s0.length() + 1;
final int len1 = s1.length() + 1;
// the array of distances
int[] cost = new int[len0];
int[] newcost = new int[len0];
// initial cost of skipping prefix in String s0
for (int i = 0; i < len0; ++i) {
cost[i] = i;
}
// dynamically computing the array of distances
// transformation cost for each letter in s1
for (int j = 1; j < len1; ++j) {
// initial cost of skipping prefix in String s1
newcost[0] = j;
// transformation cost for each letter in s0
for (int i = 1; i < len0; ++i) {
// matching current letters in both strings
final int match = (s0.charAt(i - 1) == s1.charAt(j - 1)) ? 0 : 1;
// computing cost for each transformation
final int costReplace = cost[i - 1] + match;
final int costInsert = cost[i] + 1;
final int costDelete = newcost[i - 1] + 1;
// keep minimum cost
newcost[i] = Math.min(Math.min(costInsert, costDelete), costReplace);
}
// swap cost/newcost arrays
final int[] swap = cost;
cost = newcost;
newcost = swap;
}
// the distance is the cost for transforming all letters in both strings
return cost[len0 - 1];
}
/**
* Algorithm interface used by cut string functions to provide
* a buffer filler.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@FunctionalInterface
private interface CutStringAlgorithm {
/** Add a line to the output buffer.
*
* @param line the line to add.
*/
void addLine(String line);
}
/**
* Algorithm interface used by split bracket functions to provide
* a buffer filler.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@FunctionalInterface
private interface SplitSeparatorAlgorithm {
/** Add a token to the output buffer.
*
* @param token the token to add.
*/
void addToken(String token);
}
/** Algorithm implementation.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class CutStringToArray implements CutStringAlgorithm {
private final List<String> buffer;
/**
* @param buffer is the buffer to fill.
*/
CutStringToArray(List<String> buffer) {
this.buffer = buffer;
}
@Override
public void addLine(String line) {
this.buffer.add(line);
}
}
/** Algorithm implementation.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class CutStringToString implements CutStringAlgorithm {
private final StringBuilder buffer;
/**
* @param buffer is the buffer to fill.
*/
CutStringToString(StringBuilder buffer) {
this.buffer = buffer;
}
@Override
public void addLine(String line) {
if (this.buffer.length() > 0) {
this.buffer.append('\n');
}
this.buffer.append(line);
}
}
/** Algorithm implementation.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class SplitSeparatorToListAlgorithm implements SplitSeparatorAlgorithm {
private final List<String> list;
/** Construct.
*
* @param tab strings.
*/
SplitSeparatorToListAlgorithm(List<String> tab) {
this.list = tab;
}
@Override
public void addToken(String token) {
this.list.add(token);
}
}
/** Algorithm implementation.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class SplitSeparatorToArrayAlgorithm implements SplitSeparatorAlgorithm {
private static final int BUFFER_SIZE = 20;
private String[] array = new String[BUFFER_SIZE];
private int size;
/** Construct.
*/
SplitSeparatorToArrayAlgorithm() {
//
}
@Override
public void addToken(String token) {
if (this.size >= this.array.length) {
final String[] t = new String[this.array.length + BUFFER_SIZE];
System.arraycopy(this.array, 0, t, 0, this.array.length);
this.array = t;
}
this.array[this.size] = token;
++this.size;
}
/** Replies the array;
*
* @return the array.
*/
public String[] toArray() {
if (this.array.length > this.size) {
final String[] t = new String[this.size];
System.arraycopy(this.array, 0, t, 0, this.size);
return t;
}
return this.array;
}
}
/** Algorithm implementation.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class UUIDSplitSeparatorAlgorithm implements SplitSeparatorAlgorithm {
private final List<UUID> list;
/** Construct.
* @param tab the list.
*/
UUIDSplitSeparatorAlgorithm(List<UUID> tab) {
this.list = tab;
}
@Override
public void addToken(String token) {
this.list.add(UUID.fromString(token));
}
}
/**
* Define the cutting critera of the string.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 4.0
*/
public interface CutStringCritera {
/** Replies if the given word is overfull the line.
*
* @param lineLength is the current length of the line.
* @param word is the word to add.
* @return <code>true</code> if the word is overfulling the line,
* <code>false</code> otherwise.
*/
boolean isOverfull(long lineLength, String word);
/** Replies the length of the given string.
*
* @param str the string.
* @return the length of {@code str}.
*/
long getLengthFor(String str);
/** Replies the character index at which the given string
* may be cut to fit the critera.
*
* @param str the string.
* @return the character index.
*/
int getCutIndexFor(String str);
/** Replies the critera.
*
* @return the critera.
*/
long getCritera();
}
/**
* Define the cutting critera of the string.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 4.0
*/
private static class CutStringColumnCritera implements CutStringCritera {
private final int column;
/** Construct.
*
* @param column the column.
*/
CutStringColumnCritera(int column) {
this.column = column;
}
@Override
public boolean isOverfull(long lineLength, String word) {
return (lineLength + word.length() + 1) > this.column;
}
@Override
public long getLengthFor(String str) {
return str.length();
}
@Override
public int getCutIndexFor(String str) {
if (str.length() > this.column) {
return this.column;
}
return 0;
}
@Override
public long getCritera() {
return this.column;
}
}
}