/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is the Kowari Metadata Store. * * The Initial Developer of the Original Code is Plugged In Software Pty * Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002 * Plugged In Software Pty Ltd. All Rights Reserved. * * Contributor(s): N/A. * * [NOTE: The text of this Exhibit A may differ slightly from the text * of the notices in the Source Code files of the Original Code. You * should use the text of this Exhibit A rather than the text found in the * Original Code Source Code for Your Modifications.] * */ package org.mulgara.util; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Reader; import java.util.*; /** * Purpose: A utility class for Strings. <P> * * Creation Date: 16 January 2001 <P> * * Original Author: Ben Warren (ben.warren@pisoftware.com) Original Author: * David Makepeace (david@pisoftware.com) <P> * * Last-modified Date: $Date: 2005/01/05 04:59:29 $ <P> * * Maintenance Author: $Author: newmana $ <P> * * Company: Plugged In Software <info@pisoftware.com> <P> * * Copyright © 2000 Plugged In Software Pty Ltd * (http://www.pisoftware.com/) <BR> * License: LGPL by default, or as assigned */ public class StringUtil { /** Back slash and quote char used by {@link #quoteString(String)}. */ private final static String TOKENS = "\\'"; /** Quote chars and space char used by {@link #parseString(String)}. */ private final static String SEPARATORS = "\\\"' "; /** Buffer size when reading chars or bytes for a string. */ private final static int BUFFER_SIZE = 10240; /** * Return the first prefix element of a string. * * For example, <code>getPrefixElements("a.b.c", 2)</code> returns * <code>"a.b"</code>. * * @param prefix The string to get the prefix elements from. * @param n The number of prefix elements. * @return The first n prefix elements, or the original string if there * weren't n elements. If n <= 0 an empty string is returned. */ public static String getPrefixElements(String prefix, int n) { int dotIndex = 0; if (n <= 0) return ""; //Try to get all n elements. while (n > 0) { //May be more keep going if (dotIndex != -1) { dotIndex = prefix.indexOf(".", dotIndex + 1); n--; //Not enough } else { return prefix; } } //Got all n elements - return the substring. return prefix.substring(0, dotIndex); } /** * Checks to see if a wildcard string pattern matches a string. * * For example, <code>*gh*</code> would match <code>gh</code>, * <code>tough</code>, <code>tougher</code> or <code>ghost</code>. * * @param wildcardString The string containing the pattern. * @param matchString The string to match the pattern against. * @return true If the pattern was found, false otherwise. */ public static boolean isMatch(String wildcardString, String matchString) { // The portion of the string that has not been matched with the pattern. String notYetMatched = matchString; // Tokenizer will throw away * characters. StringTokenizer strTok = new StringTokenizer(wildcardString, "*", false); // Store the tokens Vector<String> tokens = new Vector<String>(); String token = ""; int matchIndex = -1; // Always match same string. if (wildcardString.equals(matchString)) return true; // Never match empty string // (if matchString is empty will return true above) if (wildcardString.equals("")) return false; // No wildcards and strings are not equal. if (wildcardString.indexOf('*') == -1) return false; // Get all the tokens while (strTok.hasMoreTokens()) tokens.add(strTok.nextToken()); // If there were characters to match - do they have to match the start or end? if (tokens.size() > 0) { if (!wildcardString.startsWith("*") && !matchString.startsWith( (String) tokens.firstElement())) { return false; } if (!wildcardString.endsWith("*") && !matchString.endsWith( (String) tokens.lastElement())) { return false; } } // Loop through the tokens. while (tokens.size() > 0) { token = (String) tokens.remove(0); // Find the token matchIndex = notYetMatched.indexOf(token); // Not found if (matchIndex < 0) return false; //Reduce the not matched portion up to the end of the token match. notYetMatched = notYetMatched.substring(matchIndex + token.length()); } // We matched all the tokens successfully. return true; } /** * Determines if the string is null or does not contain any characters. * * @param valueStr String that is to be checked * @param allowSpaces boolean indicating whether empty spaces are valid or not * @return true if empty, false if not */ public static boolean isEmpty(String valueStr, boolean allowSpaces) { boolean isEmpty = false; if (valueStr == null) isEmpty = true; else if (valueStr.length() == 0) isEmpty = true; else if (!allowSpaces && (valueStr.trim().length() == 0)) isEmpty = true; return isEmpty; } /** * Determines if the string is null or does not contain any characters * * @param valueStr String that is to be checked * @return true if empty, false if not */ public static boolean isEmpty(String valueStr) { return isEmpty(valueStr, false); } /** * Splits a String into a String array. The String is split at every separator * character. The separator characters do not appear in the String objects in * the String array. * * @param str the String to be split. * @param sep the String containing a list of separator characters. * @return an array of String objects. */ public static String[] split(String str, String sep) { StringTokenizer st = new StringTokenizer(str, sep); String[] tokens = new String[st.countTokens()]; for (int i = 0; i < tokens.length; ++i) { tokens[i] = st.nextToken(); } return tokens; } /** * Converts an array of Strings to a single String consisting of a * space-separated list of strings. Individual strings will be quoted if they * contain spaces or quote characters. * * @param strings the array of Strings. * @return the resulting string. */ public static String toString(String... strings) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < strings.length; ++i) { String s = strings[i]; // Add a separator. if (i > 0) sb.append(' '); // Quote the URI if it contains a space or a quote. if ((s.indexOf(' ') != -1) || (s.indexOf('"') != -1) || (s.indexOf('\'') != -1) || (s.indexOf('\\') != -1)) { sb.append(quoteString(s)); } else { sb.append(s); } } return sb.toString(); } /** * Quotes a String by enclosing it in single quotes and escaping all single * quotes and back slashes. * * @param str the string to be quoted. * @return the quoted string. */ public static String quoteString(String str) { StringTokenizer st = new StringTokenizer(str, TOKENS, true); StringBuffer sb = new StringBuffer(str.length() + 4); sb.append('\''); while (st.hasMoreTokens()) { String token = st.nextToken(); char firstChar = token.charAt(0); if ((token.length() == 1) && (TOKENS.indexOf(firstChar) != -1)) { sb.append('\\'); sb.append(firstChar); } else { // Token does not contain a back slash or quote char. sb.append(token); } } sb.append('\''); return sb.toString(); } /** * Quotes a String by enclosing it in single quotes and escaping all single * quotes and back slashes. * * @param str the string to be unescaped. * @return the resulting string. */ public static String unescapeString(String str) { StringTokenizer st = new StringTokenizer(str, "\\", true); StringBuffer sb = new StringBuffer(str.length()); boolean escaped = false; while (st.hasMoreTokens()) { String token = st.nextToken(); char firstChar = token.charAt(0); if (!escaped && (token.length() == 1) && (firstChar == '\\')) { escaped = true; } else { // Token does not contain a back slash or is an escaped back slash. sb.append(token); escaped = false; } } return sb.toString(); } /** * Separates a String consisting of a space-separated list of substrings into * an array of Strings. Substrings in the input String may be quoted with * either single or double quotes. * * @param string the space-separated list of strings. * @return the string array. */ public static String[] parseString(String string) { StringTokenizer st = new StringTokenizer(string.trim(), SEPARATORS, true); List<String> strings = new ArrayList<String>(); StringBuffer sb = new StringBuffer(string.length()); boolean inQuotedString = false; boolean escaped = false; char quoteChar = '\000'; while (st.hasMoreTokens()) { String token = st.nextToken(); char firstChar = token.charAt(0); if (!escaped && (token.length() == 1) && (SEPARATORS.indexOf(firstChar) != -1)) { // Quote or space character. if (firstChar == ' ') { if (inQuotedString) { // Space in quoted string. sb.append(firstChar); } else if (sb.length() > 0) { // Output string to List. strings.add(sb.toString()); sb.setLength(0); } } else { if (inQuotedString) { if (firstChar == '\\') { // Escape the next character escaped = true; } else if (firstChar == quoteChar) { // End quote. inQuotedString = false; } else { // Quote within quoted string. sb.append(firstChar); } } else { // Begin quote. quoteChar = firstChar; inQuotedString = true; } } } else { // Token does not contain a space or quote char. sb.append(token); escaped = false; } } if (sb.length() > 0) { // Output final string to List. strings.add(sb.toString()); sb.setLength(0); } return strings.toArray(new String[strings.size()]); } /** * Quotes chars in the attribute value source string by replacing them with * the corresponding XML entities. * * @param str the source string. * @param sb the StringBuffer to append to. */ public static void quoteAV(String str, StringBuffer sb) { quoteXML(str, sb, "&<>'\""); } /** * Quotes chars in the attribute value source string by replacing them with * the corresponding XML entities. * * @param str the source string. * @return the quoted string. */ public static String quoteAV(String str) { StringBuffer sb = new StringBuffer(); quoteAV(str, sb); return sb.toString(); } /** * Quotes chars in the CDATA source string by replacing them with the * corresponding XML entities. * * @param str the source string. * @param sb the StringBuffer to append to. */ public static void quoteCDATA(String str, StringBuffer sb) { quoteXML(str, sb, "&<"); } /** * Quotes chars in the CDATA source string by replacing them with the * corresponding XML entities. * * @param str the source string. * @return the quoted string. */ public static String quoteCDATA(String str) { StringBuffer sb = new StringBuffer(); quoteCDATA(str, sb); return sb.toString(); } /** * Removes a substring from a string. Only the first instance of the substring * will be removed. Returns the original string if it does not contain the * substring. * * @param substring The substring to remove. * @param string The string to remove the substring from. * @return The string with substring removed. */ public static String removeSubstring(String substring, String string) { StringBuffer buffer = new StringBuffer(string); int beginIndex = string.indexOf(substring); int endIndex; if (beginIndex != -1) { endIndex = substring.length() + beginIndex; buffer.delete(beginIndex, endIndex); return buffer.toString(); } else { return string; } } /** * Left justify a string, inserting newlines at appropriate places to get * lines approximately the requested length. * * @param source The string data to re-format. * @param width The requested lenght of the lines. * @return The re-formatted text. */ public static String justifyLeft(String source, int width) { // Check for null param or string length less than given width, there is // nothing to do for these cases. if ( (source == null) || (source.length() <= width)) { return source; // Exit - nothing to do } // Copy the source string into a stringbuffer so we can edit it, and // initialise the counters/pointers. StringBuffer result = new StringBuffer(source); int lastSpace = -1; int lineStart = 0; int charPosition = 0; // Go through every character in the buffer while (charPosition < result.length()) { // Keep track of the last whitespace seen. if (result.charAt(charPosition) == ' ') { lastSpace = charPosition; } // Restart counting if we find a newline already in the string. if (result.charAt(charPosition) == '\n') { lastSpace = -1; lineStart = charPosition + 1; } // Ok, we have reached the required width... if (charPosition > ( (lineStart + width) - 1)) { // Did we pass some whitespace on the way here? if (lastSpace != -1) { // Yes - set the last space to be a linefeed and reset counters result.setCharAt(lastSpace, '\n'); lineStart = lastSpace + 1; lastSpace = -1; } else { // No - insert the linefeed right here, right now. result.insert(charPosition, '\n'); lineStart = charPosition + 1; } } charPosition++; } return result.toString(); } /** * Substitutes text in inputStr from values in substituteArray. * * @param inputStr A String containing text mixed in with ~1,~2,...~n tags. * @param substituteArray An array of strings which will replace the tags in * inputStr. * @return inputStr with tags replaced by the appropriate index from * substituteArray. */ public static String substituteStrings(String inputStr, String... substituteArray) { String resultStr = inputStr; if (substituteArray != null) { // Loop thru the substurion string array, replacing the "~n" values with // the array element. for (int tagInt = 0; tagInt < substituteArray.length; tagInt++) { if ((tagInt + 1) > 9) { resultStr = replaceStringWithString(resultStr, "~" + (tagInt + 1), substituteArray[tagInt]); } else { resultStr = replaceStringWithString(resultStr, "~0" + (tagInt + 1), substituteArray[tagInt]); } } } return resultStr; } /** * Substitutes one value with another throughout the string. * * @param sourceStr the string that contains the tokens * @param tokenStr the string that acts as the token to be replaced * @param replacementStr the string containing the value to be substituted * @param ignoreCase boolean indicating whether to ignore the case during the compare * @return String containing the string with all the values replaced */ public static String replaceStringWithString(String sourceStr, String tokenStr, String replacementStr, boolean ignoreCase) { if ((sourceStr == null) || (tokenStr == null) || tokenStr.equals("")) { return sourceStr; } StringBuffer resultStr = new StringBuffer(); String indexStr = sourceStr; int tokenStrLength = tokenStr.length(); int tokenStrIndex; int pos = 0; if (ignoreCase) { tokenStr = tokenStr.toUpperCase(); indexStr = sourceStr.toUpperCase(); } while ((tokenStrIndex = indexStr.indexOf(tokenStr, pos)) != -1) { resultStr.append(sourceStr.substring(pos, tokenStrIndex)); resultStr.append(replacementStr); pos = tokenStrIndex + tokenStrLength; } resultStr.append(sourceStr.substring(pos)); return resultStr.toString(); } /** * Substitutes one value with another throughout the string * * @param sourceStr the string that contains the tokens * @param tokenStr the string that acts as the token to be replaced * @param replacementStr the string containing the value to be substituted * @return String containing the string with all the values replaced */ public static String replaceStringWithString(String sourceStr, String tokenStr, String replacementStr) { return replaceStringWithString(sourceStr, tokenStr, replacementStr, false); } /** * Converts the data available through a {@link java.io.Reader} into a String. * @param reader The {@link java.io.Reader} to be converted. * @return The string that comes from the reader. * @throws IOException If there was an error accessing the Reader. */ public static String toString(Reader reader) throws IOException { char[] buffer = new char[BUFFER_SIZE]; int len; StringBuilder result = new StringBuilder(); while ((len = reader.read(buffer)) >= 0) { result.append(buffer, 0, len); } return result.toString(); } /** * Quotes the specified chars in the source string by replacing them with the * corresponding XML entities. * * @param str the source string. * @param sb the StringBuffer to append to. * @param chars the chars to quote. */ private static void quoteXML(String str, StringBuffer sb, String chars) { StringTokenizer st = new StringTokenizer(str, chars, true); while (st.hasMoreTokens()) { String token = st.nextToken(); char firstCh = token.charAt(0); if ((token.length() == 1) && (chars.indexOf(firstCh) != -1)) { switch (firstCh) { case '&': sb.append("&"); break; case '<': sb.append("<"); break; case '\'': sb.append("'"); break; case '"': sb.append("""); break; case '>': sb.append(">"); break; default: sb.append(token); break; } } else { int pos; while ( (pos = token.indexOf("]]>")) != -1) { if (pos > 0) sb.append(token.substring(0, pos)); sb.append("]]>"); token = token.substring(pos + 3); } if (token.length() > 0) sb.append(token); } } } /** Map of escape characters to their character codes */ private static Map<Character,String> map = new HashMap<Character,String>(); static { map.put('t', "\t"); map.put('t', "\t"); map.put('b', "\b"); map.put('n', "\n"); map.put('r', "\r"); map.put('f', "\f"); map.put('\\', "\\"); map.put('"', "\""); map.put('\'', "'"); } /** * Search for escape characters in a string, and replace them with the request values. * @param s The string to search. * @return A new string with all escape characters replaced with the originals. */ static final public String unescapeJavaString(String s) { StringBuilder sb = new StringBuilder(); int last = 0; int pos = 0; while ((pos = s.indexOf('\\', pos)) >= 0) { sb.append(s.substring(last, pos)); if (++pos == s.length()) break; char c = s.charAt(pos); String m = map.get(c); if (m != null) sb.append(m); else sb.append(c); last = ++pos; } sb.append(s.substring(last)); return sb.toString(); } /** * Returns a stack trace of a Throwable as a string, rather than the * default behaviour of sending it to stderr. * * @param t The Throwable containing the stack trace. * @return A string containing the stack trace. */ public static String strackTraceToString(Throwable t) { StringWriter strWriter = new StringWriter(); t.printStackTrace(new PrintWriter(strWriter)); return strWriter.toString(); } }