org.htmlunit.util.StringUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xlt Show documentation
Show all versions of xlt Show documentation
XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.
/*
* Copyright (c) 2002-2024 Gargoyle Software Inc.
*
* 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
* https://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.htmlunit.util;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.htmlunit.html.impl.Color;
/**
* String utilities class for utility functions not covered by third party libraries.
*
* @author Daniel Gredler
* @author Ahmed Ashour
* @author Martin Tamme
* @author Ronald Brill
*/
public final class StringUtils {
private static final Pattern HEX_COLOR = Pattern.compile("#([\\da-fA-F]{3}|[\\da-fA-F]{6})");
private static final Pattern RGB_COLOR =
Pattern.compile("rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%?\\s*,"
+ "\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%?\\s*,"
+ "\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%?\\s*\\)");
private static final Pattern RGBA_COLOR =
Pattern.compile("rgba\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%?\\s*,"
+ "\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%?\\s*,"
+ "\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%?\\s*,"
+ "\\s*((0?.[1-9])|[01])\\s*\\)");
private static final Pattern HSL_COLOR =
Pattern.compile("hsl\\(\\s*((0|[1-9]\\d?|[12]\\d\\d?|3[0-5]\\d)(.\\d*)?)\\s*,"
+ "\\s*((0|[1-9]\\d?|100)(.\\d*)?)%\\s*,"
+ "\\s*((0|[1-9]\\d?|100)(.\\d*)?)%\\s*\\)");
private static final Pattern ILLEGAL_FILE_NAME_CHARS = Pattern.compile("\\\\|/|\\||:|\\?|\\*|\"|<|>|\\p{Cntrl}");
private static final Map CamelizeCache_ = new ConcurrentHashMap<>();
/**
* Disallow instantiation of this class.
*/
private StringUtils() {
// Empty.
}
/**
* Escapes the characters '<', '>' and '&' into their XML entity equivalents. Note that
* sometimes we have to use this method instead of
* {@link org.apache.commons.lang3.StringEscapeUtils#escapeXml(String)} or
* {@link org.apache.commons.lang3.StringEscapeUtils#escapeHtml4(String)} because those methods
* escape some unicode characters as well.
*
* @param s the string to escape
* @return the escaped form of the specified string
*/
public static String escapeXmlChars(final String s) {
return org.apache.commons.lang3.StringUtils.
replaceEach(s, new String[] {"&", "<", ">"}, new String[] {"&", "<", ">"});
}
/**
* Escape the string to be used as attribute value.
* Only {@code <}, {@code &} and {@code "} have to be escaped (see
* http://www.w3.org/TR/REC-xml/#d0e888).
* @param attValue the attribute value
* @return the escaped value
*/
public static String escapeXmlAttributeValue(final String attValue) {
final int len = attValue.length();
StringBuilder sb = null;
for (int i = len - 1; i >= 0; --i) {
final char c = attValue.charAt(i);
String replacement = null;
if (c == '<') {
replacement = "<";
}
else if (c == '&') {
replacement = "&";
}
else if (c == '\"') {
replacement = """;
}
if (replacement != null) {
if (sb == null) {
sb = new StringBuilder(attValue);
}
sb.replace(i, i + 1, replacement);
}
}
if (sb != null) {
return sb.toString();
}
return attValue;
}
/**
* Returns the index within the specified string of the first occurrence of
* the specified search character.
*
* @param s the string to search
* @param searchChar the character to search for
* @param beginIndex the index at which to start the search
* @param endIndex the index at which to stop the search
* @return the index of the first occurrence of the character in the string or -1
*/
public static int indexOf(final String s, final char searchChar, final int beginIndex, final int endIndex) {
for (int i = beginIndex; i < endIndex; i++) {
if (s.charAt(i) == searchChar) {
return i;
}
}
return -1;
}
/**
* Returns a Color parsed from the given RGB in hexadecimal notation.
* @param token the token to parse
* @return a Color whether the token is a color RGB in hexadecimal notation; otherwise null
*/
public static Color asColorHexadecimal(final String token) {
if (token == null) {
return null;
}
final Matcher tmpMatcher = HEX_COLOR.matcher(token);
final boolean tmpFound = tmpMatcher.matches();
if (!tmpFound) {
return null;
}
final String tmpHex = tmpMatcher.group(1);
if (tmpHex.length() == 6) {
final int tmpRed = Integer.parseInt(tmpHex.substring(0, 2), 16);
final int tmpGreen = Integer.parseInt(tmpHex.substring(2, 4), 16);
final int tmpBlue = Integer.parseInt(tmpHex.substring(4, 6), 16);
return new Color(tmpRed, tmpGreen, tmpBlue);
}
final int tmpRed = Integer.parseInt(tmpHex.substring(0, 1) + tmpHex.substring(0, 1), 16);
final int tmpGreen = Integer.parseInt(tmpHex.substring(1, 2) + tmpHex.substring(1, 2), 16);
final int tmpBlue = Integer.parseInt(tmpHex.substring(2, 3) + tmpHex.substring(2, 3), 16);
return new Color(tmpRed, tmpGreen, tmpBlue);
}
/**
* Returns a Color parsed from the given rgb notation if found inside the given string.
* @param token the token to parse
* @return a Color whether the token contains a color in RGB notation; otherwise null
*/
public static Color findColorRGB(final String token) {
if (token == null) {
return null;
}
final Matcher tmpMatcher = RGB_COLOR.matcher(token);
if (!tmpMatcher.find()) {
return null;
}
final int tmpRed = Integer.parseInt(tmpMatcher.group(1));
final int tmpGreen = Integer.parseInt(tmpMatcher.group(2));
final int tmpBlue = Integer.parseInt(tmpMatcher.group(3));
return new Color(tmpRed, tmpGreen, tmpBlue);
}
/**
* Returns a Color parsed from the given rgb notation.
* @param token the token to parse
* @return a Color whether the token is a color in RGB notation; otherwise null
*/
public static Color findColorRGBA(final String token) {
if (token == null) {
return null;
}
final Matcher tmpMatcher = RGBA_COLOR.matcher(token);
if (!tmpMatcher.find()) {
return null;
}
final int tmpRed = Integer.parseInt(tmpMatcher.group(1));
final int tmpGreen = Integer.parseInt(tmpMatcher.group(2));
final int tmpBlue = Integer.parseInt(tmpMatcher.group(3));
final int tmpAlpha = (int) (Float.parseFloat(tmpMatcher.group(4)) * 255);
return new Color(tmpRed, tmpGreen, tmpBlue, tmpAlpha);
}
/**
* Returns a Color parsed from the given hsl notation if found inside the given string.
* @param token the token to parse
* @return a Color whether the token contains a color in RGB notation; otherwise null
*/
public static Color findColorHSL(final String token) {
if (token == null) {
return null;
}
final Matcher tmpMatcher = HSL_COLOR.matcher(token);
if (!tmpMatcher.find()) {
return null;
}
final float tmpHue = Float.parseFloat(tmpMatcher.group(1)) / 360f;
final float tmpSaturation = Float.parseFloat(tmpMatcher.group(4)) / 100f;
final float tmpLightness = Float.parseFloat(tmpMatcher.group(7)) / 100f;
return hslToRgb(tmpHue, tmpSaturation, tmpLightness);
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1]
*
* @param h the hue
* @param s the saturation
* @param l the lightness
* @return {@link Color}
*/
private static Color hslToRgb(final float h, final float s, final float l) {
if (s == 0f) {
return new Color(to255(l), to255(l), to255(l));
}
final float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
final float p = 2 * l - q;
final float r = hueToRgb(p, q, h + 1f / 3f);
final float g = hueToRgb(p, q, h);
final float b = hueToRgb(p, q, h - 1f / 3f);
return new Color(to255(r), to255(g), to255(b));
}
private static float hueToRgb(final float p, final float q, float t) {
if (t < 0f) {
t += 1f;
}
if (t > 1f) {
t -= 1f;
}
if (t < 1f / 6f) {
return p + (q - p) * 6f * t;
}
if (t < 1f / 2f) {
return q;
}
if (t < 2f / 3f) {
return p + (q - p) * (2f / 3f - t) * 6f;
}
return p;
}
private static int to255(final float value) {
return (int) Math.min(255, 256 * value);
}
/**
* Formats the specified color.
*
* @param color the color to format
* @return the specified color, formatted
*/
public static String formatColor(final Color color) {
return "rgb(" + color.getRed() + ", " + color.getGreen() + ", " + color.getBlue() + ")";
}
/**
* Sanitize a string for use in Matcher.appendReplacement.
* Replaces all \ with \\ and $ as \$ because they are used as control
* characters in appendReplacement.
*
* @param toSanitize the string to sanitize
* @return sanitized version of the given string
*/
public static String sanitizeForAppendReplacement(final String toSanitize) {
return org.apache.commons.lang3.StringUtils.replaceEach(toSanitize,
new String[] {"\\", "$"}, new String[]{"\\\\", "\\$"});
}
/**
* Sanitizes a string for use as filename.
* Replaces \, /, |, :, ?, *, ", <, >, control chars by _ (underscore).
*
* @param toSanitize the string to sanitize
* @return sanitized version of the given string
*/
public static String sanitizeForFileName(final String toSanitize) {
return ILLEGAL_FILE_NAME_CHARS.matcher(toSanitize).replaceAll("_");
}
/**
* Transforms the specified string from delimiter-separated (e.g. font-size
)
* to camel-cased (e.g. fontSize
).
* @param string the string to camelize
* @return the transformed string
*/
public static String cssCamelize(final String string) {
if (string == null) {
return null;
}
String result = CamelizeCache_.get(string);
if (null != result) {
return result;
}
// not found in CamelizeCache_; convert and store in cache
final int pos = string.indexOf('-');
if (pos == -1 || pos == string.length() - 1) {
// cache also this strings for performance
CamelizeCache_.put(string, string);
return string;
}
final StringBuilder builder = new StringBuilder(string);
builder.deleteCharAt(pos);
builder.setCharAt(pos, Character.toUpperCase(builder.charAt(pos)));
int i = pos + 1;
while (i < builder.length() - 1) {
if (builder.charAt(i) == '-') {
builder.deleteCharAt(i);
builder.setCharAt(i, Character.toUpperCase(builder.charAt(i)));
}
i++;
}
result = builder.toString();
CamelizeCache_.put(string, result);
return result;
}
/**
* Lowercases a string by checking and check for null first. There
* is no cache involved and the ROOT locale is used to convert it.
*
* @param s the string to lowercase
* @return the lowercased string
*/
public static String toRootLowerCase(final String s) {
return s == null ? null : s.toLowerCase(Locale.ROOT);
}
/**
* Transforms the specified string from camel-cased (e.g. fontSize
)
* to delimiter-separated (e.g. font-size
).
* to camel-cased .
* @param string the string to decamelize
* @return the transformed string
*/
public static String cssDeCamelize(final String string) {
if (string == null || string.isEmpty()) {
return string;
}
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < string.length(); i++) {
final char ch = string.charAt(i);
if (Character.isUpperCase(ch)) {
builder.append('-').append(Character.toLowerCase(ch));
}
else {
builder.append(ch);
}
}
return builder.toString();
}
/**
* Converts a string into a byte array using the specified encoding.
*
* @param charset the charset
* @param content the string to convert
* @return the String as a byte[]; if the specified encoding is not supported an empty byte[] will be returned
*/
public static byte[] toByteArray(final String content, final Charset charset) {
if (content == null || content.isEmpty()) {
return new byte[0];
}
return content.getBytes(charset);
}
/**
* Splits the provided text into an array, using whitespace as the
* separator.
* Whitespace is defined by {@link Character#isWhitespace(char)}.
*
* @param str the String to parse, may be null
* @return an array of parsed Strings, an empty array if null String input
*/
public static String[] splitAtJavaWhitespace(final String str) {
final String[] parts = org.apache.commons.lang3.StringUtils.split(str);
if (parts == null) {
return new String[0];
}
return parts;
}
/**
* Splits the provided text into an array, using blank as the
* separator.
*
* @param str the String to parse, may be null
* @return an array of parsed Strings, an empty array if null String input
*/
public static String[] splitAtBlank(final String str) {
final String[] parts = org.apache.commons.lang3.StringUtils.split(str, ' ');
if (parts == null) {
return new String[0];
}
return parts;
}
/**
* Splits the provided text into an array, using blank as the
* separator.
*
* @param str the String to parse, may be null
* @return an array of parsed Strings, an empty array if null String input
*/
public static String[] splitAtComma(final String str) {
final String[] parts = org.apache.commons.lang3.StringUtils.split(str, ',');
if (parts == null) {
return new String[0];
}
return parts;
}
/**
* Splits the provided text into an array, using comma or blank as the
* separator.
*
* @param str the String to parse, may be null
* @return an array of parsed Strings, an empty array if null String input
*/
public static String[] splitAtCommaOrBlank(final String str) {
final String[] parts = org.apache.commons.lang3.StringUtils.split(str, ", ");
if (parts == null) {
return new String[0];
}
return parts;
}
}