-
Notifications
You must be signed in to change notification settings - Fork 0
Embedding Tea
Previous - Why Use Tea | Table of Contents | Next - Lexical Structure
Tea is designed to work within a Java environment, and the Tea language itself is patterned after the Java language. Tea accesses properties from objects that follow the JavaBean conventions, and because Java is case-sensitive with respect to names, so too is Tea. Standard Java objects are used in Tea for strings, numbers, collections, etc.
Tea differs from Java in many respects, mostly due to the reduced set of features. Variables in Tea are implicitly types, but passed-in parameters are specified just like in Java. Optionally, a variable type can be declared. This is useful in situations where the object is passed in via a request attribute. Tea supports both primitive types and objects, but from the template they all appear as objects. Tea converts between primitive types and their peers (int ↔ Integer) automatically. Other special type conversion rules (see section 6 Type Conversion) are also applied automatically.
Tea templates compile into Java class files and execute just the same within a Java Virtual Machine. The Tea compiler does not generate intermediate Java files and hand them off to a Java compiler, but it instead generates the class files directly. Still, the resulting class files are as efficient as any handwritten Java code.
Because no Java compiler is used, Tea can be distributed much more easily. All that’s required is a Java2 runtime environment. Sun's JRE can be used, but the SDK isn’t necessary.
When compiled to class files, the Tea source file information, including line numbers, is preserved. If an exception is thrown from a template or from a method called by a template, an entry appears in the stack trace referring directly to the template. The source file information contains the ".tea" extension and the line number matches up directly with the templates source file. Tea templates should be compatible with most Java debugging environments.
Tea isn’t designed as a standalone system. Whereas a Java program can be launched from a main method in a class, this concept doesn’t really make sense in Tea. Tea requires a hosting system to be compiled to and run in.
The Tea implementation basically supports two components: the compiler and the runtime. A host environment may integrate with either of these components or with pieces of them. Integrating with both components results in a complete Tea integration.
Creating a new host environment requires some knowledge of the Tea implementation. The following is an example of a very simple complete integration, but it is enough to get one started in creating a custom host environment. Most developers, however, will use an existing environment like the TeaServlet, and won’t need to know how to create a host environment.
import java.io.File;
import java.io.IOException;
import org.teatrove.tea.runtime.TemplateLoader;
import org.teatrove.tea.runtime.DefaultContext;
import org.teatrove.tea.util.FileCompiler;
import org.teatrove.tea.util.ConsoleErrorReporter;
import org.teatrove.trove.util.ClassInjector;
public class SimpleTea {
/**
* Test program executes a template named Test that accepts a
* single String argument. For example,
* <pre>
* <% template Test(String arg) %>
* Message is: <% arg %>
* </pre>
*/
public static void main(String[] args) throws Exception {
SimpleTea tea = new SimpleTea(new File("."), "simple");
String result =
tea.executeTemplate("Test", new Object[] {"Hello World"});
System.out.println(result);
}
private String[] mNames;
private int mErrorCount;
private TemplateLoader mLoader;
public SimpleTea(File rootSourceDir, String rootPackage)
throws IOException
{
ClassInjector injector = new ClassInjector((File)null, rootPackage);
FileCompiler compiler = new FileCompiler
(rootSourceDir, rootPackage, null, injector);
compiler.setRuntimeContext(SimpleContext.class);
compiler.addErrorListener(new ConsoleErrorReporter(System.err));
compiler.setForceCompile(true);
mNames = compiler.compileAll(true);
mErrorCount = compiler.getErrorCount();
mLoader = new TemplateLoader(injector, rootPackage);
}
public int getErrorCount() {
return mErrorCount;
}
public String[] getTemplateNames() {
return (String[])mNames.clone();
}
public TemplateLoader.Template getTemplate(String name)
throws ClassNotFoundException, NoSuchMethodException
{
return mLoader.getTemplate(name);
}
public String executeTemplate(String name, Object[] parameters)
throws Exception
{
StringBuffer buf = new StringBuffer(256);
SimpleContext context = new SimpleContext(buf);
getTemplate(name).execute(context, parameters);
return buf.toString();
}
public static class SimpleContext extends DefaultContext {
private StringBuffer mBuffer;
public SimpleContext(StringBuffer buffer) {
mBuffer = buffer;
}
public void print(Object obj) {
mBuffer.append(toString(obj));
}
}
}
Since Tea templates have no direct way of obtaining data, a host environment is responsible for providing it. There are two ways in which this can be done. The host may pass in objects to the template parameters and it can provide a custom context that supplies functions.
A context is a special class that Tea templates require for execution. It defines functions that are callable by templates and it also provides the methods for templates to output data. The print functions can be called directly, but templates usually don’t do this. See the section on functions (5.1 Calling Functions and Templates) and expression statements (4.1.7 Expression Statement) for more information.
The SimpleTea example doesn't provide any special context functions other than the required print method and defaults, but it does pass in objects to the template via its parameters. Once a template has obtained an object, it can access bean properties and objects from it.
Although Tea is designed primarily for textual output, you may notice that there are no restrictions on how a context can be defined and what objects the print method may contain. Tea-based systems can also be created for producing image data or even for building GUIs. The ability to pass in a block of code to a function also helps in this capacity (see section 4.1.5 Substitution Statement).
Previous - Why Use Tea | Table of Contents | Next - Lexical Structure