package finiteAutomata;
import java.util.Vector;
/**
 * A non-empty set of symbols.  Alphabets are naturally associated
 * with both finite automata (input symbols) and grammars (terminal
 * symbols).
 * 
 * @author Barbara Wahl 
 * @version Fall 2007
 */
public class Alphabet {		
	// ** DATA FIELDS **
	/**
	 * alphabet symbols
	 */
	protected StringBuffer alpha;  	
	
	// ** CONSTRUCTOR **
	/**
	 * creates an Alphabet from the characters in a given string.
	 * @param inString String containing the desired alphabet characters
	 * in the desired lexicographic order
	 * @pre inString is a non-empty string
	 * @pre inString has no repeated characters
	 * @pre inString has no whitespace characters
	 * @post creates an Alphabet object whose symbols are the characters 
	 * in inString
	 */
	public Alphabet(String inString)
	{
		Assert.pre(inString.length() > 0, "inString is a non-empty string");
		Assert.pre(!Util.hasRepeat(inString),
				"inString has no repeated characters");
		Assert.pre(!Util.hasWhiteSpace(inString),
				"inString has no whitespace characters");
		alpha = new StringBuffer(inString); 
	}
	
	// ** PUBLIC METHODS **	
	/**
	 * Returns number of symbols in this Alphabet.
	 * @return alpha.length()
	 */
	public int size()
	{ 
		return alpha.length(); 
	}
	
	/**
	 * Determines if a given character is a symbol in this Alphabet.
	 * @param c char to be located
	 * @return true iff the given character is in this Alphabet
	 */
	public boolean contains(char c)
	{
		// indexOf returns a non-negative result if the char
		// is found in the String
		return (alpha.toString().indexOf(c) >= 0);
	}
	
	/**
	 * Determines the position of a given character in this Alphabet.
	 * @param c char to be located
	 * @return non-negative integer position of c in this 
	 * Alphabet, or -1 if c not found
	 */
	public int indexOf(char c)
	{
		return alpha.toString().indexOf(c);
	}
	
	/**
	 * Returns the i-th symbol in this Alphabet.
	 * @param i int position of interest
	 * @return char at position i in this Alphabet
	 * @pre 0 <= i < this.size()
	 */
	public char charAt(int i)
	{
		Assert.pre(0 <= i && i < this.size(),"0 <= i < alpha.size");
		return alpha.charAt(i);
	}
	
	/**
	 * Converts this Alphabet to a String of symbols.
	 * @return a String version of this Alphabet with no punctuation
	 * or spaces
	 */
	public String toString()
	// post: returns a String copy of alphabet symbols
	{ 
		return alpha.toString(); 
	}
	
	/**
	 * Converts this Alphabet to a String of symbols punctuated with braces 
	 * and commas.
	 * @return a pretty String version of this Alphabet
	 */
	public String toPrettyString()
	{
		StringBuffer returnString = new StringBuffer();
		returnString.append("{");
		for(int i=0; i<alpha.length() - 1; i++)
			returnString.append(alpha.charAt(i)+",");
		returnString.append(alpha.charAt(alpha.length()-1) + "}");
		return returnString.toString();
	}	
	
	/**
	 * Converts each character in the given String to its position in this
	 * Alphabet.
	 * @param inString the String of characters to be converted
	 * @param array of integers for returning the list
	 * of positions
	 * @return true iff every character in inString is a symbol
	 * in this Alphabet
	 * @pre inString is not null, and array.length >= inString.length()
	 * @post if every character in inString is a symbol in this Alphabet, 
	 * the initial segment of array will contain the position numbers 
	 * of the corresponding characters in inString
	 */
	public boolean indexArray(String inString, int[] array)
	{
		Assert.pre(array.length >= inString.length(),
				"array is at least as long as inString");
		int index;
		// for each character in inString ...
		for(int i=0; i<inString.length(); i++)
		{
			// use String class methods to find position of the symbol 
			// in alpha
			char c = inString.charAt(i);
			index = alpha.toString().indexOf(c);

			if(index < 0)     // if symbol is not in this Alphabet...
				return false; // failure!
 
			array[i] = index; // else, record position in array
		}
		return true; // success!
	}

	/**
	 * Prints a given Vector of StringBuffer objects.
	 * @param v Vector of StringBuffer objects to be printed
	 * @pre v is not null and has no null elements
	 * @post prints the strings in v, one per line; if the empty string 
	 * is encountered, prints "epsilon" instead of ""
	 */
	public static void printLanguage(Vector v)
	{
		String s;
		for(int i=0; i < v.size(); i++)
		{
			s = ((StringBuffer)v.get(i)).toString();
			Util.printString(s);
			System.out.println();
		}
	}
	
	/**
	 * Generates the language of all strings over this Alphabet up to 
	 * a given length.
	 * @param n maximum string length
	 * @return  Vector containing language alpha* (in lexicographic order) 
	 * up through strings of length n; language strings are stored in the 
	 * Vector as StringBuffer objects.
	 * @pre n >= 0
	 */
	public Vector getLanguage(int n)

		{
			Assert.pre(n >= 0, "n is non-negative");
			Vector v = new Vector();
			v.add(new StringBuffer("")); // first add the empty string
			int finger = 0; // pointer for generating more strings
			StringBuffer old = (StringBuffer) v.get(finger);
			StringBuffer newString; // currently a null reference
				
			while(old.length() < n)
			// use old to generate more strings in language
			{	
				// for each symbol in Alphabet...
				for(int i=0; i<alpha.length(); i++)
 				{
 					newString = new StringBuffer(old.toString());
 					newString.append(alpha.charAt(i));
 					v.add(newString);
 				}			
				finger++; // advance finger
				// update "old" reference
				old = (StringBuffer) v.get(finger); 
			}		
			return v; 
		}
		
	// ** TEST METHOD **
	public static void main(String[] args) {
		
		// create two different Alphabets, A1 and A2
		Alphabet A1 = new Alphabet("x");
		System.out.println("A1 = " + A1.toPrettyString());
		Alphabet A2 = new Alphabet("01");
		System.out.println("A2 = " + A2.toPrettyString());
		
		// For n = 0, 1, 2, 3 call language on A1 & display 
		// the strings in A1* up through length n using printLanguage.
		// Use System.out.println to print helpful headings!!
		for(int n=0; n<4; n++)
		{
			System.out.println("Here is A1* " +
					"up through length " + n + ":");
			Alphabet.printLanguage(A1.getLanguage(n));
			System.out.println();
		}

		// For n = 0, 1, 2, 3 call language on A2 & display 
		// the strings in A1* up through length n using printLanguage.
		// Use System.out.println to print helpful headings!!
		for(int n=0; n<4; n++)
		{
			System.out.println("Here is A2* " +
					"up through length " + n + ":");
			Alphabet.printLanguage(A2.getLanguage(n));
			System.out.println();
		}
	}
}