diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5c09613
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,136 @@
+
+
+ 4.0.0
+ com.samskivert
+ jmustache
+ jar
+ 1.1-SNAPSHOT
+ jmustache
+ A Java implementation of the Mustache templating language.
+ http://code.google.com/p/jmustache/
+
+ http://code.google.com/p/jmustache/issues/list
+
+
+
+ org.sonatype.oss
+ oss-parent
+ 5
+
+
+
+
+ GNU Lesser General Public License (LGPL), Version 2.1
+ http://www.fsf.org/licensing/licenses/lgpl.txt
+ repo
+
+
+
+
+
+ samskivert
+ Michael Bayne
+ mdb@samskivert.com
+
+
+
+
+ scm:svn:http://jmustache.googlecode.com/svn/trunk/
+ scm:svn:https://jmustache.googlecode.com/svn/trunk/
+ http://jmustache.googlecode.com/svn/trunk/
+
+
+
+
+ junit
+ junit
+ 4.8.1
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+ 1.5
+ 1.5
+ true
+ true
+ true
+
+ -Xlint" "-Xlint:-serial" "-Xlint:-path
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 2.4.3
+
+ UTF-8
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.6
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.7
+
+ true
+ public
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.3.1
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+ 2.3.1
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.5
+
+
+
+
+
+
+ release-sign-artifacts
+
+ performReleasetrue
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.1
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+ mdb@samskivert.com
+
+
+
+
+
+
+
diff --git a/src/main/java/com/samskivert/mustache/Mustache.java b/src/main/java/com/samskivert/mustache/Mustache.java
new file mode 100644
index 0000000..c133ddf
--- /dev/null
+++ b/src/main/java/com/samskivert/mustache/Mustache.java
@@ -0,0 +1,110 @@
+//
+// $Id$
+
+package com.samskivert.mustache;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Provides Mustache templating services.
+ */
+public class Mustache
+{
+ /**
+ * Compiles the supplied template into a repeatedly executable intermediate form.
+ */
+ public static Template compile (String template)
+ {
+ return compile(new StringReader(template));
+ }
+
+ /**
+ * Compiles the supplied template into a repeatedly executable intermediate form.
+ */
+ public static Template compile (Reader template)
+ {
+ return null; // TODO
+ }
+
+ private Mustache () {} // no instantiateski
+
+ /** A simple segment that reproduces a string. */
+ protected static class StringSegment extends Template.Segment {
+ public StringSegment (String text) {
+ _text = text;
+ }
+
+ @Override public void execute (Object ctx, Writer out) {
+ write(out, _text);
+ }
+
+ protected final String _text;
+ }
+
+ /** A segment that substitutes the contents of a variable. */
+ protected static class VariableSegment extends Template.Segment {
+ public VariableSegment (String name) {
+ _name = name;
+ }
+
+ @Override public void execute (Object ctx, Writer out) {
+ Object value = getValue(ctx, _name);
+ // TODO: configurable behavior on missing values
+ if (value != null) {
+ write(out, String.valueOf(value));
+ }
+ }
+
+ protected final String _name;
+ }
+
+ /** A segment that represents a section. */
+ protected static class SectionSegment extends Template.Segment {
+ public SectionSegment (String name, Template.Segment[] segs) {
+ _name = name;
+ _segs = segs;
+ }
+
+ @Override public void execute (Object ctx, Writer out) {
+ Object value = getValue(ctx, _name);
+ if (value == null) {
+ return; // TODO: configurable behavior on missing values
+ }
+ if (value instanceof Iterable>) {
+ Iterable> iable = (Iterable>)value;
+ for (Object elem : iable) {
+ executeSegs(elem, out);
+ }
+ } else if (value instanceof Boolean) {
+ if ((Boolean)value) {
+ executeSegs(ctx, out);
+ }
+ } else if (value.getClass().isArray()) {
+ for (int ii = 0, ll = Array.getLength(value); ii < ll; ii++) {
+ executeSegs(Array.get(value, ii), out);
+ }
+ } else if (value instanceof Iterator>) {
+ Iterator> iter = (Iterator>)value;
+ while (iter.hasNext()) {
+ executeSegs(iter.next(), out);
+ }
+ } else {
+ executeSegs(value, out);
+ }
+ }
+
+ protected void executeSegs (Object ctx, Writer out) {
+ for (Template.Segment seg : _segs) {
+ seg.execute(ctx, out);
+ }
+ }
+
+ protected String _name;
+ protected Template.Segment[] _segs;
+ }
+}
diff --git a/src/main/java/com/samskivert/mustache/MustacheException.java b/src/main/java/com/samskivert/mustache/MustacheException.java
new file mode 100644
index 0000000..b70bc6e
--- /dev/null
+++ b/src/main/java/com/samskivert/mustache/MustacheException.java
@@ -0,0 +1,22 @@
+//
+// $Id$
+
+package com.samskivert.mustache;
+
+/**
+ * An exception thrown when an error occurs parsing or executing a Mustache template.
+ */
+public class MustacheException extends RuntimeException
+{
+ public MustacheException (String message) {
+ super(message);
+ }
+
+ public MustacheException (Throwable cause) {
+ super(cause);
+ }
+
+ public MustacheException (String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/com/samskivert/mustache/Template.java b/src/main/java/com/samskivert/mustache/Template.java
new file mode 100644
index 0000000..f3e5a03
--- /dev/null
+++ b/src/main/java/com/samskivert/mustache/Template.java
@@ -0,0 +1,87 @@
+//
+// $Id$
+
+package com.samskivert.mustache;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * Represents a compiled template.
+ */
+public class Template
+{
+ /**
+ * Executes this template with the supplied data, writing the results to the supplied writer.
+ *
+ *
The data can be any tree of objects. Given a name foo, the following
+ * mechanisms are supported for resolving its value (and are sought in this order):
+ *
+ *
If the object is a {@link Map}, {@link Map#get} will be called with the string
+ * foo as the key.
+ *
A method named foo in the supplied object (with non-void return value).
+ *
A method named getFoo in the supplied object (with non-void return value).
+ *
A field named foo in the supplied object.
+ *
+ *
The field type, method return type, or map value type should correspond to the
+ * desired behavior if the resolved name corresponds to a section. {@link Boolean} is used for
+ * showing or hiding sections without binding a sub-context. Arrays, {@link Iterator} and
+ * {@link Iterable} implementations are used for sections that repeat, with the context bound
+ * to the elements of the array, iterator or iterable. Lambdas are current unsupported, though
+ * they would be easy enough to add if desire exists. See the Mustache documentation for more
+ * details on section behavior.
+ *
+ * @throws MustacheException if an error occurs while writing the template.
+ */
+ public void execute (Object data, Writer out) throws MustacheException
+ {
+ for (Segment seg : _segs) {
+ seg.execute(data, out);
+ }
+ }
+
+ /**
+ * Executes this template with the supplied data, returning the results as a string. See {@link
+ * #execute(Object, Writer).
+ *
+ * @throws MustacheException if an error occurs while writing the template.
+ */
+ public String execute (Object data) throws MustacheException
+ {
+ StringWriter out = new StringWriter();
+ execute(data, out);
+ return out.toString();
+ }
+
+ protected Template (Segment[] segs)
+ {
+ _segs = segs;
+ }
+
+ /** A template is broken into segments. */
+ protected static abstract class Segment
+ {
+ abstract void execute (Object ctx, Writer out);
+
+ protected Object getValue (Object ctx, String name) {
+ // TODO: support things other than values
+ return ((Map,?>)ctx).get(name);
+ }
+
+ protected static void write (Writer out, String data) {
+ try {
+ out.write(data);
+ } catch (IOException ioe) {
+ throw new MustacheException(ioe);
+ }
+ }
+ }
+
+ protected final Segment[] _segs;
+}