blob: 3f8c5c2d98039a56d60916de6df2844a8f21c5a3 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.common.core.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.eclipse.gmf.runtime.common.core.internal.CommonCoreDebugOptions;
import org.eclipse.gmf.runtime.common.core.internal.CommonCorePlugin;
import org.eclipse.gmf.runtime.common.core.internal.CommonCoreStatusCodes;
import com.ibm.icu.util.StringTokenizer;
/**
* In JDK 1.3 we don't have the luxury of replaceAll as in JDK 1.4.
* The replace methods from this string class are intended to be used
* until JDK 1.4 is used.
*
* For completely different implementations of the replace methods, please see
* http://forum.java.sun.com/thread.jsp?forum=31&thread=284142&message=1109785
*
* Replacing a whole word is completely different from replacing all instances.
* To avoid confusion, the method names are different too.
*
* @author wdiu, Wayne Diu
*/
public class StringUtil {
/**
* Delimiters to mark the beginning or end of a string, used for
* replaceWholeWords
*/
private static String delims = " !:;{}(),.?'\"\\\t\n\r"; //$NON-NLS-1$
/**
* Internet protocol delimiter ://
*/
private static final String PROTOCOL_DELIMITER = "://"; //$NON-NLS-1$
/**
* Length of the internet protocol delimiter ://
*/
private static final int PROTOCOL_DELIMITER_LENGTH = PROTOCOL_DELIMITER.length();
/**
* Default URL Encoding method. UTF-8.
*/
private static final String URL_ENCODING = "UTF-8"; //$NON-NLS-1$
/**
* UTF-8 encoded %. %25
*/
private static final String ENCODED_PERCENT = "%25"; //$NON-NLS-1$
/**
* private constructor for the static class.
*/
private StringUtil() {
super();
}
/**
* Replace the first instance of part of a string with another string
* starting from a point in the string.
* @param string is the string that contains the substring to be replaced
* @param source is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
* @param from in, out. from[0] contains where to start initially
* and is changed to where it left off.
* @return the string containing the first instance of source found
* starting from a point in the string replaced with dest and in the
* from parameter, the end index of where the last replacement was made
*/
private static String replaceFrom(
String string,
String source,
String dest,
boolean caseSensitive,
int[] from) {
if (source.compareTo(StringStatics.BLANK) == 0)
return string;
int stringLength = string.length(),
sourceLength = source.length(),
destLength = dest.length();
while (from[0] + sourceLength <= stringLength) {
int compareResult;
if (caseSensitive)
compareResult =
string.substring(
from[0],
from[0] + sourceLength).compareTo(
source);
else
compareResult =
string.substring(
from[0],
from[0] + sourceLength).compareToIgnoreCase(
source);
//not case sensitive
if (compareResult == 0) {
//System.out.println("matched " + string.substring(i, i + sourceLength) + " with " + source);
int fromIndex = from[0];
from[0] += destLength;
return string.substring(0, fromIndex)
+ dest
+ string.substring(fromIndex + sourceLength, stringLength);
}
//System.out.println("did not match " + string.substring(i, i + sourceLength) + " with " + source);
from[0]++;
}
return string;
}
/**
* Replace the first instance of part of a string with another string
* starting from a point in the string.
* @param string is the string that contains the substring to be replaced
* @param source is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
* @param from where to start
* @return the string containing the first instance of source found
* starting from a point in the string replaced with dest
*/
private static String replaceFrom(
String string,
String source,
String dest,
boolean caseSensitive,
int from) {
int fromArray[] = new int[1];
fromArray[0] = from;
return replaceFrom(string, source, dest, caseSensitive, fromArray);
}
/**
* Replace the first instance of part of a string with another string.
* @param string is the string that contains the substring to be replaced
* @param source is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
* @return the string containing the first instance of source replaced
* with dest
*/
public static String replace(
String string,
String source,
String dest,
boolean caseSensitive) {
return replaceFrom(string, source, dest, caseSensitive, 0);
}
/**
* Replace all instances of part of a string with another string
* starting from a point in the string.
* @param string is the string that contains the substring to be replaced
* @param source is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
* @param from where to start
* @return the string containing all instances of source replaced with dest
*/
private static String replaceAll(
String string,
String source,
String dest,
boolean caseSensitive,
int from) {
int fromArray[] = new int[1];
fromArray[0] = from;
String newString =
replaceFrom(string, source, dest, caseSensitive, fromArray);
from = fromArray[0];
//I don't see the need for compareTo or compareToIgnoreCase depending on caseSensitive,
//but maybe I should like in the replace method
if (newString.compareTo(string) == 0) { //it's the same, so return
return newString;
} else { //still something left to change
return replaceAll(newString, source, dest, caseSensitive, from);
}
}
/**
* Replace all instances of part of a string with another string
* @param string is the string that contains the substring to be replaced
* @param source is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
* @return the string containing all instances of source replaced with dest
*/
public static String replaceAll(
String string,
String source,
String dest,
boolean caseSensitive) {
return replaceAll(string, source, dest, caseSensitive, 0);
}
/**
* Replaces whole words found in one string with another string.
* The whole words are delimted by delimiters defined in the DELIMTERES
* constant.
*
* @param string is the string that contains the substring to be replaced
* @param pattern is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
* @param fromArray 0th element contains index of where to start searching
* @return the string containing one instances of source replaced with dest
*/
private static String replaceWholeWordsFrom(
String string,
String pattern,
String dest,
boolean caseSensitive,
int[] fromArray) {
boolean frontOK = false, backOK = false;
// find index of the first occurence of the pattern string
int index =
(caseSensitive)
? string.indexOf(pattern, fromArray[0])
: string.toUpperCase().indexOf(
pattern.toUpperCase(),
fromArray[0]);
//by default, if (index < 0) frontOK = false;
// make sure that the front of the found pattern is prefixed with either
// a delimeter character or nothing, since we are replacing WHOLE words
// mark the front of the string to be OK if that is the case. Do similar
// check for the back of the string
if (index == 0)
frontOK = true;
else if (index > 0) {
if (delims.indexOf(string.charAt(index - 1)) >= 0) {
frontOK = true;
}
}
//front is ok, check back
if (frontOK) {
if (index + pattern.length() >= string.length())
backOK = true;
else if (
delims.indexOf(string.charAt((index + pattern.length()))) >= 0)
backOK = true;
if (backOK) {
fromArray[0] = (index - 1 < 0) ? 0 : index + dest.length();
return string.substring(0, (index - 1 < 0) ? 0 : index)
+ dest
+ ((index + pattern.length() > string.length())
? StringStatics.BLANK
: string.substring(
index + pattern.length(),
string.length()));
}
}
if (index >= 0 /*&& (!frontOK || !backOK)*/
&& index + 1 < string.length()) {
fromArray[0] = index + 1;
return replaceWholeWordsFrom(
string,
pattern,
dest,
caseSensitive,
fromArray);
}
return string;
}
/**
* Replaces whole words found in one string with another string.
* The whole words are delimted by delimiters defined in the DELIMTERES
* constant.
*
* @param string is the string that contains the substring to be replaced
* @param source is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
*
* @return the string containing the first instance of source replaced with
* dest.
*/
public static String replaceWholeWords(
String string,
String source,
String dest,
boolean caseSensitive) {
int fromArray[] = new int[] {0};
return replaceWholeWordsFrom(
string,
source,
dest,
caseSensitive,
fromArray);
}
/**
* Replaces whole words found in one string with another string.
* The whole words are delimted by delimiters defined in the DELIMTERES
* constant.
*
* @param string is the string that contains the substring to be replaced
* @param source is the substring that will be replaced if it is found
* in the string
* @param dest what the substring will be replaced with if it is found
* in the string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
*
* @return the string containing all instances of source replaced with
* dest.
*/
public static String replaceAllWholeWords(
String string,
String source,
String dest,
boolean caseSensitive) {
int fromArray[] = new int[] {0};
String oldResult = null,
result =
replaceWholeWordsFrom(
string,
source,
dest,
caseSensitive,
fromArray);
while (oldResult == null || !result.equals(oldResult)) {
oldResult = result;
result =
replaceWholeWordsFrom(
oldResult,
source,
dest,
caseSensitive,
fromArray);
}
return result;
}
/**
* Returns if a substring was found as a whole word in a string.
* The whole words are delimted by delimiters defined in the DELIMTERES
* constant.
*
* @param string is the string that contains the substring to be replaced
* @param source is the substring that we are checking for in string
* @param caseSensitive true to do a case sensitive search, false to do
* a case insensitive search
*
* @return boolean true if the string was found as a word, false if the
* string was not found as a word.
*/
public static boolean doesWordExist(
String string,
String source,
boolean caseSensitive) {
StringTokenizer st = new StringTokenizer(string);
while (st.hasMoreTokens()) {
String token = st.nextToken();
//== 0 if equal.
if (((caseSensitive) && (token.compareTo(source) == 0))
|| ((!caseSensitive)
&& (token.toUpperCase().compareTo(source.toUpperCase())
== 0))) {
return true;
}
}
return false;
}
/**
* Do not use Integer.parseInt(). This is doing something different.
* Based on code from Gleb's SetMultiplicityDialog.
*
* @param string to check if it is a valid positive integer
* @return true if it's a valid positive integer, false if it isn't
*/
public static boolean isValidPositiveInteger(String string) {
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) < '0' || string.charAt(i) > '9')
return false;
}
return true;
}
/**
* Encode the url string
*
* @param url String to encode
* @return String encoded URL
*/
public static String encodeURL(String url) {
//find the ://
int protocolIndex = url.indexOf(PROTOCOL_DELIMITER);
if (protocolIndex > 0) {
String start = url.substring(0, protocolIndex + PROTOCOL_DELIMITER_LENGTH);
//find the first / after ;//
int slashIndex = url.indexOf('/', protocolIndex + PROTOCOL_DELIMITER_LENGTH);
if (slashIndex == -1) {
slashIndex = url.length();
}
String domain = url.substring(protocolIndex + PROTOCOL_DELIMITER_LENGTH, slashIndex);
// This code has been commented until we migrate to Java 2
// IDN is only available in Java 2
// private static final String DOMAIN_ENCODING = "ISO-8859-1";
// try {
// domain = java.net.IDN.toASCII(domain, DOMAIN_ENCODING);
// } catch (IllegalArgumentException e) {
// //this should not be logged, just do not convert the domain string
// }
//may be empty
String end = url.substring(slashIndex, url.length());
end = encodePercentage(end);
end = encode(end);
return start + domain + end;
}
return url;
}
/**
* Encodes %s in a string to %25 when not followed by a valid hex number.
*
* @param string to encode
* @return String with %25 encoded
*/
private static String encodePercentage(String string) {
for (int index = string.indexOf('%'); index != -1; ) {
int length = string.length();
if (length > index + 2) {
//check the next 2 digits
if (!isOKForHex(string.charAt(index + 1)) || !isOKForHex(string.charAt(index + 2))) {
//encode % at index
string = replace(string, index, ENCODED_PERCENT);
}
}
else {
//the string is too short for the numbers after % to be a valid
//hex number
string = replace(string, index, ENCODED_PERCENT);
}
index = string.indexOf('%', index + 1);
}
return string;
}
/**
* Verifies that a given character could be part of a valid hex number.
*
* @param aChar checks that this char could be part of a valid hex number.
* @return true if the character could be part of a valid hex number.
*/
private static boolean isOKForHex(char aChar) {
if ((aChar >= '0') && (aChar <= '9')) {
return true;
}
if ((aChar >= 'a') && (aChar <= 'f')) {
return true;
}
if ((aChar >= 'A') && (aChar <= 'F')) {
return true;
}
return false;
}
/**
* Returns a replaced string made up of the original string replaced with
* newPart at position index.
*
* @param original String to replace
* @param index position to replace at
* @param newPart the new String to be replaced at position index
* @return replaced String made up of the original string replaced with
* newPart at position index.
*/
private static String replace(String original, int index, String newPart) {
return original.substring(0, index) + newPart + original.substring(index + 1);
}
/**
* Finds a position of a special character that shouldn't be url encoded
* starting from the given start index.
*
* Special characters are % / & = + ? #
*
* @param string look for the special character in this string
* @param start index to start looking at the string from
*
* @return position of a special character that shouldn't be url encoded
* starting from the given start index. -1 if it could not find a special
* character that shouldn't be url encoded.
*/
private static int findUnEncodeableCharacter(String string, int start) {
if (start >= string.length())
return -1;
int indices[] = new int[] {string.indexOf('%', start),
string.indexOf('/', start), string.indexOf('&', start),
string.indexOf('=', start), string.indexOf('+', start),
string.indexOf('?', start), string.indexOf('#', start)
};
int index = -1;
for (int i = 0; i < indices.length; i++) {
if (indices[i] != -1 && indices[i] < index || index == -1) {
index = indices[i];
}
}
return index;
}
/**
* Runs URLEncoder.encode on a string, but excludes the special characters
* that should not be encoded.
*
* It assumes that percentages that need to be encoded have already been
* encoded. This may be done using the encodePercentage method. Therefore,
* percentage characters are treated as special characters.
*
* Special characters are determined by the findUnEncodeableCharacter
* method.
*
* @param string
* @return
*/
private static String encode(String string) {
int beginIndex = findUnEncodeableCharacter(string, 0);
if (beginIndex == -1) beginIndex = 0;
while (beginIndex < string.length()) {
int endIndex = findUnEncodeableCharacter(string, beginIndex + 1);
if (endIndex == -1) endIndex = string.length();
String begin = string.substring(0, beginIndex + 1);
String middle = string.substring(beginIndex + 1, endIndex);
String end = string.substring(endIndex);
//encode the middle
try {
middle = URLEncoder.encode(middle, URL_ENCODING);
} catch (UnsupportedEncodingException e) {
//UTF-8 should never be unavailable
Log.error(CommonCorePlugin.getDefault(), CommonCoreStatusCodes.ENCODING_FAILURE, URL_ENCODING + " unsupported."); //$NON-NLS-1$
Trace.catching(CommonCorePlugin.getDefault(), CommonCoreDebugOptions.EXCEPTIONS_CATCHING, StringUtil.class, "encode", e); //$NON-NLS-1$
}
beginIndex = begin.length() + middle.length();
string = begin + middle + end;
}
return string;
}
}