/******************************************************************************* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you 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.apache.ofbiz.htmlreport.util; | |
import java.util.Collection; | |
import java.util.Iterator; | |
/** | |
* Provides String utility functions.<p> | |
* | |
*/ | |
public final class ReportStringUtil { | |
/** Constant for <code>"false"</code>. */ | |
public static final String FALSE = Boolean.toString(false); | |
/** a convenient shorthand to the line separator constant. */ | |
public static final String LINE_SEPARATOR = System.getProperty("line.separator"); | |
/** Contains all chars that end a sentence in the {@link #trimToSize(String, int, int, String)} method. */ | |
public static final char[] SENTENCE_ENDING_CHARS = {'.', '!', '?'}; | |
/** a convenient shorthand for tabulations. */ | |
public static final String TABULATOR = " "; | |
/** Constant for <code>"true"</code>. */ | |
public static final String TRUE = Boolean.toString(true); | |
/** Day constant. */ | |
private static final long DAYS = 1000 * 60 * 60 * 24; | |
/** Hour constant. */ | |
private static final long HOURS = 1000 * 60 * 60; | |
/** Minute constant. */ | |
private static final long MINUTES = 1000 * 60; | |
/** Second constant. */ | |
private static final long SECONDS = 1000; | |
/** | |
* Default constructor (empty), private because this class has only | |
* static methods.<p> | |
*/ | |
private ReportStringUtil() { | |
// empty | |
} | |
/** | |
* Changes the filename suffix. | |
* | |
* @param filename the filename to be changed | |
* @param suffix the new suffix of the file | |
* | |
* @return the filename with the replaced suffix | |
*/ | |
public static String changeFileNameSuffixTo(String filename, String suffix) { | |
int dotPos = filename.lastIndexOf('.'); | |
if (dotPos != -1) { | |
return filename.substring(0, dotPos + 1) + suffix; | |
} else { | |
// the string has no suffix | |
return filename; | |
} | |
} | |
/** | |
* Returns a string representation for the given collection using the given separator.<p> | |
* | |
* @param collection the collection to print | |
* @param separator the item separator | |
* | |
* @return the string representation for the given collection | |
*/ | |
public static String collectionAsString(Collection<String> collection, String separator) { | |
StringBuffer string = new StringBuffer(128); | |
Iterator<String> it = collection.iterator(); | |
while (it.hasNext()) { | |
string.append(it.next()); | |
if (it.hasNext()) { | |
string.append(separator); | |
} | |
} | |
return string.toString(); | |
} | |
/** | |
* Replaces occurrences of special control characters in the given input with | |
* a HTML representation.<p> | |
* | |
* This method currently replaces line breaks to <code><br/></code> and special HTML chars | |
* like <code>< > & "</code> with their HTML entity representation.<p> | |
* | |
* @param source the String to escape | |
* | |
* @return the escaped String | |
*/ | |
public static String escapeHtml(String source) { | |
if (source == null) { | |
return null; | |
} | |
source = ReportEncoder.escapeXml(source); | |
source = substitute(source, "\r", ""); | |
source = substitute(source, "\n", "<br/>\n"); | |
return source; | |
} | |
/** | |
* Escapes a String so it may be used in JavaScript String definitions.<p> | |
* | |
* This method replaces line breaks, quotation marks and \ characters.<p> | |
* | |
* @param source the String to escape | |
* | |
* @return the escaped String | |
*/ | |
public static String escapeJavaScript(String source) { | |
source = substitute(source, "\\", "\\\\"); | |
source = substitute(source, "\"", "\\\""); | |
source = substitute(source, "\'", "\\\'"); | |
source = substitute(source, "\r\n", "\\n"); | |
source = substitute(source, "\n", "\\n"); | |
return source; | |
} | |
/** | |
* Escapes a String so it may be used as a Perl5 regular expression.<p> | |
* | |
* This method replaces the following characters in a String:<br> | |
* <code>{}[]()\$^.*+/</code><p> | |
* | |
* @param source the string to escape | |
* | |
* @return the escaped string | |
*/ | |
public static String escapePattern(String source) { | |
if (source == null) { | |
return null; | |
} | |
StringBuffer result = new StringBuffer(source.length() * 2); | |
for (int i = 0; i < source.length(); ++i) { | |
char ch = source.charAt(i); | |
switch (ch) { | |
case '\\': | |
result.append("\\\\"); | |
break; | |
case '/': | |
result.append("\\/"); | |
break; | |
case '$': | |
result.append("\\$"); | |
break; | |
case '^': | |
result.append("\\^"); | |
break; | |
case '.': | |
result.append("\\."); | |
break; | |
case '*': | |
result.append("\\*"); | |
break; | |
case '+': | |
result.append("\\+"); | |
break; | |
case '|': | |
result.append("\\|"); | |
break; | |
case '?': | |
result.append("\\?"); | |
break; | |
case '{': | |
result.append("\\{"); | |
break; | |
case '}': | |
result.append("\\}"); | |
break; | |
case '[': | |
result.append("\\["); | |
break; | |
case ']': | |
result.append("\\]"); | |
break; | |
case '(': | |
result.append("\\("); | |
break; | |
case ')': | |
result.append("\\)"); | |
break; | |
default: | |
result.append(ch); | |
} | |
} | |
return new String(result); | |
} | |
/** | |
* Formats a runtime in the format hh:mm:ss, to be used e.g. in reports.<p> | |
* | |
* If the runtime is greater then 24 hours, the format dd:hh:mm:ss is used.<p> | |
* | |
* @param runtime the time to format | |
* | |
* @return the formatted runtime | |
*/ | |
public static String formatRuntime(long runtime) { | |
long seconds = (runtime / SECONDS) % 60; | |
long minutes = (runtime / MINUTES) % 60; | |
long hours = (runtime / HOURS) % 24; | |
long days = runtime / DAYS; | |
StringBuffer strBuf = new StringBuffer(); | |
if (days > 0) { | |
if (days < 10) { | |
strBuf.append('0'); | |
} | |
strBuf.append(days); | |
strBuf.append(':'); | |
} | |
if (hours < 10) { | |
strBuf.append('0'); | |
} | |
strBuf.append(hours); | |
strBuf.append(':'); | |
if (minutes < 10) { | |
strBuf.append('0'); | |
} | |
strBuf.append(minutes); | |
strBuf.append(':'); | |
if (seconds < 10) { | |
strBuf.append('0'); | |
} | |
strBuf.append(seconds); | |
return strBuf.toString(); | |
} | |
/** | |
* Returns <code>true</code> if the provided String is either <code>null</code> | |
* or the empty String <code>""</code>.<p> | |
* | |
* @param value the value to check | |
* | |
* @return true, if the provided value is null or the empty String, false otherwise | |
*/ | |
public static boolean isEmpty(String value) { | |
return (value == null) || (value.length() == 0); | |
} | |
/** | |
* Returns <code>true</code> if the provided String is either <code>null</code> | |
* or contains only white spaces.<p> | |
* | |
* @param value the value to check | |
* | |
* @return true, if the provided value is null or contains only white spaces, false otherwise | |
*/ | |
public static boolean isEmptyOrWhitespaceOnly(String value) { | |
return isEmpty(value) || (value.trim().length() == 0); | |
} | |
/** | |
* Returns <code>true</code> if the provided Objects are either both <code>null</code> | |
* or equal according to {@link Object#equals(Object)}.<p> | |
* | |
* @param value1 the first object to compare | |
* @param value2 the second object to compare | |
* | |
* @return <code>true</code> if the provided Objects are either both <code>null</code> | |
* or equal according to {@link Object#equals(Object)} | |
*/ | |
public static boolean isEqual(Object value1, Object value2) { | |
if (value1 == null) { | |
return (value2 == null); | |
} | |
return value1.equals(value2); | |
} | |
/** | |
* Returns <code>true</code> if the provided String is neither <code>null</code> | |
* nor the empty String <code>""</code>.<p> | |
* | |
* @param value the value to check | |
* | |
* @return true, if the provided value is not null and not the empty String, false otherwise | |
*/ | |
public static boolean isNotEmpty(String value) { | |
return (value != null) && (value.length() != 0); | |
} | |
/** | |
* Returns <code>true</code> if the provided String is neither <code>null</code> | |
* nor contains only white spaces.<p> | |
* | |
* @param value the value to check | |
* | |
* @return <code>true</code>, if the provided value is <code>null</code> | |
* or contains only white spaces, <code>false</code> otherwise | |
*/ | |
public static boolean isNotEmptyOrWhitespaceOnly(String value) { | |
return (value != null) && (value.trim().length() > 0); | |
} | |
/** | |
* Returns the last index of any of the given chars in the given source.<p> | |
* | |
* If no char is found, -1 is returned.<p> | |
* | |
* @param source the source to check | |
* @param chars the chars to find | |
* | |
* @return the last index of any of the given chars in the given source, or -1 | |
*/ | |
public static int lastIndexOf(String source, char[] chars) { | |
// now try to find an "sentence ending" char in the text in the "findPointArea" | |
int result = -1; | |
for (int i = 0; i < chars.length; i++) { | |
int pos = source.lastIndexOf(chars[i]); | |
if (pos > result) { | |
// found new last char | |
result = pos; | |
} | |
} | |
return result; | |
} | |
/** | |
* Returns the last index a whitespace char the given source.<p> | |
* | |
* If no whitespace char is found, -1 is returned.<p> | |
* | |
* @param source the source to check | |
* | |
* @return the last index a whitespace char the given source, or -1 | |
*/ | |
public static int lastWhitespaceIn(String source) { | |
if (isEmpty(source)) { | |
return -1; | |
} | |
int pos = -1; | |
for (int i = source.length() - 1; i >= 0; i--) { | |
if (Character.isWhitespace(source.charAt(i))) { | |
pos = i; | |
break; | |
} | |
} | |
return pos; | |
} | |
/** | |
* Substitutes <code>searchString</code> in the given source String with <code>replaceString</code>.<p> | |
* | |
* This is a high-performance implementation which should be used as a replacement for | |
* <code>{@link String#replaceAll(java.lang.String, java.lang.String)}</code> in case no | |
* regular expression evaluation is required.<p> | |
* | |
* @param source the content which is scanned | |
* @param searchString the String which is searched in content | |
* @param replaceString the String which replaces <code>searchString</code> | |
* | |
* @return the substituted String | |
*/ | |
public static String substitute(String source, String searchString, String replaceString) { | |
if (source == null) { | |
return null; | |
} | |
if (isEmpty(searchString)) { | |
return source; | |
} | |
if (replaceString == null) { | |
replaceString = ""; | |
} | |
int len = source.length(); | |
int sl = searchString.length(); | |
int rl = replaceString.length(); | |
int length; | |
if (sl == rl) { | |
length = len; | |
} else { | |
int c = 0; | |
int s = 0; | |
int e; | |
while ((e = source.indexOf(searchString, s)) != -1) { | |
c++; | |
s = e + sl; | |
} | |
if (c == 0) { | |
return source; | |
} | |
length = len - (c * (sl - rl)); | |
} | |
int s = 0; | |
int e = source.indexOf(searchString, s); | |
if (e == -1) { | |
return source; | |
} | |
StringBuffer sb = new StringBuffer(length); | |
while (e != -1) { | |
sb.append(source.substring(s, e)); | |
sb.append(replaceString); | |
s = e + sl; | |
e = source.indexOf(searchString, s); | |
} | |
e = len; | |
sb.append(source.substring(s, e)); | |
return sb.toString(); | |
} | |
/** | |
* Returns the java String literal for the given String. <p> | |
* | |
* This is the form of the String that had to be written into source code | |
* using the unicode escape sequence for special characters. <p> | |
* | |
* Example: "�" would be transformed to "\\u00C4".<p> | |
* | |
* @param s a string that may contain non-ascii characters | |
* | |
* @return the java unicode escaped string Literal of the given input string | |
*/ | |
public static String toUnicodeLiteral(String s) { | |
StringBuffer result = new StringBuffer(); | |
char[] carr = s.toCharArray(); | |
String unicode; | |
for (int i = 0; i < carr.length; i++) { | |
result.append("\\u"); | |
// append leading zeros | |
unicode = Integer.toHexString(carr[i]).toUpperCase(); | |
for (int j = 4 - unicode.length(); j > 0; j--) { | |
result.append("0"); | |
} | |
result.append(unicode); | |
} | |
return result.toString(); | |
} | |
/** | |
* Returns a substring of the source, which is at most length characters long.<p> | |
* | |
* This is the same as calling {@link #trimToSize(String, int, String)} with the | |
* parameters <code>(source, length, " ...")</code>.<p> | |
* | |
* @param source the string to trim | |
* @param length the maximum length of the string to be returned | |
* | |
* @return a substring of the source, which is at most length characters long | |
*/ | |
public static String trimToSize(String source, int length) { | |
return trimToSize(source, length, length, " ..."); | |
} | |
/** | |
* Returns a substring of the source, which is at most length characters long.<p> | |
* | |
* If a char is cut, the given <code>suffix</code> is appended to the result.<p> | |
* | |
* This is almost the same as calling {@link #trimToSize(String, int, int, String)} with the | |
* parameters <code>(source, length, length*, suffix)</code>. If <code>length</code> | |
* if larger then 100, then <code>length* = length / 2</code>, | |
* otherwise <code>length* = length</code>.<p> | |
* | |
* @param source the string to trim | |
* @param length the maximum length of the string to be returned | |
* @param suffix the suffix to append in case the String was trimmed | |
* | |
* @return a substring of the source, which is at most length characters long | |
*/ | |
public static String trimToSize(String source, int length, String suffix) { | |
int area = (length > 100) ? length / 2 : length; | |
return trimToSize(source, length, area, suffix); | |
} | |
/** | |
* Returns a substring of the source, which is at most length characters long, cut | |
* in the last <code>area</code> chars in the source at a sentence ending char or whitespace.<p> | |
* | |
* If a char is cut, the given <code>suffix</code> is appended to the result.<p> | |
* | |
* @param source the string to trim | |
* @param length the maximum length of the string to be returned | |
* @param area the area at the end of the string in which to find a sentence ender or whitespace | |
* @param suffix the suffix to append in case the String was trimmed | |
* | |
* @return a substring of the source, which is at most length characters long | |
*/ | |
public static String trimToSize(String source, int length, int area, String suffix) { | |
if ((source == null) || (source.length() <= length)) { | |
// no operation is required | |
return source; | |
} | |
if (isEmpty(suffix)) { | |
// we need an empty suffix | |
suffix = ""; | |
} | |
// must remove the length from the after sequence chars since these are always added in the end | |
int modLength = length - suffix.length(); | |
if (modLength <= 0) { | |
// we are to short, return beginning of the suffix | |
return suffix.substring(0, length); | |
} | |
int modArea = area + suffix.length(); | |
if ((modArea > modLength) || (modArea < 0)) { | |
// area must not be longer then max length | |
modArea = modLength; | |
} | |
// first reduce the String to the maximum allowed length | |
String findPointSource = source.substring(modLength - modArea, modLength); | |
String result; | |
// try to find an "sentence ending" char in the text | |
int pos = lastIndexOf(findPointSource, SENTENCE_ENDING_CHARS); | |
if (pos >= 0) { | |
// found a sentence ender in the lookup area, keep the sentence ender | |
result = source.substring(0, modLength - modArea + pos + 1) + suffix; | |
} else { | |
// no sentence ender was found, try to find a whitespace | |
pos = lastWhitespaceIn(findPointSource); | |
if (pos >= 0) { | |
// found a whitespace, don't keep the whitespace | |
result = source.substring(0, modLength - modArea + pos) + suffix; | |
} else { | |
// not even a whitespace was found, just cut away what's to long | |
result = source.substring(0, modLength) + suffix; | |
} | |
} | |
return result; | |
} | |
} |