package org.basex.util; import java.net.*; import java.nio.charset.*; import java.security.*; import java.util.*; import org.basex.core.*; import org.basex.util.list.*; /** * <p>This class provides convenience operations for strings.</p> * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ public final class Strings { /** UTF8 encoding string. */ public static final String UTF8 = "UTF-8"; /** UTF16 encoding string. */ public static final String UTF16 = "UTF-16"; /** UTF16BE encoding string. */ public static final String UTF16BE = "UTF-16BE"; /** UTF16 encoding string. */ public static final String UTF16LE = "UTF-16LE"; /** UTF16 encoding string. */ public static final String UTF32 = "UTF-32"; /** ISO-8859-1 encoding string. */ public static final String ISO88591 = "ISO-8859-1"; /** UTF8 encoding strings. */ private static final String[] ALL_UTF8 = { UTF8, "UTF8" }; /** UTF16-LE encoding strings. */ private static final String[] ALL_UTF16 = { UTF16, "UTF16" }; /** UTF32 encoding strings. */ private static final String[] ALL_UTF32 = { UTF32, "UTF32" }; /** Hidden constructor. */ private Strings() { } /** * Converts the specified string into an long value. * {@link Long#MIN_VALUE} is returned if the input is invalid. * @param string string to be converted * @return resulting long value */ public static long toLong(final String string) { return Token.toLong(Token.token(string)); } /** * Converts the specified string into an integer value. * {@link Integer#MIN_VALUE} is returned if the input is invalid. * @param string string to be converted * @return resulting integer value */ public static int toInt(final String string) { return Token.toInt(Token.token(string)); } /** * Compares two strings for equality. The arguments may be {@code null}. * @param str1 first string * @param str2 strings to be compared * @return true if one test is successful */ public static boolean eq(final String str1, final String str2) { return str1 == null ? str2 == null : str1.equals(str2); } /** * Compares several strings for equality. The arguments may be {@code null}. * @param str first string * @param strings strings to be compared * @return true if one test is successful */ public static boolean eq(final String str, final String... strings) { for(final String s : strings) { if(str == null ? s == null : str.equals(s)) return true; } return false; } /** * Compares several strings for equality, ignoring the case. * @param str first string * @param strings strings to be compared * @return true if one test is successful */ public static boolean eqic(final String str, final String... strings) { for(final String s : strings) { if(str == null ? s == null : str.equalsIgnoreCase(s)) return true; } return false; } /** * Splits a string around matches of the given separator. * @param string string to be split * @param sep separation character * @return resulting strings */ public static String[] split(final String string, final char sep) { return split(string, sep, Integer.MAX_VALUE); } /** * Splits a string around matches of the given separator. * @param string string to be split * @param sep separation character * @param limit maximum number of strings (must be 1 or larger) * @return resulting strings */ public static String[] split(final String string, final char sep, final int limit) { final StringList sl = new StringList(limit == Integer.MAX_VALUE ? Array.CAPACITY : limit); final int tl = string.length(); int s = 0, c = 1; for(int p = 0; p < tl && c < limit; p++) { if(string.charAt(p) == sep) { sl.add(string.substring(s, p)); s = p + 1; c++; } } return sl.add(string.substring(s, tl)).finish(); } /** * Joins strings. * @param strings string to be joined * @param sep separation string * @return resulting string */ public static String join(final String[] strings, final String sep) { final StringBuilder sb = new StringBuilder(); for(final String string : strings) { if(sb.length() != 0) sb.append(sep); sb.append(string); } return sb.toString(); } /** * Deletes a character from the strings. * @param string string * @param ch character to be removed * @return resulting token */ public static String delete(final String string, final char ch) { if(!contains(string, ch)) return string; final int tl = string.length(); final StringBuilder sb = new StringBuilder(tl - 1); for(int p = 0; p < tl; p++) { final char c = string.charAt(p); if(c != ch) sb.append(c); } return sb.toString(); } /** * Checks if a character occurs in a strings. * @param string string * @param ch character to be found * @return result of check */ public static boolean contains(final String string, final char ch) { return string.indexOf(ch) != -1; } /** * Returns an MD5 hash in lower case. * @param string string to be hashed * @return md5 hash */ public static String md5(final String string) { return hash(string, "MD5"); } /** * Returns an SHA256 hash in lower case. * @param string string to be hashed * @return sha256 hash */ public static String sha256(final String string) { return hash(string, "SHA-256"); } /** * Returns a hash in lower case. * @param string string to be hashed * @param algo hashing algorithm * @return hash */ private static String hash(final String string, final String algo) { try { final MessageDigest md = MessageDigest.getInstance(algo); return Token.string(Token.hex(md.digest(Token.token(string)), false)); } catch(final Exception ex) { throw Util.notExpected(ex); } } /** * Returns a unified representation of the specified encoding. * @param encoding input encoding (UTF-8 is returned for a {@code null} reference) * @return encoding */ public static String normEncoding(final String encoding) { return normEncoding(encoding, false); } /** * Returns a unified representation of the specified encoding. * @param encoding input encoding (UTF-8 is returned for a {@code null} reference) * @param utf16 normalize UTF-16 encoding * @return encoding */ public static String normEncoding(final String encoding, final boolean utf16) { if(encoding == null) return UTF8; final String e = encoding.toUpperCase(Locale.ENGLISH); if(eq(e, ALL_UTF8)) return UTF8; if(e.equals(UTF16LE)) return UTF16LE; if(e.equals(UTF16BE)) return UTF16BE; if(eq(e, ALL_UTF16)) return utf16 ? UTF16BE : UTF16; if(eq(e, ALL_UTF32)) return UTF32; return encoding; } /** * Checks if the specified encoding is supported. * @param encoding encoding * @return result of check */ public static boolean supported(final String encoding) { try { return Charset.isSupported(encoding); } catch(final IllegalArgumentException ex) { return false; } } /** * Checks if the specified string is "yes", "true", "on" or "1". * @param string string to be checked * @return result of check */ public static boolean yes(final String string) { return eqic(string, Text.TRUE, Text.YES, Text.ON, "1"); } /** * Capitalizes the first letter of a string. * @param string input string * @return capitalized string */ public static String capitalize(final String string) { final StringBuilder sb = new StringBuilder(); if(!string.isEmpty()) sb.append(Character.toUpperCase(string.charAt(0))).append(string.substring(1)); return sb.toString(); } /** * Converts the given string to a Java class name. Slashes will be replaced with dots, and * the last package segment will be capitalized and camel-cased. * @param string string to convert * @return class name */ public static String className(final String string) { final String s = string.replace('/', '.'); final int c = s.lastIndexOf('.') + 1; return s.substring(0, c) + capitalize(camelCase(s.substring(c))); } /** * Converts the given string to camel case. * @param string string to convert * @return resulting string */ public static String camelCase(final String string) { final StringBuilder sb = new StringBuilder(); boolean upper = false; final int sl = string.length(); for(int s = 0; s < sl; s++) { final char ch = string.charAt(s); if(ch == '-') { upper = true; } else if(upper) { sb.append(Character.toUpperCase(ch)); upper = false; } else { sb.append(ch); } } return sb.toString(); } /** * Checks if the specified string is "no", "false", "off" or "0". * @param string string to be checked * @return result of check */ public static boolean no(final String string) { return eqic(string, Text.FALSE, Text.NO, Text.OFF, "0"); } /** * Converts a URI to a directory path. * See http://docs.basex.org/wiki/Repository#URI_Rewriting for details. * @param uri namespace uri * @return converted path */ public static String uri2path(final String uri) { String path = uri; try { final URI u = new URI(uri); final TokenBuilder tb = new TokenBuilder(); if(u.isOpaque()) { tb.add(u.getScheme()).add('/').add(u.getSchemeSpecificPart().replace(':', '/')); } else { final String auth = u.getAuthority(); if(auth != null) { // reverse authority, replace dots by slashes. example: basex.org -> org/basex final String[] comp = split(auth, '.'); for(int c = comp.length - 1; c >= 0; c--) tb.add('/').add(comp[c]); } // add remaining path final String p = u.getPath(); tb.add(p == null || p.isEmpty() ? "/" : p.replace('.', '/')); } path = tb.toString(); } catch(final URISyntaxException ignore) { } // replace special characters with dashes; remove multiple slashes path = path.replaceAll("[^\\w.-/]+", "-").replaceAll("//+", "/"); // add "index" string if(path.endsWith("/")) path += "index"; // remove heading slash if(path.startsWith("/")) path = path.substring(1); return path; } }