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): + *

+ *

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; +}