/* DirectiveParser.java -- Parser for directives like repeat.
**
** Copyright (C) 2003 Eric Laroche.  All rights reserved.
**
** @author Eric Laroche <laroche@lrdev.com>
** @version 1.1 @(#)$Id: DirectiveParser.java,v 1.1 2003/02/16 12:40:36 laroche Exp $
**
** This program is free software;
** you can redistribute it and/or modify it.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**
*/

// note: no wildcard imports

// import Callback;

/** DirectiveParser: Parser for directives like repeat.
*
* @author Eric Laroche <laroche@lrdev.com>
* @version 1.1 @(#)$Id: DirectiveParser.java,v 1.1 2003/02/16 12:40:36 laroche Exp $
*
* <p>
* Parse directives have the format "12[xyz]" which will call the
* provided callback object twelve times.
*/
public class DirectiveParser
{
	/** Version.
	*/
	private final static String m_version =
		"Version: 1.1 @(#)$Id: DirectiveParser.java,v 1.1 2003/02/16 12:40:36 laroche Exp $";

	/** Author.
	*/
	private final static String m_author =
		"Author: Eric Laroche <laroche@lrdev.com>";

	// ----------------

	/** Parse and execute a string for directives.
	*/
	public static void execute(String s, Callback callbackObject)
	{
		// null is ok, just do nothing
		if (s == null) {
			return;
		}

		int i = 0;
		for (;;) {

			// handle possible plain stuff first

			// search for first character handled by us
			int j = i;
			for (; j < s.length(); j++) {
				if (
					Character.isDigit(s.charAt(j)) ||
					s.charAt(j) == '[' ||
					s.charAt(j) == ']'
				) {
					break;
				}
			}

			if (j > i) {
				// plain stuff
				callbackObject.callback(s.substring(i, j));
			}

			if (j == s.length()) {
				// done
				break;
			}

			// do the possible argument
			// note: no negative arguments allowed
			// anyway: '-' may be used as a command, not a sign
			int argument = 1; // default is once
			if (Character.isDigit(s.charAt(j))) {
				for (argument = 0; j < s.length() && Character.isDigit(s.charAt(j)); j++) {
					argument *= 10;
					argument += Character.digit(s.charAt(j), 10);
				}
			}

			if (j == s.length()) {
				// if now j is larger than at the beginning of numerical argument parsing,
				// such a numerical argument must have been consumed
				throw new IllegalArgumentException("isolated numerical argument");
			}

			// find end of command (sequence)
			int k = j + 1; // default is 1 (non-sequence)
			int m = k; // default next token (non-sequence)
			if (s.charAt(j) == '[') { // ']'
				// skip braket
				j++;
				// find the _corresponding_ closing braket
				k = j;
				int braketCount = 1;
				for (;;) {
					if (k == s.length()) {
						throw new IllegalArgumentException("unmatched braket");
					}
					switch (s.charAt(k)) {
					case '[' :
					// ']'
						braketCount++;
						break;
					// '['
					case ']' :
						braketCount--;
						break;
					}
					if (braketCount == 0) {
						break;
					}
					k++;
				}
				m = k + 1; // next token (sequence)
			}

			// execute sequence
			// note: it may be inefficient to call the parser recursively in the case
			// of directive-less contents
			for (int n = 0; n < argument; n++) {
				// recursion
				execute(s.substring(j, k), callbackObject);
			}

			i = m;
		}
	}
}

