package it.mice.voila.runtime.util;

/**
 * Licensed under the MICE s.r.l.
 * End User License Agreement
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.micegroup.it/voila/license.html
 */

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;

/**
 * This class provides some methods for dynamically invoking methods in objects,
 * and some string manipulation methods used by torque. The string methods will
 * soon be moved into the turbine string utilities class.
 * 
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
 * @version $Id: StringUtils.java,v 1.1 2007/02/16 11:34:01 zzy9v4 Exp $
 */
public class StringUtils {
	/**
	 * charset per il calcolo di una random string.
	 */
	private static final String charset = "!0123456789abcdefghijklmnopqrstuvwxyz";

	/**
	 * Line separator for the OS we are operating on.
	 */
	private static final String EOL = System.getProperty("line.separator");

	/**
	 * Length of the line separator.
	 */
	private static final int EOL_LENGTH = EOL.length();

	/**
	 * Concatenates a list of objects as a String.
	 * 
	 * @param list
	 *            The list of objects to concatenate.
	 * @return A text representation of the concatenated objects.
	 */
	public String concat(List list) {
		StringBuffer sb = new StringBuffer();
		int size = list.size();

		for (int i = 0; i < size; i++) {
			sb.append(list.get(i).toString());
		}
		return sb.toString();
	}

	/**
	 * Return a package name as a relative path name
	 * 
	 * @param String
	 *            package name to convert to a directory.
	 * @return String directory path.
	 */
	static public String getPackageAsPath(String pckge) {
		return pckge.replace('.', File.separator.charAt(0)) + File.separator;
	}

	/**
	 * <p>
	 * Remove underscores from a string and replaces first letters with
	 * capitals. Other letters are changed to lower case.
	 * </p>
	 * 
	 * <p>
	 * For example <code>foo_bar</code> becomes <code>FooBar</code> but
	 * <code>foo_barBar</code> becomes <code>FooBarbar</code>.
	 * </p>
	 * 
	 * @param data
	 *            string to remove underscores from.
	 * @return String
	 * @deprecated Use the org.apache.commons.util.StringUtils class instead.
	 *             Using its firstLetterCaps() method in conjunction with a
	 *             StringTokenizer will achieve the same result.
	 */
	static public String removeUnderScores(String data) {
		String temp = null;
		StringBuffer out = new StringBuffer();
		temp = data;

		StringTokenizer st = new StringTokenizer(temp, "_");

		while (st.hasMoreTokens()) {
			String element = (String) st.nextElement();
			out.append(firstLetterCaps(element));
		}

		return out.toString();
	}

	/**
	 * <p>
	 * 'Camels Hump' replacement of underscores.
	 * </p>
	 * 
	 * <p>
	 * Remove underscores from a string but leave the capitalization of the
	 * other letters unchanged.
	 * </p>
	 * 
	 * <p>
	 * For example <code>foo_barBar</code> becomes <code>FooBarBar</code>.
	 * </p>
	 * 
	 * @param data
	 *            string to hump
	 * @return String
	 */
	static public String removeAndHump(String data) {
		return removeAndHump(data, "_");
	}

	/**
	 * <p>
	 * 'Camels Hump' replacement.
	 * </p>
	 * 
	 * <p>
	 * Remove one string from another string but leave the capitalization of the
	 * other letters unchanged.
	 * </p>
	 * 
	 * <p>
	 * For example, removing "_" from <code>foo_barBar</code> becomes
	 * <code>FooBarBar</code>.
	 * </p>
	 * 
	 * @param data
	 *            string to hump
	 * @param replaceThis
	 *            string to be replaced
	 * @return String
	 */
	static public String removeAndHump(String data, String replaceThis) {
		String temp = null;
		StringBuffer out = new StringBuffer();
		temp = data;

		StringTokenizer st = new StringTokenizer(temp, replaceThis);

		while (st.hasMoreTokens()) {
			String element = (String) st.nextElement();
			out.append(capitalizeFirstLetter(element));
		}// while

		return out.toString();
	}

	/**
	 * <p>
	 * Makes the first letter caps and the rest lowercase.
	 * </p>
	 * 
	 * <p>
	 * For example <code>fooBar</code> becomes <code>Foobar</code>.
	 * </p>
	 * 
	 * @param data
	 *            capitalize this
	 * @return String
	 */
	static public String firstLetterCaps(String data) {
		String firstLetter = data.substring(0, 1).toUpperCase();
		String restLetters = data.substring(1).toLowerCase();
		return firstLetter + restLetters;
	}

	/**
	 * <p>
	 * Capitalize the first letter but leave the rest as they are.
	 * </p>
	 * 
	 * <p>
	 * For example <code>fooBar</code> becomes <code>FooBar</code>.
	 * </p>
	 * 
	 * @param data
	 *            capitalize this
	 * @return String
	 */
	static public String capitalizeFirstLetter(String data) {
		String firstLetter = data.substring(0, 1).toUpperCase();
		String restLetters = data.substring(1);
		return firstLetter + restLetters;
	}

	/**
	 * Create a string array from a string separated by delim
	 * 
	 * @param line
	 *            the line to split
	 * @param delim
	 *            the delimter to split by
	 * @return a string array of the split fields
	 */
	public static String[] split(String line, String delim) {
		List list = new ArrayList();
		StringTokenizer t = new StringTokenizer(line, delim);
		while (t.hasMoreTokens()) {
			list.add(t.nextToken());
		}
		return (String[]) list.toArray(new String[list.size()]);
	}

	/**
	 * Chop i characters off the end of a string. This method assumes that any
	 * EOL characters in String s and the platform EOL will be the same. A 2
	 * character EOL will count as 1 character.
	 * 
	 * @param string
	 *            String to chop.
	 * @param i
	 *            Number of characters to chop.
	 * @return String with processed answer.
	 */
	public static String chop(String s, int i) {
		return chop(s, i, EOL);
	}

	/**
	 * Chop i characters off the end of a string. A 2 character EOL will count
	 * as 1 character.
	 * 
	 * @param string
	 *            String to chop.
	 * @param i
	 *            Number of characters to chop.
	 * @param eol
	 *            A String representing the EOL (end of line).
	 * @return String with processed answer.
	 */
	public static String chop(String s, int i, String eol) {
		if (i == 0 || s == null || eol == null) {
			return s;
		}

		int length = s.length();

		/*
		 * if it is a 2 char EOL and the string ends with it, nip it off. The
		 * EOL in this case is treated like 1 character
		 */
		if (eol.length() == 2 && s.endsWith(eol)) {
			length -= 2;
			i -= 1;
		}

		if (i > 0) {
			length -= i;
		}

		if (length < 0) {
			length = 0;
		}

		return s.substring(0, length);
	}

	/**
	 * Read the contents of a file and place them in a string object.
	 * 
	 * @param String
	 *            path to file.
	 * @return String contents of the file.
	 */
	public static String fileContentsToString(String file) {
		String contents = "";

		File f = new File(file);

		if (f.exists()) {
			try {
				FileReader fr = new FileReader(f);
				char[] template = new char[(int) f.length()];
				fr.read(template);
				contents = new String(template);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return contents;
	}

	/**
	 * Remove/collapse multiple newline characters.
	 * 
	 * @param String
	 *            string to collapse newlines in.
	 * @return String
	 */
	public static String collapseNewlines(String argStr) {
		char last = argStr.charAt(0);
		StringBuffer argBuf = new StringBuffer();

		for (int cIdx = 0; cIdx < argStr.length(); cIdx++) {
			char ch = argStr.charAt(cIdx);
			if (ch != '\n' || last != '\n') {
				argBuf.append(ch);
				last = ch;
			}
		}

		return argBuf.toString();
	}

	/**
	 * Remove/collapse multiple spaces.
	 * 
	 * @param String
	 *            string to remove multiple spaces from.
	 * @return String
	 */
	public static String collapseSpaces(String argStr) {
		char last = argStr.charAt(0);
		StringBuffer argBuf = new StringBuffer();

		for (int cIdx = 0; cIdx < argStr.length(); cIdx++) {
			char ch = argStr.charAt(cIdx);
			if (ch != ' ' || last != ' ') {
				argBuf.append(ch);
				last = ch;
			}
		}

		return argBuf.toString();
	}

	/**
	 * Replaces all instances of oldString with newString in line. Taken from
	 * the Jive forum package.
	 * 
	 * @param String
	 *            original string.
	 * @param String
	 *            string in line to replace.
	 * @param String
	 *            replace oldString with this.
	 * @return String string with replacements.
	 */
	public static final String sub(String line, String oldString,
			String newString) {
		int i = 0;
		if ((i = line.indexOf(oldString, i)) >= 0) {
			char[] line2 = line.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuffer buf = new StringBuffer(line2.length);
			buf.append(line2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			while ((i = line.indexOf(oldString, i)) > 0) {
				buf.append(line2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(line2, j, line2.length - j);
			return buf.toString();
		}
		return line;
	}

	/**
	 * Returns the output of printStackTrace as a String.
	 * 
	 * @param e
	 *            A Throwable.
	 * @return A String.
	 */
	public static final String stackTrace(Throwable e) {
		String foo = null;
		try {
			// And show the Error Screen.
			ByteArrayOutputStream ostr = new ByteArrayOutputStream();
			e.printStackTrace(new PrintWriter(ostr, true));
			foo = ostr.toString();
		} catch (Exception f) {
			// Do nothing.
		}
		return foo;
	}

	/**
	 * Return a context-relative path, beginning with a "/", that represents the
	 * canonical version of the specified path after ".." and "." elements are
	 * resolved out. If the specified path attempts to go outside the boundaries
	 * of the current context (i.e. too many ".." path elements are present),
	 * return <code>null</code> instead.
	 * 
	 * @param path
	 *            Path to be normalized
	 * @return String normalized path
	 */
	public static final String normalizePath(String path) {
		// Normalize the slashes and add leading slash if necessary
		String normalized = path;
		if (normalized.indexOf('\\') >= 0) {
			normalized = normalized.replace('\\', '/');
		}

		if (!normalized.startsWith("/")) {
			normalized = "/" + normalized;
		}

		// Resolve occurrences of "//" in the normalized path
		int index = 0;
		while (index > -1) {
			index = normalized.indexOf("//");
			if (index > -1) {
				normalized = normalized.substring(0, index)
						+ normalized.substring(index + 1);
			}
		}

		// Resolve occurrences of "%20" in the normalized path
		index = 0;
		while (index > -1) {
			index = normalized.indexOf("%20");
			if (index > -1) {
				normalized = normalized.substring(0, index) + " "
						+ normalized.substring(index + 3);
			}
		}

		// Resolve occurrences of "/./" in the normalized path
		index = 0;
		while (index > -1) {
			index = normalized.indexOf("/./");
			if (index > -1) {
				normalized = normalized.substring(0, index)
						+ normalized.substring(index + 2);
			}
		}

		// Resolve occurrences of "/../" in the normalized path
		index = 1;
		while (index > 0) {
			index = normalized.indexOf("/../");
			if (index > 0) {
				int index2 = normalized.lastIndexOf('/', index - 1);
				normalized = normalized.substring(0, index2)
						+ normalized.substring(index + 3);
			} else if (index == 0) {
				normalized = null; // Trying to go outside our context
			}
		}

		// Return the normalized path that we have completed
		return (normalized);
	}

	/**
	 * If state is true then return the trueString, else return the falseString.
	 * 
	 * @param boolean
	 * @param String
	 *            trueString
	 * @param String
	 *            falseString
	 */
	public String select(boolean state, String trueString, String falseString) {
		if (state) {
			return trueString;
		} else {
			return falseString;
		}
	}

	/**
	 * Check to see if all the string objects passed in are empty.
	 * 
	 * @param list
	 *            A list of {@link java.lang.String} objects.
	 * @return Whether all strings are empty.
	 */
	public boolean allEmpty(List list) {
		int size = list.size();

		for (int i = 0; i < size; i++) {
			if (list.get(i) != null && list.get(i).toString().length() > 0) {
				return false;
			}
		}
		return true;
	}

	public static String convertToProperties(String input)
			throws RuntimeException {
		StringBuffer velocitySetters = new StringBuffer("");
		if (input != null && input.trim().length() > 0) {
			StringTokenizer st = new StringTokenizer(input, ";\n\r");
			while (st.hasMoreTokens()) {
				String element = st.nextToken();
				// String assignedVariable = element.substring(0,
				// element.indexOf('='));
				// String expression =
				// element.substring(element.indexOf('=')+1);
				velocitySetters.append("#set( $").append(element)
						.append(" )\r\n");
			}
		}
		return velocitySetters.toString();
	}

	public static Map<String, String> convertToMap(String input)
			throws RuntimeException {
		Map<String, String> result = new HashMap<String, String>();
		if (input != null && input.trim().length() > 0) {
			StringTokenizer st = new StringTokenizer(input, ";\n\r");
			while (st.hasMoreTokens()) {
				String element = st.nextToken();
				int equalsSign = element.indexOf("=");
				String queryId = element.substring(0, equalsSign);
				String queryText = element.substring(equalsSign + 1);
				result.put(queryId, queryText);
			}
		}
		return result;
	}

	public static String getRandomString(int length) {
		Random rand = new Random(System.currentTimeMillis());
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			int pos = rand.nextInt(charset.length());
			sb.append(charset.charAt(pos));
		}
		return sb.toString();
	}

	/**
	 * Trim specified charcater from front of string
	 * 
	 * @param text
	 *            Text
	 * @param character
	 *            Character to remove
	 * @return Trimmed text
	 */
	public static String trimFront(String text, char character) {
		String normalizedText;
		int index;

		if (text == null) {
			return text;
		}

		normalizedText = text.trim();
		index = 0;

		while (normalizedText.charAt(index) == character) {
			index++;
		}
		return normalizedText.substring(index).trim();
	}

	/**
	 * Trim specified charcater from end of string
	 * 
	 * @param text
	 *            Text
	 * @param character
	 *            Character to remove
	 * @return Trimmed text
	 */
	public static String trimEnd(String text, char character) {
		String normalizedText;
		int index;

		if (text == null) {
			return text;
		}

		normalizedText = text.trim();
		index = normalizedText.length() - 1;

		while (normalizedText.charAt(index) == character) {
			if (--index < 0) {
				return "";
			}
		}
		return normalizedText.substring(0, index + 1).trim();
	}

	/**
	 * Trim specified charcater from both ends of a String
	 * 
	 * @param text
	 *            Text
	 * @param character
	 *            Character to remove
	 * @return Trimmed text
	 */
	public static String trimAll(String text, char character) {
		String normalizedText = trimFront(text, character);

		return trimEnd(normalizedText, character);
	}

	/**
	 * Cycle thru values based on currentValues (ex: cycleStrings("two", "one",
	 * "two", "three") return "three"). In null, last values, or not in list
	 * value is provided as currentValue then the first one will be returned.
	 * 
	 * @param currentValue
	 *            the current value from wich ask for the next
	 * @param values
	 *            list of values to cycle to
	 * @return the next values or the first if it is the end.
	 */
	public static String cycleStrings(String currentValue, String... values) {
		for (int i = 0; i < values.length; i++) {
			if (values[i].equals(currentValue)) {
				i++;
				if (i == values.length) {
					i = 0;
				}
				return values[i];
			}
		}
		return values[0];
	}

	/**
	 * Transform the given input Html string into a linear text string without html tags.
	 * @param htmlString html string
	 * @return stripped linear text string
	 */
	public static String stripHtml(String htmlString) {
		// Remove HTML tag from java String
		String noHTMLString = htmlString.replaceAll("\\<.*?\\>", "");

		// Remove Carriage return from java String
		noHTMLString = noHTMLString.replaceAll("\r", "<br/>");

		// Remove New line from java string and replace html break
		noHTMLString = noHTMLString.replaceAll("\n", " ");
		noHTMLString = noHTMLString.replaceAll("\'", "&#39;");
		noHTMLString = noHTMLString.replaceAll("\"", "&quot;");
		return noHTMLString;
	}
	
	public static String stripCrLf(String input) {
		return replace(replace(replace(input, "\n", ""), "\r", ""), "\"", "\\\"");
	}

	public static String replace(String text, String searchString, String replacement) {
		return org.apache.commons.lang.StringUtils.replace(text, searchString, replacement);
	}
	
}
