/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.util;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringEscapeUtils;
import org.olat.core.id.Identity;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.filter.impl.NekoHTMLScanner;
import org.olat.core.util.filter.impl.OWASPAntiSamyXSSFilter;
import com.thoughtworks.xstream.core.util.Base64Encoder;
/**
* enclosing_type Description: <br>
* helper class for formating Strings (not locale specific)
*
* @author Felix Jost
*/
public class StringHelper {
private static final OLog log = Tracing.createLoggerFor(StringHelper.class);
private static final NumberFormat numFormatter;
private static final String WHITESPACE_REGEXP = "^\\s*$";
private static final Pattern WHITESPACE_PATTERN = Pattern.compile(WHITESPACE_REGEXP);
private static final int LONG_MAX_LENGTH = Long.toString(Long.MAX_VALUE).length();
/**
* regex for not allowing
* <code>;,:</code> <code>ALL_WITHOUT_COMMA_2POINT_STRPNT</code>
*/
public static final String ALL_WITHOUT_COMMA_2POINT_STRPNT = "^[^,;:]*$";
private static final Pattern ALL_WITHOUT_COMMA_2POINT_STRPNT_PATTERN = Pattern.compile(ALL_WITHOUT_COMMA_2POINT_STRPNT);
private static final String X_MAC_ENC = "x-mac-";
private static final String MAC_ENC = "mac";
static {
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setDecimalSeparator('.');
numFormatter = new DecimalFormat("#.#", dfs);
}
/**
* unused
*
* @param in
* @param delim
* @return List
*/
public static List<String> getParts(String in, String delim) {
List<String> li = new ArrayList<String>();
String part;
int delimlen = delim.length();
int oldpos = 0;
int k;
while ((k = in.indexOf(delim, oldpos)) != -1) {
part = in.substring(oldpos, k);
li.add(part);
oldpos = k + delimlen;
}
if (oldpos != 0) { // min. ein Trennzeichen -> nimm rest
part = in.substring(oldpos);
li.add(part);
}
return li;
}
/**
* @param date
* @param locale
* @return formatted date
*/
public static String formatLocaleDate(long date, Locale locale) {
if (date == -1) return "-";
return DateFormat.getDateInstance(DateFormat.SHORT, locale).format(new Date(date));
}
/**
* @param date
* @param locale
* @return formatted date
*/
public static String formatLocaleDateFull(long date, Locale locale) {
if (date == -1) return "-";
return DateFormat.getDateInstance(DateFormat.FULL, locale).format(new Date(date));
}
/**
*
* @param date
* @param locale
* @return Formatted date
*/
public static String formatLocaleDateFull(Date date, Locale locale) {
if (date == null) return "-";
return DateFormat.getDateInstance(DateFormat.FULL, locale).format(date);
}
/**
* @param date
* @param locale
* @return formatted date/time
*/
public static String formatLocaleDateTime(long date, Locale locale) {
if (date == -1) return "-";
return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale).format(new Date(date));
}
/**
* @param time
* @param locale
* @return formatted time
*/
public static String formatLocaleTime(long time, Locale locale) {
if (time == -1) return "-";
return DateFormat.getTimeInstance(DateFormat.SHORT, locale).format(new Date(time));
}
/**
*
* @param time
* @param locale
* @return
*/
public static String formatLocaleTime(Date time, Locale locale) {
if (time == null) return "-";
return DateFormat.getTimeInstance(DateFormat.SHORT, locale).format(time);
}
/**
* @param f
* @param fractionDigits
* @return formatted float
*/
public static String formatFloat(float f, int fractionDigits) {
numFormatter.setMaximumFractionDigits(fractionDigits);
return numFormatter.format(f);
}
/**
* @param url
* @return encoded string
*/
public static String urlEncodeUTF8(String url) {
String encodedURL;
try {
encodedURL = URLEncoder.encode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
/*
* from java.nio.Charset Standard charsets Every implementation of the
* Java platform is required to support the following standard charsets...
* ... UTF-8 Eight-bit UCS Transformation Format ...
*/
throw new AssertException("utf-8 encoding is needed for proper encoding, but not offered on this java platform????");
}
return encodedURL;
}
/**
* Encode the string into a Base64 string.
*
* @param unencoded The string to encode
* @return The encoded string
*/
public static String encodeBase64(String unencoded) {
return new Base64Encoder().encode(unencoded.getBytes());
}
/**
* Encode the string into a Base64 string.
*
* @param unencoded The bytes to encode
* @return The encoded string
*/
public static String encodeBase64(byte[] unencoded) {
return new Base64Encoder().encode(unencoded);
}
/**
* Decode a Base 64 string.
*
* @param encoded The string to decode
* @return The decoded string
*/
public static String decodeBase64(String encoded) {
byte[] decoded = new Base64Encoder().decode(encoded);
return new String(decoded);
}
/**
* Converts all keys of a hash map to a string array.
*
* @param m The (hash) map with the key and values
* @return The string array containing all keys for this map
*/
public static String[] getMapKeysAsStringArray(Map<String,?> m) {
return m.keySet().toArray(new String[m.size()]);
}
/**
* Converts all values of a hash map to a string array.
*
* @param m The (hash) map with the key and values
* @return The string array containing all values for this map
*/
public static String[] getMapValuesAsStringArray(Map<?,String> m) {
return m.values().toArray(new String[m.size()]);
}
/**
* matches any but ^[^,;:]*$
*
* @param s
* @return true if does not match regexp
*/
public static boolean containsNoneOfCoDouSemi(String s) {
if (s == null) return false;
Matcher m = ALL_WITHOUT_COMMA_2POINT_STRPNT_PATTERN.matcher(s);
return m.find();
}
/**
* Checks if a string has anything in it to display. Will return true if the
* string is not null and does contain at least one none-whitespace character.
*
* @param s The String to be evaluated
* @return true if the string contains any non-whitespace character, false
* otherwhise
*/
public static boolean containsNonWhitespace(String s) {
if (s == null || s.length() == 0) return false;
char firstChar = s.charAt(0);
if(firstChar > 32 && firstChar < 127) {
return true;
}
Matcher matcher = WHITESPACE_PATTERN.matcher(s);
// if string matches whitespace pattern then string does not
// contain non-whitespace
return !matcher.find();
}
public static boolean isSame(String s1, String s2) {
if(s1 == null && s2 == null) {
return true;
}
if(s1 == null || s2 == null) {
return false;
}
return s1.equals(s2);
}
public static boolean isSame(Object s1, Object s2) {
if(s1 == null && s2 == null) {
return true;
}
if(s1 == null || s2 == null) {
return false;
}
return s1.equals(s2);
}
public static boolean isSame(boolean s1, boolean s2) {
return s1 == s2;
}
/**
* Check if the string contains some HTML tags
* @param s
* @return
*/
public static boolean isHtml(String s) {
if (s == null) return false;
boolean containsHtml = new NekoHTMLScanner().scan(s);
return containsHtml;
}
/**
* takes an array of Identies and converts them to a String containing the
* Identity-Emails separated by a <b>, </b>. The returned String can be fed
* directly to the e-mailer helper as the e-mail to field. <br>
* <ul>
* <li>Entries in the parameter emailRecipientIdentites are expected to be
* not null.</li>
* </ul>
*
* @param emailRecipientIdentities
* @return "email1, email2, email3," or null if emailRecipientIdentites was
* null
*/
public static String formatIdentitesAsEmailToString(final Identity[] emailRecipientIdentities) {
int elCnt = emailRecipientIdentities.length;
//2..n recipients
StringBuilder tmpDET = new StringBuilder();
for (int i = 0; i < elCnt; i++) {
tmpDET.append(emailRecipientIdentities[i].getUser().getProperty(UserConstants.EMAIL, null));
if (i < elCnt - 1) {
tmpDET.append(", ");
}
}
return tmpDET.toString();
}
/**
* takes a List containing email Strings and converts them to a String
* containing the Email Strings separated by a <b>, </b>. The returned String
* can be fed directly to the e-mailer helper as the e-mail to field. <br>
* <ul>
* <li>Entries in the parameter emailRecipients are expected to be not null
* and of Type String.</li>
* </ul>
*
* @param emailRecipients
* @param delimiter
* @return "email1, email2, email3," or null if emailRecipientIdentites was
* null
*/
public static String formatIdentitesAsEmailToString(final List<String> emailRecipients, String delimiter) {
int elCnt = emailRecipients.size();
//2..n recipients
StringBuilder tmpDET = new StringBuilder();
for (int i = 0; i < elCnt; i++) {
tmpDET.append(emailRecipients.get(i));
if (i < elCnt - 1) {
tmpDET.append(delimiter);
}
}
return tmpDET.toString();
}
public static final String escapeHtml(String str) {
return StringEscapeUtils.escapeHtml(str);
}
public static final void escapeHtml(Writer writer, String str) {
try {
StringEscapeUtils.escapeHtml(writer, str);
} catch (IOException e) {
log.error("Error escaping HTML", e);
}
}
public static final String xssScan(String str) {
return new OWASPAntiSamyXSSFilter().filter(str);
}
public static final String xssScan(StringBuilder str) {
if(str == null) return null;
if(str.length() == 0) return "";
return new OWASPAntiSamyXSSFilter().filter(str.toString());
}
public static final boolean xssScanForErrors(String str) {
OWASPAntiSamyXSSFilter filter = new OWASPAntiSamyXSSFilter();
filter.filter(str);
return filter.getNumOfErrors() > 0;
}
public static final String escapeJavaScript(String str) {
return StringEscapeUtils.escapeJavaScript(str);
}
public static final void escapeJavaScript(Writer writer, String str) {
try {
StringEscapeUtils.escapeJavaScript(writer, str);
} catch (IOException e) {
log.error("Error escaping JavaScript", e);
}
}
/**
* @param cellValue
* @return stripped string
*/
public static String stripLineBreaks(String cellValue) {
cellValue = cellValue.replace('\n', ' ');
cellValue = cellValue.replace('\r', ' ');
return cellValue;
}
/**
* transforms a displayname to a name that causes no problems on the filesystem
* (e.g. Webclass Energie 2004/2005 -> Webclass_Energie_2004_2005)
*
* @param s
* @return transformed string
*/
public static String transformDisplayNameToFileSystemName(String s){
//replace some separator with an underscore
s = s.replace('?', '_').replace('\\', '_').replace('/', '_').replace(' ', '_');
//remove all non-ascii characters
return FileUtils.normalizeFilename(s);
}
/**
* The method do only a precheck if the string can be a number. It's goal
* is to prevent to generate hunderds of exceptions in a loop by using
* the Long.parseLong() method (exceptions is time and CPU intensive).
*
* return True if the string can be a digit (there is not boundaries check)
*/
public static boolean isLong(String string) {
if(string == null || string.length() == 0) {
return false;
}
int stop = string.startsWith("-") ? 1 : 0;
if(string.length() > LONG_MAX_LENGTH + stop) {
return false;
}
char[] charArr = string.toCharArray();
for(int i=charArr.length; i-->stop; ) {
char ch = charArr[i];
if(ch < 48 || ch > 57) {
return false;
}
}
return true;
}
public static String cleanUTF8ForXml(String string) {
if(string == null) return null;
if(string.length() == 0) return string;
StringBuilder sb = new StringBuilder();
char[] charArr = string.toCharArray();
int numOfCharacters = charArr.length;
for(int i=0; i<numOfCharacters; i++) {
char ch = charArr[i];
if(ch < 32) {
switch(ch) {
case '\n': sb.append(ch); break;//0x000A
case '\t': sb.append(ch); break;//0x0009
case '\r': sb.append(ch); break;//0x000D
}
} else if(ch >= 0x0020 && ch <= 0xD7FF) {
sb.append(ch);
} else if(ch >= 0xE000 && ch <= 0xFFFD) {
sb.append(ch);
} else if(ch >= 0x10000 && ch <= 0x10FFFF) {
sb.append(ch);
}
}
return sb.toString();
}
public static String replaceAllCaseInsensitive(String expression, String name, String replacement) {
if(!StringHelper.containsNonWhitespace(expression)) {
return expression;
}
String lcExpresion = expression.toLowerCase();
String lcName = name.toLowerCase();
int index = 0;
while((index = lcExpresion.indexOf(lcName, index)) >= 0) {
int startIndex = index;
int stopIndex = index + lcName.length();
String newExpression = expression.substring(0, startIndex);
newExpression += replacement;
newExpression += expression.substring(stopIndex);
expression = newExpression;
lcExpresion = expression.toLowerCase();
index = startIndex + replacement.length();
}
return expression;
}
/**
* @param extractedCharset
* @return
*/
public static String check4xMacRoman(String extractedCharset) {
//OLAT-1844
//TODO:pb: why do http encoding names not match java encoding names?
//the encoding name 'x-mac-roman' must be translated to javas 'x-MacRoman'
//but it must be x-mac-roman for browser and htmleditor.. weird naming problem.
if(extractedCharset == null) return null;
if(extractedCharset.toLowerCase().startsWith(X_MAC_ENC)) {
String tmp = extractedCharset.substring(6);
String first = tmp.substring(0, 1);
tmp = tmp.substring(1);
//e.g. convert 'x-mac-roman' to 'x-MacRoman'
extractedCharset = "x-Mac"+first.toUpperCase()+tmp;
return extractedCharset;
} else if (extractedCharset.toLowerCase().startsWith(MAC_ENC)) {
//word for macintosh creates charset=macintosh which java does not know, load with iso-8859-1
return "iso-8859-1";
}
return extractedCharset;
}
/**
* set of strings to one string comma separated.<br>
* e.g. ["a","b","c","s"] -> "a,b,c,s"
* @param selection
* @return
*/
public static String formatAsCSVString(Set<String> entries) {
boolean isFirst = true;
String csvStr = null;
for (Iterator<String> iter = entries.iterator(); iter.hasNext();) {
String group = iter.next();
if (isFirst) {
csvStr = group;
isFirst = false;
} else {
csvStr += ", " + group;
}
}
return csvStr;
}
/**
* list of strings to one string comma separated.<br>
* e.g. ["a","b","c","s"] -> "a,b,c,s"
* @param selection
* @return
*/
public static String formatAsCSVString(List<String> entries) {
boolean isFirst = true;
String csvStr = null;
for (Iterator<String> iter = entries.iterator(); iter.hasNext();) {
String group = iter.next();
if (isFirst) {
csvStr = group;
isFirst = false;
} else {
csvStr += ", " + group;
}
}
return csvStr;
}
/**
* list of strings to one string comma separated.<br>
* e.g. ["z","a","b","c","s","a"] -> "a, b, c, s, z"
* No duplicates, alphabetically sorted
* @param selection
* @return
*/
public static String formatAsSortUniqCSVString(List<String> s) {
Map <String,String>u = new HashMap<String,String>();
for (Iterator <String> si = s.iterator(); si.hasNext();) {
u.put(si.next().trim(), null);
}
List <String>rv = new ArrayList<String>();
rv.addAll(u.keySet());
rv.remove("");
Collections.sort(rv);
return formatAsCSVString (rv);
}
/**
* list of strings to one string comma separated.<br>
* e.g. ["z","a","b","c","s","a"] -> "a, b, c, s, z"
* No duplicates, alphabetically sorted
* @param selection
* @return
*/
public static String formatAsSortUniqCSVString(Set<String> s) {
Map <String,String>u = new HashMap<String,String>();
for (Iterator <String> si = s.iterator(); si.hasNext();) {
u.put(si.next().trim(), null);
}
List <String>rv = new ArrayList<String>();
rv.addAll(u.keySet());
rv.remove("");
Collections.sort(rv);
return formatAsCSVString (rv);
}
}