|  |  |  | @ -19,6 +19,7 @@ | 
			
		
	
		
			
				
					|  |  |  |  |  */ | 
			
		
	
		
			
				
					|  |  |  |  | package org.airsonic.player.util; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | import org.apache.commons.codec.DecoderException; | 
			
		
	
		
			
				
					|  |  |  |  | import org.apache.commons.codec.binary.Hex; | 
			
		
	
		
			
				
					|  |  |  |  | import org.apache.commons.lang.StringUtils; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -28,11 +29,11 @@ import java.net.URL; | 
			
		
	
		
			
				
					|  |  |  |  | import java.net.URLDecoder; | 
			
		
	
		
			
				
					|  |  |  |  | import java.net.URLEncoder; | 
			
		
	
		
			
				
					|  |  |  |  | import java.nio.charset.StandardCharsets; | 
			
		
	
		
			
				
					|  |  |  |  | import java.security.MessageDigest; | 
			
		
	
		
			
				
					|  |  |  |  | import java.text.*; | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.*; | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.regex.Matcher; | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.regex.Pattern; | 
			
		
	
		
			
				
					|  |  |  |  | import java.util.stream.Stream; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | /** | 
			
		
	
		
			
				
					|  |  |  |  |  * Miscellaneous string utility methods. | 
			
		
	
	
		
			
				
					|  |  |  | @ -42,15 +43,8 @@ import java.util.regex.Pattern; | 
			
		
	
		
			
				
					|  |  |  |  | public final class StringUtil { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     public static final String ENCODING_UTF8 = "UTF-8"; | 
			
		
	
		
			
				
					|  |  |  |  |     private static final DateFormat ISO_8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private static final String[][] HTML_SUBSTITUTIONS = { | 
			
		
	
		
			
				
					|  |  |  |  |             {"&", "&"}, | 
			
		
	
		
			
				
					|  |  |  |  |             {"<", "<"}, | 
			
		
	
		
			
				
					|  |  |  |  |             {">", ">"}, | 
			
		
	
		
			
				
					|  |  |  |  |             {"'", "'"}, | 
			
		
	
		
			
				
					|  |  |  |  |             {"\"", """}, | 
			
		
	
		
			
				
					|  |  |  |  |     }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private static final Pattern SPLIT_PATTERN = Pattern.compile("\"([^\"]*)\"|(\\S+)"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     private static final String[][] MIME_TYPES = { | 
			
		
	
		
			
				
					|  |  |  |  |             {"mp3", "audio/mpeg"}, | 
			
		
	
	
		
			
				
					|  |  |  | @ -98,58 +92,6 @@ public final class StringUtil { | 
			
		
	
		
			
				
					|  |  |  |  |     private StringUtil() { | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Returns the specified string converted to a format suitable for | 
			
		
	
		
			
				
					|  |  |  |  |      * HTML. All single-quote, double-quote, greater-than, less-than and | 
			
		
	
		
			
				
					|  |  |  |  |      * ampersand characters are replaces with their corresponding HTML | 
			
		
	
		
			
				
					|  |  |  |  |      * Character Entity code. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param s the string to convert | 
			
		
	
		
			
				
					|  |  |  |  |      * @return the converted string | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public static String toHtml(String s) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (s == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return null; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         for (String[] substitution : HTML_SUBSTITUTIONS) { | 
			
		
	
		
			
				
					|  |  |  |  |             if (s.contains(substitution[0])) { | 
			
		
	
		
			
				
					|  |  |  |  |                 s = s.replaceAll(substitution[0], substitution[1]); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         return s; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Formats the given date to a ISO-8601 date/time format, and UTC timezone. | 
			
		
	
		
			
				
					|  |  |  |  |      * <p/> | 
			
		
	
		
			
				
					|  |  |  |  |      * The returned date uses the following format: 2007-12-17T14:57:17 | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param date The date to format | 
			
		
	
		
			
				
					|  |  |  |  |      * @return The corresponding ISO-8601 formatted string. | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public static String toISO8601(Date date) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (date == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return null; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         synchronized (ISO_8601_DATE_FORMAT) { | 
			
		
	
		
			
				
					|  |  |  |  |             return ISO_8601_DATE_FORMAT.format(date); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Removes the suffix (the substring after the last dot) of the given string. The dot is | 
			
		
	
		
			
				
					|  |  |  |  |      * also removed. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param s The string in question, e.g., "foo.mp3". | 
			
		
	
		
			
				
					|  |  |  |  |      * @return The string without the suffix, e.g., "foo". | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public static String removeSuffix(String s) { | 
			
		
	
		
			
				
					|  |  |  |  |         int index = s.lastIndexOf('.'); | 
			
		
	
		
			
				
					|  |  |  |  |         return index == -1 ? s : s.substring(0, index); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Returns the proper MIME type for the given suffix. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
	
		
			
				
					|  |  |  | @ -269,16 +211,14 @@ public final class StringUtil { | 
			
		
	
		
			
				
					|  |  |  |  |             return new String[0]; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         Pattern pattern = Pattern.compile("\".*?\"|\\S+"); | 
			
		
	
		
			
				
					|  |  |  |  |         Matcher matcher = pattern.matcher(input); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         List<String> result = new ArrayList<String>(); | 
			
		
	
		
			
				
					|  |  |  |  |         while (matcher.find()) { | 
			
		
	
		
			
				
					|  |  |  |  |             String element = matcher.group(); | 
			
		
	
		
			
				
					|  |  |  |  |             if (element.startsWith("\"") && element.endsWith("\"") && element.length() > 1) { | 
			
		
	
		
			
				
					|  |  |  |  |                 element = element.substring(1, element.length() - 1); | 
			
		
	
		
			
				
					|  |  |  |  |         List<String> result = new ArrayList<>(); | 
			
		
	
		
			
				
					|  |  |  |  |         Matcher m = SPLIT_PATTERN.matcher(input); | 
			
		
	
		
			
				
					|  |  |  |  |         while (m.find()) { | 
			
		
	
		
			
				
					|  |  |  |  |             if (m.group(1) != null) { | 
			
		
	
		
			
				
					|  |  |  |  |                 result.add(m.group(1)); // quoted string
 | 
			
		
	
		
			
				
					|  |  |  |  |             } else { | 
			
		
	
		
			
				
					|  |  |  |  |                 result.add(m.group(2)); // unquoted string
 | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             result.add(element); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         return result.toArray(new String[result.size()]); | 
			
		
	
	
		
			
				
					|  |  |  | @ -320,21 +260,9 @@ public final class StringUtil { | 
			
		
	
		
			
				
					|  |  |  |  |             return new int[0]; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         String[] strings = StringUtils.split(s); | 
			
		
	
		
			
				
					|  |  |  |  |         int[] ints = new int[strings.length]; | 
			
		
	
		
			
				
					|  |  |  |  |         for (int i = 0; i < strings.length; i++) { | 
			
		
	
		
			
				
					|  |  |  |  |             ints[i] = Integer.parseInt(strings[i]); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         return ints; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Determines whether a is equal to b, taking null into account. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @return Whether a and b are equal, or both null. | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public static boolean isEqual(Object a, Object b) { | 
			
		
	
		
			
				
					|  |  |  |  |         return Objects.equals(a, b); | 
			
		
	
		
			
				
					|  |  |  |  |         return Stream.of(StringUtils.split(s)) | 
			
		
	
		
			
				
					|  |  |  |  |                 .mapToInt(Integer::parseInt) | 
			
		
	
		
			
				
					|  |  |  |  |                 .toArray(); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
	
		
			
				
					|  |  |  | @ -348,18 +276,11 @@ public final class StringUtil { | 
			
		
	
		
			
				
					|  |  |  |  |             return null; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         String[] elements = s.split("_"); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if (elements.length == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |             return new Locale(s, "", ""); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         if (elements.length == 1) { | 
			
		
	
		
			
				
					|  |  |  |  |             return new Locale(elements[0], "", ""); | 
			
		
	
		
			
				
					|  |  |  |  |         List<String> elements = new ArrayList<>(Arrays.asList(s.split("_", 3))); | 
			
		
	
		
			
				
					|  |  |  |  |         while (elements.size() < 3) { | 
			
		
	
		
			
				
					|  |  |  |  |             elements.add(""); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         if (elements.length == 2) { | 
			
		
	
		
			
				
					|  |  |  |  |             return new Locale(elements[0], elements[1], ""); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         return new Locale(elements[0], elements[1], elements[2]); | 
			
		
	
		
			
				
					|  |  |  |  |         return new Locale(elements.get(0), elements.get(1), elements.get(2)); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
	
		
			
				
					|  |  |  | @ -404,34 +325,15 @@ public final class StringUtil { | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param s The string to decode. | 
			
		
	
		
			
				
					|  |  |  |  |      * @return The decoded string. | 
			
		
	
		
			
				
					|  |  |  |  |      * @throws Exception If an error occurs. | 
			
		
	
		
			
				
					|  |  |  |  |      * @throws DecoderException If an error occurs. | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public static String utf8HexDecode(String s) throws Exception { | 
			
		
	
		
			
				
					|  |  |  |  |     public static String utf8HexDecode(String s) throws DecoderException { | 
			
		
	
		
			
				
					|  |  |  |  |         if (s == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return null; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         return new String(Hex.decodeHex(s.toCharArray()), StandardCharsets.UTF_8); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Calculates the MD5 digest and returns the value as a 32 character hex string. | 
			
		
	
		
			
				
					|  |  |  |  |      * | 
			
		
	
		
			
				
					|  |  |  |  |      * @param s Data to digest. | 
			
		
	
		
			
				
					|  |  |  |  |      * @return MD5 digest as a hex string. | 
			
		
	
		
			
				
					|  |  |  |  |      */ | 
			
		
	
		
			
				
					|  |  |  |  |     public static String md5Hex(String s) { | 
			
		
	
		
			
				
					|  |  |  |  |         if (s == null) { | 
			
		
	
		
			
				
					|  |  |  |  |             return null; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         try { | 
			
		
	
		
			
				
					|  |  |  |  |             MessageDigest md5 = MessageDigest.getInstance("MD5"); | 
			
		
	
		
			
				
					|  |  |  |  |             return new String(Hex.encodeHex(md5.digest(s.getBytes(StandardCharsets.UTF_8)))); | 
			
		
	
		
			
				
					|  |  |  |  |         } catch (Exception x) { | 
			
		
	
		
			
				
					|  |  |  |  |             throw new RuntimeException(x.getMessage(), x); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     /** | 
			
		
	
		
			
				
					|  |  |  |  |      * Returns the file part of an URL. For instance: | 
			
		
	
		
			
				
					|  |  |  |  |      * <p/> | 
			
		
	
	
		
			
				
					|  |  |  | 
 |