package org.pmdreports.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * A collection of static utility methods related to Strings. Many of these
 * methods were migrated from the Digital Evolution StringUtilis class.
 * 
 * @author Ben Northrop Date: August 20th, 2002
 */
public class StringUtil {
	// CONSTANTS
	/**
	 * Used by replace( String, Map ) function
	 */
	public final static String TOKEN_NAME = "[PARAM_VALUE_";

	/**
	 * Used by replace( String, Map ) function
	 */
	public final static String TOKEN_END_CHARACTER = "]";

	/**
	 * Used as a spacer character for a value that cannot be substituted in the
	 * replace(Map) method.
	 */
	public final static String NO_VALUE = "--";

	// CONSTRUCTOR
	/**
	 * Private constructor since a utilities class.
	 */
	private StringUtil() {
		// utility class, needs no constructor
	}

	// HELPER METHODS
	/**
	 * Checks whether a String is either null or the empty string.
	 * 
	 * @author Ben Northrop date: August 25th, 2002
	 */
	public static boolean isEmpty(String str) {
		return str == null || str.trim().equals("");
	}

	/**
	 * Split a string into smaller chunks given a delimiter. For example, the
	 * following string would be split as follows, given "," as a delimiter:
	 * 
	 * "23SA, ASB3, xxx, 1234" --> {"23SA", "ASB3", "xxx", "1234"}
	 * 
	 * @author Ben Northrop date: July 30th, 2002
	 */
	public static List<String> getSplitList(String splitString, String delimiter) {
		final List<String> stringList = new ArrayList<String>();

		// Validation: prevent null pointer exception
		if (splitString == null) {
			return stringList;
		}
		if (delimiter == null) {
			return stringList;
		}

		// Handle nulls in the list, like "ben,zemin,,vineet", split
		// into 4 Strings
		final String newSplitString = replaceWith(splitString, delimiter, delimiter
				+ " ");

		final StringTokenizer tokenizer = new StringTokenizer(newSplitString,
				delimiter);
		String str = "";
		while (tokenizer.hasMoreTokens()) {

			str = tokenizer.nextToken();
			str = str.trim();

			stringList.add(str);
		}

		return stringList;
	}

	public static String replaceWith(String sourceStr, String oldStr,
			String newStr) {

		String tmpNewStr = newStr;
		if (tmpNewStr == null) {
			tmpNewStr = "";
		}

		final StringBuffer sb = new StringBuffer();
		final int startSub = sourceStr.indexOf(oldStr);
		String returnValue;

		// had to use recursion instead of StringTokenizer because bugs in JDK
		// 1.3
		if (startSub >= 0) {

			final String rtStr = replaceWith(sourceStr.substring(startSub
					+ oldStr.length(), sourceStr.length()), oldStr, tmpNewStr);

			sb.append(sourceStr.substring(0, startSub));
			sb.append(tmpNewStr);
			sb.append(rtStr);

			returnValue = sb.toString();
		} else {
			returnValue = sourceStr;
		}

		return returnValue;

	}

	/**
	 * If first parameter is equal to second - returns third (replaceTo),
	 * otherwise returns the original string (first param).
	 */
	public static String validateAndReplace(String inStr, String ifEqual,
			String replaceTo) {
		if (inStr == null && ifEqual == null) {
			return replaceTo;
		} else if (inStr == null && ifEqual != null) {
			return inStr;
		} else {
			return (inStr.equals(ifEqual)) ? replaceTo : inStr;
		}
	}

	/**
	 * If passed string is null - returns an empty string value.
	 */
	public static String convertNullString(String inString) {
		return (inString == null) ? "" : inString;
	}

	/**
	 * Given an input string, it replaces all occurences of each replaceWhat
	 * element with the corresponding elements of replaceWith. Both replaceWhat
	 * and replaceWith are String objects so trying to substitute "aaa" with
	 * "bbbbbb" is perfectly valid
	 * 
	 * @author Marius Constantin date Aug 2, 2002
	 */
	public static String replace(String in, String[] replaceWhat,
			String[] replaceWith) {
		String tmpIn = in;
		StringBuffer sb = null;

		int idxStart = 0;
		int idxEnd = -1;

		for (int i = 0; i < replaceWhat.length; i++) {
			sb = new StringBuffer(); // NOPMD
			while ((idxEnd = tmpIn.indexOf(replaceWhat[i], idxStart)) != -1) {
				sb.append(tmpIn.substring(idxStart, idxEnd));
				sb.append(replaceWith[i]);

				idxStart = idxEnd + replaceWhat[i].length();
			}

			// appending last portion of the searched string
			sb.append(tmpIn.substring(idxStart));

			tmpIn = sb.toString();

			idxStart = 0;
			idxEnd = -1;
		}

		return in;
	}

	/**
	 * Replaces all occurences in the 'params' Map in one pass through the 'in'
	 * String. Depends on the values of TOKEN_NAME and TOKEN_END_CHARACTER
	 * 
	 * @param <code>in</code> The input String
	 * @param <code>params</code> A Map containing as keys the strings to be
	 *            looked up and as values the string that keys have to be
	 *            replaced with
	 * 
	 * @author Marius Constantin date: Aug 7, 2002
	 */
	public static String replace(String in, Map params) {
		StringBuffer sb = new StringBuffer();
		String tmpIn = in;

		int tokenLength = TOKEN_NAME.length();
		int startTokenIdx = 0;
		int endTokenIdx = 0;
		String token = null;
		boolean canContinue = true;
		while (canContinue) {
			startTokenIdx = tmpIn.indexOf(TOKEN_NAME);
			if (startTokenIdx == -1) {
				sb.append(tmpIn);
				canContinue = false;
			} else {
				endTokenIdx = tmpIn.indexOf(TOKEN_END_CHARACTER, startTokenIdx
						+ tokenLength) + 1;
				token = tmpIn.substring(startTokenIdx + tokenLength,
						endTokenIdx - 1);

				sb.append(tmpIn.substring(0, startTokenIdx));

				Object value = params.get(token);
				if (value == null) {
					sb.append(NO_VALUE);
				} else {
					sb.append(value);
				}

				tmpIn = tmpIn.substring(endTokenIdx);
			}
		}

		return sb.toString();
	}

	/**
	 * Checks if a string is a valid number or not.
	 * 
	 * @author Ben Northrop Date: September 9th, 2002
	 * 
	 */
	public static boolean isValidNumber(String str) {
		if (str == null) {
			return false;
		} else {
			try {
				Integer.parseInt(str);
			} catch (NumberFormatException nfe) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Return a string with padding appended at the end such that the total
	 * length of the return string will be the length passed in as totalLength.
	 * For example, calling pad("ben", 5) would return the string "ben ".
	 * 
	 * @author Ben Northrop
	 */
	public static String padToTotalLength(String str, int totalLength) {
		int numSpaces = 0;
		if (str == null) {
			numSpaces = totalLength;
		} else if (str.length() == totalLength) {
			return str;
		} else if (str.length() > totalLength) {
			return str.substring(0, totalLength);
		} else {
			numSpaces = totalLength - str.length();
		}

		return pad(str, numSpaces);
	}

	/**
	 * Pad a string with a given number of white spaces at the end.
	 * 
	 * @author Ben Northrop Date: November 18th, 2002
	 */
	public static String pad(String str, int spaces) {
		StringBuffer sb = new StringBuffer();
		sb.append(str);
		for (int i = 0; i < spaces; i++) {
			sb.append(' ');
		}
		return sb.toString();
	}

	/**
	 * Truncate a string to a given length.
	 */
	public static String truncate(String str, int length) {
		if (str == null) {
			return "";
		}

		if (str.length() <= length) {
			return str;
		}

		return str.substring(0, length) + "..";
	}

}
