src/com/floreysoft/jmte/renderer/DefaultCollectionRenderer.java
@SuppressWarnings("rawtypes")
public class DefaultCollectionRenderer implements Renderer<Collection> {

	@Override
	public String render(Collection collection, Locale locale, Map<String, Object> model) {
		final String renderedResult;

		if (collection.size() == 0) {
			renderedResult = "";
		} else if (collection.size() == 1) {
			renderedResult = collection.iterator().next().toString();
		} else {
			renderedResult = collection.toString();
		}
		return renderedResult;

	}

}
src/com/floreysoft/jmte/message/SilentErrorHandler.java
public class SilentErrorHandler extends AbstractErrorHandler implements ErrorHandler {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void error(ErrorMessage errorMessage, Token token, Map<String, Object> parameters) throws ParseException {
		// Silent by design
	}
}
src/com/floreysoft/jmte/message/DefaultErrorHandler.java
public class DefaultErrorHandler extends AbstractErrorHandler implements ErrorHandler {
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void error(ErrorMessage errorMessage, Token token,
			Map<String, Object> parameters) throws ParseException {
		Message message = new ResourceBundleMessage(errorMessage.key).withModel(
				parameters).onToken(token);
		logger.severe(message.format(locale));
		throw new ParseException(message);
	}
}
src/com/floreysoft/jmte/util/Util.java
public class Util {

	public final static MiniParser MINI_PARSER = MiniParser.defaultInstance();
	public final static MiniParser RAW_MINI_PARSER = MiniParser
			.fullRawInstance();
	public final static MiniParser NO_QUOTE_MINI_PARSER = new MiniParser(
			MiniParser.DEFAULT_ESCAPE_CHAR, (char) -1, false, false, false);

	public final static MiniParser RAW_OUTPUT_MINI_PARSER = MiniParser
			.rawOutputInstance();

	/**
	 * Writes a string into a file.
	 * 
	 * @param string
	 *            the string
	 * @param file
	 *            the file
	 * @param charsetName
	 *            encoding of the file
	 */
	public static void stringToFile(String string, File file, String charsetName) {
		FileOutputStream fos = null;
		Writer writer = null;
		try {
			try {
				fos = new FileOutputStream(file);
				writer = new OutputStreamWriter(fos, charsetName);
				writer.write(string);
			} finally {
				if (writer != null) {
					writer.close();
				} else if (fos != null) {
					fos.close();
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Transforms a file into a string.
	 * 
	 * @param file
	 *            the file to be transformed
	 * @param charsetName
	 *            encoding of the file
	 * @return the string containing the content of the file
	 */
	public static String fileToString(File file, String charsetName) {
		FileInputStream fileInputStream = null;
		try {
			try {
				fileInputStream = new FileInputStream(file);
				return streamToString(fileInputStream, charsetName);
			} finally {
				if (fileInputStream != null) {
					fileInputStream.close();
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}

	/**
	 * Transforms a file into a string.
	 * 
	 * @param fileName
	 *            name of the file to be transformed
	 * @param charsetName
	 *            encoding of the file
	 * @return the string containing the content of the file
	 */
	public static String fileToString(String fileName, String charsetName) {
		return fileToString(new File(fileName), charsetName);
	}

	/**
	 * Transforms a stream into a string.
	 * 
	 * @param is
	 *            the stream to be transformed
	 * @param charsetName
	 *            encoding of the file
	 * @return the string containing the content of the stream
	 */
	public static String streamToString(InputStream is, String charsetName) {
		try {
			Reader r = null;
			try {
				r = new BufferedReader(new InputStreamReader(is, charsetName));
				return readerToString(r);
			} finally {
				if (r != null) {
					try {
						r.close();
					} catch (IOException e) {
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}

	/**
	 * Loads a stream from the classpath and transforms it into a string.
	 * 
	 * @param resourceName
	 *            the name of the resource to be transformed
	 * @param charsetName
	 *            encoding of the resource
	 * @return the string containing the content of the resource
	 * @see ClassLoader#getResourceAsStream(String)
	 */
	public static String resourceToString(String resourceName,
			String charsetName) {
		InputStream templateStream = Thread.currentThread()
				.getContextClassLoader().getResourceAsStream(resourceName);
		String template = Util.streamToString(templateStream, "UTF-8");
		return template;
	}

	/**
	 * Transforms a reader into a string.
	 * 
	 * @param reader
	 *            the reader to be transformed
	 * @return the string containing the content of the reader
	 */
	public static String readerToString(Reader reader) {
		try {
			StringBuilder sb = new StringBuilder();
			char[] buf = new char[1024];
			int numRead = 0;
			while ((numRead = reader.read(buf)) != -1) {
				sb.append(buf, 0, numRead);
			}
			return sb.toString();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}

	public static byte[] streamToBa(InputStream is) {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			byte[] buf = new byte[1024];
			int numRead = 0;
			while ((numRead = is.read(buf)) != -1) {
				baos.write(buf, 0, numRead);
			}
			byte[] byteArray = baos.toByteArray();
			return byteArray;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}

	/**
	 * Transforms any array to a matching list
	 * 
	 * @param value
	 *            something that might be an array
	 * @return List representation if passed in value was an array,
	 *         <code>null</code> otherwise
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static List<Object> arrayAsList(Object value) {
		if (value instanceof List) {
			return (List<Object>) value;
		}
		List list = null;
		if (value instanceof int[]) {
			list = new ArrayList();
			int[] array = (int[]) value;
			for (int i : array) {
				list.add(i);
			}
		} else if (value instanceof short[]) {
			list = new ArrayList();
			short[] array = (short[]) value;
			for (short i : array) {
				list.add(i);
			}
		} else if (value instanceof char[]) {
			list = new ArrayList();
			char[] array = (char[]) value;
			for (char i : array) {
				list.add(i);
			}
		} else if (value instanceof byte[]) {
			list = new ArrayList();
			byte[] array = (byte[]) value;
			for (byte i : array) {
				list.add(i);
			}
		} else if (value instanceof long[]) {
			list = new ArrayList();
			long[] array = (long[]) value;
			for (long i : array) {
				list.add(i);
			}
		} else if (value instanceof double[]) {
			list = new ArrayList();
			double[] array = (double[]) value;
			for (double i : array) {
				list.add(i);
			}
		} else if (value instanceof float[]) {
			list = new ArrayList();
			float[] array = (float[]) value;
			for (float i : array) {
				list.add(i);
			}
		} else if (value instanceof boolean[]) {
			list = new ArrayList();
			boolean[] array = (boolean[]) value;
			for (boolean i : array) {
				list.add(i);
			}
		} else if (value.getClass().isArray()) {
			Object[] array = (Object[]) value;
			list = Arrays.asList(array);
		}
		return list;
	}

	public static String extractArrayIndex(String objectName) {
		return objectName.substring(objectName.indexOf("[") + 1, objectName.indexOf("]"));
	}

	public static String extractArrayName(String objectName) {
		return objectName.substring(0, objectName.indexOf("["));
	}

	public static boolean isArrayAccess(String objectName) {
		return objectName.contains("[") || objectName.contains("]");
	}

	public static boolean isValidArrayAccess(String objectName) {
		final int arrayOpenIndex = objectName.indexOf("[");
		final int arrayCloseIndex = objectName.indexOf("]");
		return arrayOpenIndex != -1 && arrayCloseIndex != -1 && arrayOpenIndex < arrayCloseIndex;
	}

	/**
	 * Trims off white space from the beginning of a string.
	 * 
	 * @param input
	 *            the string to be trimmed
	 * @return the trimmed string
	 */
	public static String trimFront(String input) {
		int i = 0;
		while (i < input.length() && Character.isWhitespace(input.charAt(i)))
			i++;
		return input.substring(i);
	}

	/**
	 * Scans the input and spits out begin/end pairs telling you where
	 * expressions can be found.
	 * 
	 * @param input
	 *            the input
	 * @return the begin/end pairs telling you where expressions can be found
	 */
	public static List<StartEndPair> scan(String input, String splitStart,
			String splitEnd, boolean useEscaping) {
		List<StartEndPair> result = new ArrayList<StartEndPair>();
		int fromIndex = 0;
		while (true) {
			int exprStart = input.indexOf(splitStart, fromIndex);
			if (exprStart == -1) {
				break;
			}
			if (useEscaping && Util.isEscaped(input, exprStart)) {
				fromIndex = exprStart + splitStart.length();
				continue;
			}

			exprStart += splitStart.length();
			int exprEnd = input.indexOf(splitEnd, exprStart);
			if (exprEnd == -1) {
				break;
			}
			while (useEscaping && Util.isEscaped(input, exprEnd)) {
				exprEnd = input.indexOf(splitEnd, exprEnd + splitEnd.length());
			}

			fromIndex = exprEnd + splitEnd.length();

			StartEndPair startEndPair = new StartEndPair(exprStart, exprEnd);
			result.add(startEndPair);
		}
		return result;
	}

	/**
	 * A character is escaped when it is preceded by an unescaped slash.
	 */
	static boolean isEscaped(String input, int index) {
		return isEscaped(input, index, MiniParser.DEFAULT_ESCAPE_CHAR);
	}

	static boolean isEscaped(String input, int index, char escapeCharacter) {
		boolean escaped;
		int leftOfIndex = index - 1;
		if (leftOfIndex >= 0) {
			if (input.charAt(leftOfIndex) == escapeCharacter) {
				int leftOfleftOfIndex = leftOfIndex - 1;
				escaped = leftOfleftOfIndex < 0
						|| input.charAt(leftOfleftOfIndex) != escapeCharacter;
			} else {
				escaped = false;
			}
		} else {
			escaped = false;
		}
		return escaped;
	}

	@SuppressWarnings("unchecked")
	public static <T> Set<T> asSet(T... a) {
		return a != null ? new HashSet(Arrays.asList(a)) : Collections
				.emptySet();
	}

	public static String unifyNewlines(String source) {
		final String regex = "\\r?\\n";
		final String clearedSource = source.replaceAll(regex, "\n");
		return clearedSource;
	}

}
src/com/floreysoft/jmte/renderer/DefaultMapRenderer.java
@SuppressWarnings("rawtypes")
public class DefaultMapRenderer implements Renderer<Map> {

	@Override
	public String render(Map map, Locale locale, Map<String, Object> model) {
		final String renderedResult;

		if (map.size() == 0) {
			renderedResult = "";
		} else if (map.size() == 1) {
			renderedResult = map.values().iterator().next().toString();
		} else {
			renderedResult = map.toString();
		}
		return renderedResult;

	}

}
src/com/floreysoft/jmte/encoder/Encoder.java
public interface Encoder {
	/**
	 * Encodes a string into another one.
	 * 
	 * @param string
	 *            string to encode
	 * @return the encoded string
	 */
	public String encode(String string);
}
src/com/floreysoft/jmte/message/ErrorEntry.java
public class ErrorEntry {
    public final Message formattedMessage;
    public final ErrorMessage errorMessage;
    public final Token token;
    public final Map<String, Object> parameters;

    public ErrorEntry(Message formattedMessage, ErrorMessage errorMessage, Token token, Map<String, Object> parameters) {
        this.formattedMessage = formattedMessage;
        this.errorMessage = errorMessage;
        this.token = token;
        this.parameters = parameters;
    }
}
src/com/floreysoft/jmte/renderer/DefaultObjectRenderer.java
public class DefaultObjectRenderer implements Renderer<Object> {

	@Override
	public String render(Object value, Locale locale, Map<String, Object> model) {
		final String renderedResult;

		if (value instanceof String) {
			renderedResult = (String) value;
		} else {
			final List<Object> arrayAsList = Util.arrayAsList(value);
			if (arrayAsList != null) {
				renderedResult = arrayAsList.size() > 0 ? arrayAsList.get(0)
						.toString() : "";
			} else {
				renderedResult = value.toString();
			}
		}
		return renderedResult;
	}
}
src/com/floreysoft/jmte/token/Token.java
public interface Token extends Cloneable {

	/**
	 * Returns the text of the token.
	 * 
	 * @return the text
	 */
	public String getText();

	public int getLine();

	public int getColumn();

	public String getSourceName();
	
	public Object evaluate(TemplateContext context);
	
	public int getTokenIndex();
	
	public String emit();

	public Object getAnnotation();
}
src/com/floreysoft/jmte/renderer/DefaultIterableRenderer.java
@SuppressWarnings("unchecked")
public class DefaultIterableRenderer implements Renderer<Iterable> {

	@Override
	public String render(Iterable iterable, Locale locale, Map<String, Object> model) {
		final String renderedResult;

		final Iterator<?> iterator = iterable.iterator();
		renderedResult = iterator.hasNext() ? iterator.next().toString() : "";
		return renderedResult;

	}

}


Question: package com.floreysoft.jmte;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import com.floreysoft.jmte.encoder.Encoder;
import com.floreysoft.jmte.message.DefaultErrorHandler;
import com.floreysoft.jmte.message.ErrorEntry;
import com.floreysoft.jmte.message.SilentErrorHandler;
import com.floreysoft.jmte.renderer.DefaultCollectionRenderer;
import com.floreysoft.jmte.renderer.DefaultIterableRenderer;
import com.floreysoft.jmte.renderer.DefaultMapRenderer;
import com.floreysoft.jmte.renderer.DefaultObjectRenderer;
import com.floreysoft.jmte.template.*;
import com.floreysoft.jmte.token.IfToken;
import com.floreysoft.jmte.token.Token;
import com.floreysoft.jmte.util.Tool;
import com.floreysoft.jmte.util.Util;



/**
 * <p>
 * The template engine - <b>THIS IS WHERE YOU START LOOKING</b>.
 * </p>
 * 
 * <p>
 * Usually this is the only class you need calling
 * {@link #transform(String, Map)}. Like this
 * </p>
 * 
 * <pre>
 * Engine engine = new Engine();
 * String transformed = engine.transform(input, model);
 * </pre>
 * 
 * <p>
 * You have to provide the template input written in the template language and a
 * model from String to Object. Maybe like this
 * </p>
 * 
 * <pre>
 * String input = &quot;${name}&quot;;
 * Map&lt;String, Object&gt; model = new HashMap&lt;String, Object&gt;();
 * model.put(&quot;name&quot;, &quot;Minimal Template Engine&quot;);
 * Engine engine = new Engine();
 * String transformed = engine.transform(input, model);
 * assert (transformed.equals(&quot;Minimal Template Engine&quot;));
 * </pre>
 * 
 * where <code>input</code> contains the template and <code>model</code> the
 * model. <br>
 * 
 * <p>
 * Use {@link #setUseCompilation(boolean)} to switch on compilation mode. This
 * will compile the template into Java byte code before execution. Especially
 * when the template is used often this will speed up the execution by a factor
 * between 2 and 10. However, each compiled template results in a new class
 * definition and a new globally cached singleton instance of it.
 * </p>
 * 
 * <p>
 * This class is thread safe.
 * </p>
 * 
 * @see ErrorHandler
 * @see Tool
 * @see Renderer
 * @see NamedRenderer
 * @see ModelAdaptor
 * @see ProcessListener
 */
public final class Engine implements RendererRegistry {

	public final static String VERSION = "@version@";

	public static Engine createEngine() {
		Engine engine = new Engine();
		return engine;
	}

	private String exprStartToken = "${";
	private String exprEndToken = "}";
	private double expansionSizeFactor = 2;
	private ErrorHandler errorHandler = new DefaultErrorHandler();
	private ModelAdaptor modelAdaptor = new DefaultModelAdaptor();
	private Encoder encoder = null;
	private OutputAppender outputAppender = new DefaultOutputAppender();

	private final Map<Class<?>, Renderer<?>> renderers = new HashMap<Class<?>, Renderer<?>>();
	private final Map<Class<?>, Renderer<?>> resolvedRendererCache = new HashMap<Class<?>, Renderer<?>>();

	private final Map<String, AnnotationProcessor<?>> annotationProcessors = new HashMap<String, AnnotationProcessor<?>>();

	private final Map<String, NamedRenderer> namedRenderers = new HashMap<String, NamedRenderer>();
	private final Map<Class<?>, Set<NamedRenderer>> namedRenderersForClass = new HashMap<Class<?>, Set<NamedRenderer>>();

	/**
	 * Creates a new engine having <code>${</code> and <code>}</code> as start
	 * and end strings for expressions.
	 */
	public Engine() {
		init();
	}

	private void init() {
		registerRenderer(Object.class, new DefaultObjectRenderer());
		registerRenderer(Map.class, new DefaultMapRenderer());
		registerRenderer(Collection.class, new DefaultCollectionRenderer());
Answer: 		registerRenderer(Iterable.class, new DefaultIterableRenderer());