Index
StoryTemplate.java


/*
 *  "Net Libs" by Copyright (C) Michael Benson - 7/27/97
 *
 *  Loosely based on "Mac Libs" which is loosely based on the old
 *  "Mad Libs" books for creating stories from forms.
 *
 *  "StoryTemplate" contains the story templates.
 */

package NetLibs;

import java.awt.*;
import java.applet.Applet;
import java.util.*;
import java.io.*;
import java.net.*;

import COM.bensoft.base.*;
import COM.bensoft.widgets.*;


/**
 * StoryTemplate implements the class which handles reading the story
 * template text, getting the prompts, reading the dictionaries, and
 * choosing random words. 
 *  
 * @author	Michael Benson
 */
public class StoryTemplate implements NetLibsConst
{	
	private static final String		_templateDir = "templates/";
	private static final String		_wordDir = "words/";
	private static final String		_numberString = "number";
	private	Random					_rand;
	private	Vector					_wordList;
	private	Vector					_wordCat;

	/**
	 * StoryTemplate constructor.
	 */
	public StoryTemplate()
	{
		_rand = new Random();
		_wordList = new Vector();
		_wordCat = new Vector();
	}

	/**
	 * Reads in a story template.
	 *
	 * @param		applet		the parent applet (used for code base).
	 * @param		storyName	the name of the story template.
	 * @exception	IOException	if error reading template file.
	 * @return		string containing the story template.
	 */
	public String getTemplate (Applet applet, String storyName) throws IOException
	{
		// Create a URL for the template file name:
		URL templateURL = new URL(applet.getCodeBase().toString() + 
						_templateDir + storyName + ".txt");

		// Read in the raw story template:
		InputStream in = templateURL.openStream();
		byte[] data = new byte[MAXFILESIZE];
		int bytes_read = in.read(data);
		in.close();
		
		// Return the template as a string:
		return new String(data, 0);
	}

	/**
	 * Reads in a prompt file and separates the prompts.  Creates a
	 * vector in which each element is a prompt string.
	 *
	 * @param		applet		the parent applet (used for code base).
	 * @param		storyName	the name of the story template.
	 * @exception	IOException	if error reading prompt file.
	 * @exception	MalformedURLException if error finding prompt file.
	 * @return		vector containing each prompt string.
	 */
	public Vector getPrompts (Applet applet, String storyName) 
						throws IOException, MalformedURLException
	{
		Vector	prompts = new Vector();
		String	str;
		
		// Create a URL for the prompt file name:
		URL promptURL = new URL(applet.getCodeBase().toString() + 
						_templateDir + storyName + ".prompts");
		
		// Read in the whole prompt file:
		InputStream in = promptURL.openStream();
		byte[] data = new byte[MAXFILESIZE];
		int bytes_read = in.read(data);
		in.close();
				
		// Look through the buffer for the carriage returns and
		// separate the prompts:
		StringBuffer buf = new StringBuffer();
		for (int i = 0;  i < bytes_read;  i++) {
			if ((char)(data[i]) == '\n') {
				str = buf.toString();
				prompts.addElement(str);
				buf = new StringBuffer();
			} else {
				buf.append((char)(data[i]));
			}
		}
		return prompts;
	}
	
	/**
	 * Gets a random word of type prompt.  If number prompt
	 * is found, it will generate a random number.  Also, if the prompt
	 * is capitalized, the chosen word will be capitalized.
	 *
	 * @param		applet		the parent applet (used for code base).
	 * @exception	IOException	if error reading prompt file.
	 * @exception	MalformedURLException if error finding prompt file.
	 * @return		the word chosen at random.
	 */
	public String getRandomWord (Applet applet, String prompt)
						throws IOException, MalformedURLException
	{
		Vector	words;
		String	str, numstr;
		Integer	lower = null;
		Integer	upper = null;
		float	lo, up;

		if (prompt.startsWith(_numberString)) {
			// Generate a random number and convert to a string.
			// Find the limits in the prompt ("number from xxx to yyy"):
			StringTokenizer st = new StringTokenizer(prompt);
			int n = 0;
			while (st.hasMoreTokens()) {
				numstr = st.nextToken();
				if (n == 2) {
					// Lower limit:
					lower = new Integer(numstr);
				} else if (n == 4) {
					// Upper limit:
					upper = new Integer(numstr);
				}
				n++;
			}
			
			if (lower == null || upper == null) {
				lo = (float)1.0;
				up = (float)100.0;
			} else {
				lo = lower.floatValue();
				up = upper.floatValue() - lo + (float)1.0;
			}
			int m = (int)(_rand.nextFloat() * up + lo);
			Integer i = new Integer(m);
			str = i.toString();
		}
		else {
			// Determine if the prompt is capitalized:
			boolean isCapital = Character.isUpperCase(prompt.charAt(0));
			
			// Get the list of words either from the cache or the file:
			words = _getWordList(applet, prompt);
						
			// Get the nth word:
			float f = words.size();
			int n = (int)(_rand.nextFloat() * f);
			if (n >= words.size()) {
				// There is a tiny probability of generating a number
				// that's 1 too big, so let's insure against that:
				n = words.size() - 1;
			}
			str = (String)(words.elementAt(n));
			if (isCapital) {
				str = BensUtils.capitalizePhrase(str);
			}
		}
		return str;
	}
	
	/**
	 * Reads in a dictionary file and separates the words.  Creates
	 * a vector in which each element is a word string.  Caches dictionaries
	 * so the next time a particular word category is used, it will just
	 * use the cached version rather than re-reading the file.
	 *
	 * @param		applet		the parent applet (used for code base).
	 * @param		prompt		the word category (noun, verb, etc.)
	 * @exception	IOException	if error reading prompt file.
	 * @exception	MalformedURLException if error finding prompt file.
	 * @return		vector containing the list of words.
	 */
	private Vector _getWordList (Applet applet, String prompt)
						throws IOException, MalformedURLException
	{
		String	cprompt = prompt.toLowerCase();
		String	str;
		
		// First determine if this word category has already been cached:
		for (int i = 0;  i < _wordCat.size();  i++) {
			if (((String)(_wordCat.elementAt(i))).equals(cprompt)) {
				// Return the cached word list:
				return (Vector)(_wordList.elementAt(i));
			}
		}
		
		// Read in the word file.
		// Create a URL for the word file name:
		URL wordURL = new URL(applet.getCodeBase().toString() + 
						_wordDir + cprompt + ".words");
		
		// Read in the whole word file:
		InputStream in = wordURL.openStream();
		byte[] data = new byte[MAXFILESIZE];
		int bytes_read = in.read(data);
		in.close();

		// Look through the buffer for the carriage returns and
		// separate the words:
		Vector words = new Vector();
		StringBuffer buf = new StringBuffer();
		for (int i = 0;  i < bytes_read;  i++) {
			if ((char)(data[i]) == '\n') {
				str = buf.toString();
				words.addElement(str);
				buf = new StringBuffer();
			} else {
				buf.append((char)(data[i]));
			}
		}
		
		// Add the word list to the cached lists and return it:
		_wordCat.addElement(new String(cprompt));
		_wordList.addElement(words);
		return words;
	}

}