/*

 * Delivered to the U.S. Government in Accordance with Contract FA8650-10-C-7080.

 *

 * EXPORT CONTROLLED

 * ECCN#   EAR99

 * Warning: This document contains technical data which is controlled under the

 * Bureau of Industry & Security's Export Administration Regulations and may not be

 * exported or re-exported without authorization by the Bureau of Industry & Security.

 * Diversion contrary to US law is prohibited.

 *

 * (c) 2011 United Technologies Corp.

 * Developed with the sponsorship of the Defense Advanced Research Projects Agency (DARPA).

 *

 * Permission is hereby granted, free of charge, to any person obtaining a copy of this data,

 * including any software or models in source or binary form, as well as any drawings, 

 * specifications, and documentation (collectively "the Data"), to deal in the Data without 

 * restriction, including without limitation the rights to use, copy, modify, merge, publish,

 * distribute, sublicense, and/or sell copies of the Data, and to permit persons to whom the Data

 * is furnished to do so, subject to the following conditions:

 * 

 * The above copyright notice and this permission notice shall be included in all copies or

 * substantial portions of the Data.

 *

 * THE DATA IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING

 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND

 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, SPONSORS, DEVELOPERS, CONTRIBUTORS, OR

 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION

 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE DATA OR THE

 * USE OR OTHER DEALINGS IN THE DATA.

*/



package aee.util;



import java.util.ArrayList;

import java.util.Arrays;

import java.util.HashSet;

import java.util.List;

import java.util.Set;



/**

 * A grab bag of string utilities

 */

public class StringUtils {

	

	/**

	 * Insert escape characters into a string

	 * @param text        The source string to get the escape characters

	 * @param escapeList  The list of source characters to be escaped. If escapeChar

	 *                    is not part of this list, it is added to it

	 * @param escapeChar  The escape character

	 * @return            text with escape characters added

	 */

	public static String escape(String text, String escapeList, char escapeChar) {

		

		// Must always escape the escape character itself

		if (escapeList.indexOf(escapeChar) < 0)

			escapeList += escapeChar;

		

		// Find the next character in text to escape

		int pos = 0;

		do {

			pos = indexOf(text, escapeList, pos);

			if (pos >= 0) {

				

				// Escape it and advance past the character

				text = String.format("%s%c%s", text.substring(0, pos), 

						                           escapeChar,

						                           text.substring(pos));

				pos += 2;

			}

		

		// Continue as long as we escaped something and we still have some text left to check

		} while (pos >= 0  &&  pos < text.length());

		

		return text;

	}

	

	/**

	 * Finds the first position in text of ANY character in charList

	 * @param text     The string to search

	 * @param charList The list of characters to find in text

	 * @param pos      The beginning index in text to search

	 * @return         -1 if none of charList are in text, else the smallest position of the

	 *                 charList character found

	 *                 

	 *                       0123456789

	 * For example, if text="abcdefghij" and charList="eio" and pos=0, then the value

	 * returned is 4 since the e from eio was found before i and o. If pos=5, then the

	 * value returned would be 8.       

	 */

	public static int indexOf(String text, String charList, int pos) {

		

		// Assume we won't find anything

		int lowestPos = text.length() + 1;

		

		// Cycle thru the character list

		for (int i = 0; i < charList.length(); i++) {

			char next    = charList.charAt(i);

			

			// Save the minimum index found

			int  nextpos = text.indexOf(next, pos);

			if (nextpos >= 0)

				lowestPos = Math.min(lowestPos, nextpos);

		}

		

		// Did we find anything?

		if (lowestPos > text.length())

			lowestPos = -1;

		return lowestPos;

	}

	

	/**

	 * Uppercase the first character in a string

	 * @param text  The input string

	 * @return  Text with the first letter uppercased

	 */	

	 public static String upFirst(String text) {

	  	StringBuilder buffer = new StringBuilder();

	  	if (text.length() > 0) {

	  		buffer.append(text.substring(0,1).toUpperCase());

	  		if (text.length() > 1)

	  			buffer.append(text.substring(1));

	  	}

	  	return buffer.toString();

	  }

	 

	 /**

	  * Join an array into a single string

	  * 

	  * @param array  The array to be joined

	  * @param delim  The join delimiter

	  * @return A string in the format array[0].delim.array[1].delim...array[n]

	  */

	 public static String join(Object[] array, String delim) {

		 return joinWrapped(array, delim, null, null);

	 }

	 

	 /**

	  * Join an array into a single string, but add a leading and trailing delimiter

	  * to each array element. For example, you could define the prefix string to be "["

	  * and the suffix string to be "]", then each array element would be [element]. A

	  * more common use is to set prefix = suffix = "\"" so each element is enclosed in quotes.

	  * 

	  * @param array  The array to be joined

	  * @param delim  The join delimiter

	  * @param prefix The leading enclose string. Can be null if not wanted

	  * @param suffix The trailing enclose string. Can be null if not wanted

	  * @return A string in the format 

	  *         pre.array[0].suf.delim.pre.array[1].suf.delim...pre.array[n].suf

	  */

	 public static String joinWrapped(Object[] array, String delim, String prefix, String suffix) {

		 String        eL  = (prefix == null)? "" : prefix;

		 String        eT  = (suffix == null)? "" : suffix;

		 

		 StringBuilder buf = new StringBuilder();

		 String        dlm = "";

		 for (Object element : array) {

			 buf.append(String.format("%s%s%s%s", dlm, eL, element.toString(), eT));

			 dlm = delim;

		 }

		 return buf.toString();	 

	 }

	 

	 /**

	  * Check if any candidate is a member of an array of legal values

	  * @param candidates   The candidates to check

	  * @param legalValues  The legal values

	  * @return true if there is some candidate that is a member of legalValues

	  */

	 public static boolean isMember(List<String> candidates, String[] legalValues) {

		 Set<String> legalSet = new HashSet<String>(legalValues.length);

	   legalSet.addAll(Arrays.asList(legalValues));

	   

	   boolean valid = false;

	   for (String candidate : candidates) {

	  	 if (legalSet.contains(candidate)) {

	  		 valid = true;

	  		 break;

	  	 }

	   }

	   return valid;

	 }

	 

	 /**

	  * Check if a candidate is a member of an array of legal values

	  * @param candidate    The candidate to check

	  * @param legalValues  The legal values

	  * @return true if the candidate is a member of legalValues

	  */

	 public static boolean isMember(String candidate, String[] legalValues) {

		 List<String> candidates = new ArrayList<String>(1);

		 candidates.add(candidate);

		 return isMember(candidates, legalValues);

	 }

	 

	 /**

	  * Find the candidate index in a list of legal values

	  * @param candidate    The member to find

	  * @param legalValues  List of legal values

	  * @return             Index of candidate in legalValues or -1 if not there

	  */

	 public static int getMember(String candidate, String[] legalValues) {

		 int member = -1;

		 for (int i = 0; member < 0  &&  i < legalValues.length; i++) {

			 if (legalValues[i].equals(candidate))

				 member = i;

		 }

		 return member;

	 }



	/**

	 * Split a string on a delimiter and return the trimmed tokens as a list

	 * @param tokenText  The string to split

	 * @param delim      The split regular expression

	 * @return           Tokens as a list

	 */

	public static List<String> splitAsList(String tokenText, String delim) {

		return Arrays.asList(split(tokenText, delim));

	}

	

	/**

	 * Find all white space delimited tokens in a string

	 * @param text  The text to tokenize

	 * @return      Array of all whitespace delimited tokens

	 */

	public static String[] getWStokens(String text) {

		return split(text, "\\s+");

	}



	/**

	 * Safe string split. Handles empty input

	 * @param tokenText  The string to split

	 * @param delim      The split regular expression

	 * @return           Tokens as an array

	 */

	public static String[] split(String tokenText, String delim) {

		tokenText = tokenText.trim();

		String[] tokens = ("".equals(tokenText))? new String[0] 

		                                        : tokenText.split(delim);

		for (int i = 0; i < tokens.length; i++) {

			tokens[i] = tokens[i].trim();

		}

		return tokens;

	}

	

	/**

	 * Check if text string starts with any of the possible choices

	 * @param text

	 * @param choices

	 * @return true if the text starts with any of the choices

	 */

	public static boolean startsWith(String text, String[] choices)

	{

	  boolean found = false;

	  if ( text == null ) return found;

	  if ( choices == null ) return found;

	  for (String choice : choices) {

	    if ( text.startsWith(choice)) {

	    	found = true;

	    	break;

	    }

	  }     

	  return found;

	}

	

	/**

	 * Check if a string has any whitespace characters

	 * @param text The text to check

	 * @return true if it has whitespace

	 */

	public static boolean hasWhitespace(String text) {

		// Note that split doesn't count trailing whitespace

		// hence tacking on a "."

		return ((text+".").split("\\s").length > 1);

	}

	

	/**

	 * "Visualize" whitespace. Converts blanks to "_", tabs to \t, newlines to \n, and carriage

	 * returns to \r

	 * @param text  The text to visualize

	 * @return      The converted text

	 */

	public static String visualize(String text) {

		text = text.replaceAll(" ",  "_");

		text = text.replaceAll("\t", "\\\\t");

		text = text.replaceAll("\n", "\\\\n");

		text = text.replaceAll("\r", "\\\\r");

		return text;

	}

	

	/**

	 * Check if any text in list is in value

	 * @param value  A text string

	 * @param list   A list of strings

	 * @return true if there is a list[i] such that value.contains(list[i])

	 */

	public static boolean contains(String value, String[] list) {

		boolean status = false;

		for (String listItem : list) {

			if (value.contains(listItem)) {

				status = true;

				break;

			}

		}

		return status;

	}

	

	/**

	 * Compute the number of digits in an integer

	 * @param value  The integer

	 * @return       The number of display digits (including optional negative sign)

	 */

	public static int mag10(int value) {

		int ndigits = 1;

		if (value < 0) {

			++ndigits;

			value *= -1;

		}

		

		while ((value/=10) >= 1) {

			++ndigits;

		}

		return ndigits;

	}



	/**

	 * Truncate a string on the left (and prefix with ... if its too big to fit

	 * in the supplied width

	 * @param text   The input text

	 * @param width  The width for the text. Must be bigger than 3

	 * @return Either the text if its small enough or the text left truncated

	 */

	public static String leftTrunc(String text, int width) {

		String output = text;

		int    len    = text.length();

		if (len > width) {

			if (width <= 3)

				throw new IllegalArgumentException("Invalid width in leftTrunc()");

			output = "..." + text.substring(Math.max(len-width+3,0));

		}

		return output;

	}

	

	/**

	 * Find the character position of the first difference between two strings

	 * @param str1 First string

	 * @param str2 Second string

	 * @return Position of first difference between them or -1 if no difference at all

	 */

	public static int diff1(String str1, String str2) {

		int diffpos = -1;

		int L1      = str1.length();

		int L2      = str2.length();

		int len     = Math.min(L1, L2);

		for (int i = 0; i < len; i++) {

			char c1 = str1.charAt(i);

			char c2 = str2.charAt(i);

			if (c1 != c2) {

				diffpos = i;

				break;

			}

		} // i

		if (diffpos == -1) {

			if (L1 != L2)

				diffpos = len;

		}

		return diffpos;

	}

	

	/**

	 * This assumes a filename is input. The name is returned minus that ending 

	 * extension. For example, if the input is "jim.csv", then "jim" is returned

	 * @param fn Filename with optional ending extension

	 * @return   Filename without the extension

	 */

	public static String removeExt(String fn) {

		int lastDot = fn.lastIndexOf('.');

		if (lastDot != -1)

			fn = fn.substring(0, lastDot);

		return fn;

	}

	

	/**

	 * Trim trailing delimiters from a string. If a string ends with ANY of the

	 * characters in dlms, it is trimmed

	 * @param source  The source string

	 * @param dlms    The delimiters. If null, the string "\\/" is used

	 * @return The source minus the trailing delimiter (if any)

	 */

	public static String trimTrailingDlm(String source, String dlms) {

		if (dlms == null) dlms = "\\/";

		char lastChar = source.charAt(source.length()-1);

		for (char c : dlms.toCharArray()) {

			if (c == lastChar) {

				source = source.substring(0, source.length() - 1);

				break;

			}

		}

		return source;

	}

}

