Method from freemarker.core.Environment Detail: |
public Object __getitem__(String key) throws TemplateModelException {
return BeansWrapper.getDefaultInstance().unwrap(getVariable(key));
}
|
public void __setitem__(String key,
Object o) throws TemplateException {
setGlobalVariable(key, getObjectWrapper().wrap(o));
}
|
void clearLastReturnValue() {
this.lastReturnValue = null;
}
|
void fallback() throws IOException, TemplateException {
TemplateModel macroOrTransform = getNodeProcessor(currentNodeName, currentNodeNS, nodeNamespaceIndex);
if (macroOrTransform instanceof Macro) {
visit((Macro) macroOrTransform, null, null, null, null);
}
else if (macroOrTransform instanceof TemplateTransformModel) {
visit(null, (TemplateTransformModel) macroOrTransform, null);
}
}
|
String formatDate(Date date,
int type) throws TemplateModelException {
DateFormat df = getDateFormatObject(type);
if(df == null) {
throw new TemplateModelException("Can't convert the date to string, because it is not known which parts of the date variable are in use. Use ?date, ?time or ?datetime built-in, or ?string.< format > or ?string(format) built-in with this date.");
}
return df.format(date);
}
|
String formatNumber(Number number) {
if(numberFormat == null) {
numberFormat = getNumberFormatObject(getNumberFormat());
}
return numberFormat.format(number);
}
|
public NumberFormat getCNumberFormat() {
// It can't be cached in a static field, because DecimalFormat-s aren't
// thread-safe.
if (cNumberFormat == null) {
cNumberFormat = (DecimalFormat) C_NUMBER_FORMAT.clone();
}
return cNumberFormat;
}
Returns the NumberFormat used for the c built-in.
This is always US English "0.################" , without
grouping and without superfluous decimal separator. |
Collator getCollator() {
if(collator == null) {
collator = Collator.getInstance(getLocale());
}
return collator;
}
|
public Configuration getConfiguration() {
return getTemplate().getConfiguration();
}
|
public static Environment getCurrentEnvironment() {
return (Environment)threadEnv.get();
}
Retrieves the environment object associated with the current
thread. Data model implementations that need access to the
environment can call this method to obtain the environment object
that represents the template processing that is currently running
on the current thread. |
Macro.Context getCurrentMacroContext() {
return currentMacroContext;
}
|
public Environment.Namespace getCurrentNamespace() {
return currentNamespace;
}
Returns the main name-space.
This is correspondent of FTL .namespace hash. |
String getCurrentRecoveredErrorMesssage() throws TemplateException {
if(recoveredErrorStack.isEmpty()) {
throw new TemplateException(
".error is not available outside of a < #recover > block", this);
}
return (String) recoveredErrorStack.get(recoveredErrorStack.size() -1);
}
|
public TemplateNodeModel getCurrentVisitorNode() {
return currentVisitorNode;
}
|
public TemplateHashModel getDataModel() {
final TemplateHashModel result = new TemplateHashModel() {
public boolean isEmpty() {
return false;
}
public TemplateModel get(String key) throws TemplateModelException {
TemplateModel value = rootDataModel.get(key);
if (value == null) {
value = getConfiguration().getSharedVariable(key);
}
return value;
}
};
if (rootDataModel instanceof TemplateHashModelEx) {
return new TemplateHashModelEx() {
public boolean isEmpty() throws TemplateModelException {
return result.isEmpty();
}
public TemplateModel get(String key) throws TemplateModelException {
return result.get(key);
}
//NB: The methods below do not take into account
// configuration shared variables even though
// the hash will return them, if only for BWC reasons
public TemplateCollectionModel values() throws TemplateModelException {
return ((TemplateHashModelEx) rootDataModel).values();
}
public TemplateCollectionModel keys() throws TemplateModelException {
return ((TemplateHashModelEx) rootDataModel).keys();
}
public int size() throws TemplateModelException {
return ((TemplateHashModelEx) rootDataModel).size();
}
};
}
return result;
}
|
DateFormat getDateFormatObject(int dateType) throws TemplateModelException {
switch(dateType) {
case TemplateDateModel.UNKNOWN: {
return null;
}
case TemplateDateModel.TIME: {
if(timeFormat == null) {
timeFormat = getDateFormatObject(dateType, getTimeFormat());
}
return timeFormat;
}
case TemplateDateModel.DATE: {
if(dateFormat == null) {
dateFormat = getDateFormatObject(dateType, getDateFormat());
}
return dateFormat;
}
case TemplateDateModel.DATETIME: {
if(dateTimeFormat == null) {
dateTimeFormat = getDateFormatObject(dateType, getDateTimeFormat());
}
return dateTimeFormat;
}
default: {
throw new TemplateModelException("Unrecognized date type " + dateType);
}
}
}
|
DateFormat getDateFormatObject(int dateType,
String pattern) throws TemplateModelException {
if(dateFormats == null) {
dateFormats = new Map[4];
dateFormats[TemplateDateModel.UNKNOWN] = new HashMap();
dateFormats[TemplateDateModel.TIME] = new HashMap();
dateFormats[TemplateDateModel.DATE] = new HashMap();
dateFormats[TemplateDateModel.DATETIME] = new HashMap();
}
Map typedDateFormat = dateFormats[dateType];
DateFormat format = (DateFormat) typedDateFormat.get(pattern);
if(format != null) {
return format;
}
// Get format from global format cache
synchronized(localizedDateFormats) {
Locale locale = getLocale();
TimeZone timeZone = getTimeZone();
DateFormatKey fk = new DateFormatKey(dateType, pattern, locale, timeZone);
format = (DateFormat)localizedDateFormats.get(fk);
if(format == null) {
// Add format to global format cache. Note this is
// globally done once per locale per pattern.
StringTokenizer tok = new StringTokenizer(pattern, "_");
int style = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : DateFormat.DEFAULT;
if(style != -1) {
switch(dateType) {
case TemplateDateModel.UNKNOWN: {
throw new TemplateModelException(
"Can't convert the date to string using a " +
"built-in format, because it is not known which " +
"parts of the date variable are in use. Use " +
"?date, ?time or ?datetime built-in, or " +
"?string.< format > or ?string(< format >) built-in "+
"with explicit formatting pattern with this date.");
}
case TemplateDateModel.TIME: {
format = DateFormat.getTimeInstance(style, locale);
break;
}
case TemplateDateModel.DATE: {
format = DateFormat.getDateInstance(style, locale);
break;
}
case TemplateDateModel.DATETIME: {
int timestyle = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : style;
if(timestyle != -1) {
format = DateFormat.getDateTimeInstance(style, timestyle, locale);
}
break;
}
}
}
if(format == null) {
try {
format = new SimpleDateFormat(pattern, locale);
}
catch(IllegalArgumentException e) {
throw new TemplateModelException("Can't parse " + pattern + " to a date format.", e);
}
}
format.setTimeZone(timeZone);
localizedDateFormats.put(fk, format);
}
}
// Clone it and store the clone in the local cache
format = (DateFormat)format.clone();
typedDateFormat.put(pattern, format);
return format;
}
|
public String getDefaultNS() {
return currentNamespace.getTemplate().getDefaultNS();
}
|
String getEffectiveURLEscapingCharset() {
if (!urlEscapingCharsetCached) {
cachedURLEscapingCharset = getURLEscapingCharset();
if (cachedURLEscapingCharset == null) {
cachedURLEscapingCharset = getOutputEncoding();
}
urlEscapingCharsetCached = true;
}
return cachedURLEscapingCharset;
}
Returns the name of the charset that should be used for URL encoding.
This will be null if the information is not available.
The function caches the return value, so it is quick to call it
repeately. |
public Environment.Namespace getGlobalNamespace() {
return globalNamespace;
}
Returns a fictitious name-space that contains the globally visible variables
that were created in the template, but not the variables of the data-model.
There is no such thing in FTL; this strange method was added because of the
JSP taglib support, since this imaginary name-space contains the page-scope
attributes. |
public TemplateModel getGlobalVariable(String name) throws TemplateModelException {
TemplateModel result = globalNamespace.get(name);
if (result == null) {
result = rootDataModel.get(name);
}
if (result == null) {
result = getConfiguration().getSharedVariable(name);
}
return result;
}
Returns the globally visible variable of the given name (or null).
This is correspondent to FTL .globals.name .
This will first look at variables that were assigned globally via:
<#global ...> and then at the data model exposed to the template. |
public TemplateHashModel getGlobalVariables() {
return new TemplateHashModel() {
public boolean isEmpty() {
return false;
}
public TemplateModel get(String key) throws TemplateModelException {
TemplateModel result = globalNamespace.get(key);
if (result == null) {
result = rootDataModel.get(key);
}
if (result == null) {
result = getConfiguration().getSharedVariable(key);
}
return result;
}
};
}
Returns the read-only hash of globally visible variables.
This is the correspondent of FTL .globals hash.
That is, you see the variables created with
<#global ...> , and the variables of the data-model.
To create new global variables, use setGlobalVariable . |
public Set getKnownVariableNames() throws TemplateModelException {
// shared vars.
Set set = getConfiguration().getSharedVariableNames();
// root hash
if (rootDataModel instanceof TemplateHashModelEx) {
TemplateModelIterator rootNames =
((TemplateHashModelEx) rootDataModel).keys().iterator();
while(rootNames.hasNext()) {
set.add(((TemplateScalarModel)rootNames.next()).getAsString());
}
}
// globals
for (TemplateModelIterator tmi = globalNamespace.keys().iterator(); tmi.hasNext();) {
set.add(((TemplateScalarModel) tmi.next()).getAsString());
}
// current name-space
for (TemplateModelIterator tmi = currentNamespace.keys().iterator(); tmi.hasNext();) {
set.add(((TemplateScalarModel) tmi.next()).getAsString());
}
// locals and loop vars
if(currentMacroContext != null) {
set.addAll(currentMacroContext.getLocalVariableNames());
}
if (localContextStack != null) {
for (int i = localContextStack.size()-1; i >=0; i--) {
LocalContext lc = (LocalContext) localContextStack.get(i);
set.addAll(lc.getLocalVariableNames());
}
}
return set;
}
Returns a set of variable names that are known at the time of call. This
includes names of all shared variables in the Configuration ,
names of all global variables that were assigned during the template processing,
names of all variables in the current name-space, names of all local variables
and loop variables. If the passed root data model implements the
TemplateHashModelEx interface, then all names it retrieves through a call to
TemplateHashModelEx#keys() method are returned as well.
The method returns a new Set object on each call that is completely
disconnected from the Environment. That is, modifying the set will have
no effect on the Environment object. |
TemplateModel getLastReturnValue() {
return lastReturnValue;
}
|
ArrayList getLocalContextStack() {
return localContextStack;
}
|
public TemplateModel getLocalVariable(String name) throws TemplateModelException {
if (localContextStack != null) {
for (int i = localContextStack.size()-1; i >=0; i--) {
LocalContext lc = (LocalContext) localContextStack.get(i);
TemplateModel tm = lc.getLocalVariable(name);
if (tm != null) {
return tm;
}
}
}
return currentMacroContext == null ? null : currentMacroContext.getLocalVariable(name);
}
Returns the loop or macro local variable corresponding to this
variable name. Possibly null.
(Note that the misnomer is kept for backward compatibility: loop variables
are not local variables according to our terminology.) |
Environment.Namespace getMacroNamespace(Macro macro) {
return (Namespace) macroToNamespaceLookup.get(macro);
}
|
public Environment.Namespace getMainNamespace() {
return mainNamespace;
}
Returns the main name-space.
This is correspondent of FTL .main hash. |
public Environment.Namespace getNamespace(String name) {
if (name.startsWith("/")) name = name.substring(1);
if (loadedLibs != null) {
return (Namespace) loadedLibs.get(name);
} else {
return null;
}
}
Returns the name-space for the name if exists, or null. |
public String getNamespaceForPrefix(String prefix) {
return currentNamespace.getTemplate().getNamespaceForPrefix(prefix);
}
|
TemplateModel getNodeProcessor(TemplateNodeModel node) throws TemplateException {
String nodeName = node.getNodeName();
if (nodeName == null) {
throw new TemplateException("Node name is null.", this);
}
TemplateModel result = getNodeProcessor(nodeName, node.getNodeNamespace(), 0);
if (result == null) {
String type = node.getNodeType();
/* DD: Original version: */
if (type == null) {
type = "default";
}
result = getNodeProcessor("@" + type, null, 0);
/* DD: Jonathan's non-BC version and IMHO otherwise wrong version:
if (type != null) {
result = getNodeProcessor("@" + type, null, 0);
}
if (result == null) {
result = getNodeProcessor("@default", null, 0);
}
*/
}
return result;
}
|
NumberFormat getNumberFormatObject(String pattern) {
if(numberFormats == null) {
numberFormats = new HashMap();
}
NumberFormat format = (NumberFormat) numberFormats.get(pattern);
if(format != null)
{
return format;
}
// Get format from global format cache
synchronized(localizedNumberFormats)
{
Locale locale = getLocale();
NumberFormatKey fk = new NumberFormatKey(pattern, locale);
format = (NumberFormat)localizedNumberFormats.get(fk);
if(format == null)
{
// Add format to global format cache. Note this is
// globally done once per locale per pattern.
if("number".equals(pattern))
{
format = NumberFormat.getNumberInstance(locale);
}
else if("currency".equals(pattern))
{
format = NumberFormat.getCurrencyInstance(locale);
}
else if("percent".equals(pattern))
{
format = NumberFormat.getPercentInstance(locale);
}
else
{
format = new DecimalFormat(pattern, new DecimalFormatSymbols(getLocale()));
}
localizedNumberFormats.put(fk, format);
}
}
// Clone it and store the clone in the local cache
format = (NumberFormat)format.clone();
numberFormats.put(pattern, format);
return format;
}
|
public Writer getOut() {
return out;
}
|
public String getPrefixForNamespace(String nsURI) {
return currentNamespace.getTemplate().getPrefixForNamespace(nsURI);
}
|
public Template getTemplate() {
return (Template)getParent();
}
Retrieves the currently processed template. |
public Template getTemplateForImporting(String name) throws IOException {
return getTemplateForInclusion(name, null, true);
}
|
public Template getTemplateForInclusion(String name,
String encoding,
boolean parse) throws IOException {
if (encoding == null) {
encoding = getTemplate().getEncoding();
}
if (encoding == null) {
encoding = getConfiguration().getEncoding(this.getLocale());
}
return getConfiguration().getTemplate(name, getLocale(), encoding, parse);
}
Gets a template for inclusion; used with #include(Template includedTemplate) .
The advantage over simply using config.getTemplate(...) is that it chooses
the default encoding as the include directive does. |
TemplateTransformModel getTransform(Expression exp) throws TemplateException {
TemplateTransformModel ttm = null;
TemplateModel tm = exp.getAsTemplateModel(this);
if (tm instanceof TemplateTransformModel) {
ttm = (TemplateTransformModel) tm;
}
else if (exp instanceof Identifier) {
tm = getConfiguration().getSharedVariable(exp.toString());
if (tm instanceof TemplateTransformModel) {
ttm = (TemplateTransformModel) tm;
}
}
return ttm;
}
|
public TemplateModel getVariable(String name) throws TemplateModelException {
TemplateModel result = getLocalVariable(name);
if (result == null) {
result = currentNamespace.get(name);
}
if (result == null) {
result = getGlobalVariable(name);
}
return result;
}
Returns the variable that is visible in this context.
This is the correspondent to an FTL top-level variable reading expression.
That is, it tries to find the the variable in this order:
- An loop variable (if we're in a loop or user defined directive body) such as foo_has_next
- A local variable (if we're in a macro)
- A variable defined in the current namespace (say, via <#assign ...>)
- A variable defined globally (say, via <#global ....>)
- Variable in the data model:
- A variable in the root hash that was exposed to this
rendering environment in the Template.process(...) call
- A shared variable set in the configuration via a call to Configuration.setSharedVariable(...)
|
public Environment.Namespace importLib(String name,
String namespace) throws IOException, TemplateException {
return importLib(getTemplateForImporting(name), namespace);
}
Emulates import directive, except that name must be tempate
root relative.
It's the same as importLib(getTemplateForImporting(name), namespace) .
But, you may want to separately call these two methods, so you can determine the source of
exceptions more precisely, and thus achieve more intelligent error handling. |
public Environment.Namespace importLib(Template loadedTemplate,
String namespace) throws IOException, TemplateException {
if (loadedLibs == null) {
loadedLibs = new HashMap();
}
String templateName = loadedTemplate.getName();
Namespace existingNamespace = (Namespace) loadedLibs.get(templateName);
if (existingNamespace != null) {
if (namespace != null) {
setVariable(namespace, existingNamespace);
}
}
else {
Namespace newNamespace = new Namespace(loadedTemplate);
if (namespace != null) {
currentNamespace.put(namespace, newNamespace);
if (currentNamespace == mainNamespace) {
globalNamespace.put(namespace, newNamespace);
}
}
Namespace prevNamespace = this.currentNamespace;
this.currentNamespace = newNamespace;
loadedLibs.put(templateName, currentNamespace);
Writer prevOut = out;
this.out = NULL_WRITER;
try {
include(loadedTemplate);
} finally {
this.out = prevOut;
this.currentNamespace = prevNamespace;
}
}
return (Namespace) loadedLibs.get(templateName);
}
Emulates import directive. |
void importMacros(Template template) {
for (Iterator it = template.getMacros().values().iterator(); it.hasNext();) {
visitMacroDef((Macro) it.next());
}
}
|
public void include(Template includedTemplate) throws IOException, TemplateException {
Template prevTemplate = getTemplate();
setParent(includedTemplate);
importMacros(includedTemplate);
try {
visit(includedTemplate.getRootTreeNode());
}
finally {
setParent(prevTemplate);
}
}
Processes a Template in the context of this Environment , including its
output in the Environment 's Writer. |
public void include(String name,
String encoding,
boolean parse) throws IOException, TemplateException {
include(getTemplateForInclusion(name, encoding, parse));
}
Emulates include directive, except that name must be tempate
root relative.
It's the same as include(getTemplateForInclusion(name, encoding, parse)) .
But, you may want to separately call these two methods, so you can determine the source of
exceptions more precisely, and thus achieve more intelligent error handling. |
public void outputInstructionStack(PrintWriter pw) {
pw.println("----------");
ListIterator iter = elementStack.listIterator(elementStack.size());
if(iter.hasPrevious()) {
pw.print("== > ");
TemplateElement prev = (TemplateElement) iter.previous();
pw.print(prev.getDescription());
pw.print(" [");
pw.print(prev.getStartLocation());
pw.println("]");
}
while(iter.hasPrevious()) {
TemplateElement prev = (TemplateElement) iter.previous();
if (prev instanceof UnifiedCall || prev instanceof Include) {
String location = prev.getDescription() + " [" + prev.getStartLocation() + "]";
if(location != null && location.length() > 0) {
pw.print(" in ");
pw.println(location);
}
}
}
pw.println("----------");
pw.flush();
}
Outputs the instruction stack. Useful for debugging.
TemplateException s incorporate this information in their stack
traces. |
int parseDateStyleToken(String token) {
if("short".equals(token)) {
return DateFormat.SHORT;
}
if("medium".equals(token)) {
return DateFormat.MEDIUM;
}
if("long".equals(token)) {
return DateFormat.LONG;
}
if("full".equals(token)) {
return DateFormat.FULL;
}
return -1;
}
|
public void process() throws IOException, TemplateException {
Object savedEnv = threadEnv.get();
threadEnv.set(this);
try {
// Cached values from a previous execution are possibly outdated.
clearCachedValues();
try {
visit(getTemplate().getRootTreeNode());
// Do not flush if there was an exception.
out.flush();
} finally {
// It's just to allow the GC to free memory...
clearCachedValues();
}
} finally {
threadEnv.set(savedEnv);
}
}
Processes the template to which this environment belongs. |
void recurse(TemplateNodeModel node,
TemplateSequenceModel namespaces) throws IOException, TemplateException {
if (node == null) {
node = this.getCurrentVisitorNode();
if (node == null) {
throw new TemplateModelException(
"The target node of recursion is missing or null.");
}
}
TemplateSequenceModel children = node.getChildNodes();
if (children == null) return;
for (int i=0; i< children.size(); i++) {
TemplateNodeModel child = (TemplateNodeModel) children.get(i);
if (child != null) {
visit(child, namespaces);
}
}
}
|
String renderElementToString(TemplateElement te) throws IOException, TemplateException {
Writer prevOut = out;
try {
StringWriter sw = new StringWriter();
this.out = sw;
visit(te);
return sw.toString();
}
finally {
this.out = prevOut;
}
}
|
public void setCurrentVisitorNode(TemplateNodeModel node) {
currentVisitorNode = node;
}
sets TemplateNodeModel as the current visitor node. .current_node |
public void setDateFormat(String formatName) {
super.setDateFormat(formatName);
dateFormat = null;
}
|
public void setDateTimeFormat(String formatName) {
super.setDateTimeFormat(formatName);
dateTimeFormat = null;
}
|
public void setGlobalVariable(String name,
TemplateModel model) {
globalNamespace.put(name, model);
}
Sets a variable that is visible globally.
This is correspondent to FTL <#global name=model> .
This can be considered a convenient shorthand for:
getGlobalNamespace().put(name, model) |
void setLastReturnValue(TemplateModel lastReturnValue) {
this.lastReturnValue = lastReturnValue;
}
|
public void setLocalVariable(String name,
TemplateModel model) {
if(currentMacroContext == null) {
throw new IllegalStateException("Not executing macro body");
}
currentMacroContext.setLocalVar(name, model);
}
Sets a local variable (one effective only during a macro invocation).
This is correspondent to FTL <#local name=model> . |
public void setLocale(Locale locale) {
super.setLocale(locale);
// Clear local format cache
numberFormats = null;
numberFormat = null;
dateFormats = null;
timeFormat = dateFormat = dateTimeFormat = null;
collator = null;
}
|
public void setNumberFormat(String formatName) {
super.setNumberFormat(formatName);
numberFormat = null;
}
|
public void setOut(Writer out) {
this.out = out;
}
|
public void setOutputEncoding(String outputEncoding) {
urlEscapingCharsetCached = false;
super.setOutputEncoding(outputEncoding);
}
|
public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) {
super.setTemplateExceptionHandler(templateExceptionHandler);
lastThrowable = null;
}
|
public void setTimeFormat(String formatName) {
super.setTimeFormat(formatName);
timeFormat = null;
}
|
public void setTimeZone(TimeZone timeZone) {
super.setTimeZone(timeZone);
// Clear local date format cache
dateFormats = null;
timeFormat = dateFormat = dateTimeFormat = null;
}
|
public void setURLEscapingCharset(String urlEscapingCharset) {
urlEscapingCharsetCached = false;
super.setURLEscapingCharset(urlEscapingCharset);
}
|
public void setVariable(String name,
TemplateModel model) {
currentNamespace.put(name, model);
}
Sets a variable in the current namespace.
This is correspondent to FTL <#assign name=model> .
This can be considered a convenient shorthand for:
getCurrentNamespace().put(name, model) |
void visit(TemplateElement element) throws IOException, TemplateException {
pushElement(element);
try {
element.accept(this);
}
catch (TemplateException te) {
handleTemplateException(te);
}
finally {
popElement();
}
}
"Visit" the template element. |
void visit(BodyInstruction.Context bctxt) throws IOException, TemplateException {
Macro.Context invokingMacroContext = getCurrentMacroContext();
ArrayList prevLocalContextStack = localContextStack;
TemplateElement body = invokingMacroContext.body;
if (body != null) {
this.currentMacroContext = invokingMacroContext.prevMacroContext;
currentNamespace = invokingMacroContext.bodyNamespace;
Configurable prevParent = getParent();
setParent(currentNamespace.getTemplate());
this.localContextStack = invokingMacroContext.prevLocalContextStack;
if (invokingMacroContext.bodyParameterNames != null) {
pushLocalContext(bctxt);
}
try {
visit(body);
}
finally {
if (invokingMacroContext.bodyParameterNames != null) {
popLocalContext();
}
this.currentMacroContext = invokingMacroContext;
currentNamespace = getMacroNamespace(invokingMacroContext.getMacro());
setParent(prevParent);
this.localContextStack = prevLocalContextStack;
}
}
}
|
void visit(IteratorBlock.Context ictxt) throws IOException, TemplateException {
pushLocalContext(ictxt);
try {
ictxt.runLoop(this);
}
catch (BreakInstruction.Break br) {
}
catch (TemplateException te) {
handleTemplateException(te);
}
finally {
popLocalContext();
}
}
|
void visit(TemplateElement attemptBlock,
TemplateElement recoveryBlock) throws IOException, TemplateException {
Writer prevOut = this.out;
StringWriter sw = new StringWriter();
this.out = sw;
TemplateException thrownException = null;
try {
visit(attemptBlock);
} catch (TemplateException te) {
thrownException = te;
} finally {
this.out = prevOut;
}
if (thrownException != null) {
if (logger.isErrorEnabled()) {
String msg = "Error in attempt block " + attemptBlock.getStartLocation();
logger.error(msg, thrownException);
}
try {
recoveredErrorStack.add(thrownException.getMessage());
visit(recoveryBlock);
} finally {
recoveredErrorStack.remove(recoveredErrorStack.size() -1);
}
} else {
out.write(sw.toString());
}
}
Visit a block using buffering/recovery |
void visit(TemplateNodeModel node,
TemplateSequenceModel namespaces) throws IOException, TemplateException {
if (nodeNamespaces == null) {
SimpleSequence ss = new SimpleSequence(1);
ss.add(currentNamespace);
nodeNamespaces = ss;
}
int prevNodeNamespaceIndex = this.nodeNamespaceIndex;
String prevNodeName = this.currentNodeName;
String prevNodeNS = this.currentNodeNS;
TemplateSequenceModel prevNodeNamespaces = nodeNamespaces;
TemplateNodeModel prevVisitorNode = currentVisitorNode;
currentVisitorNode = node;
if (namespaces != null) {
this.nodeNamespaces = namespaces;
}
try {
TemplateModel macroOrTransform = getNodeProcessor(node);
if (macroOrTransform instanceof Macro) {
visit((Macro) macroOrTransform, null, null, null, null);
}
else if (macroOrTransform instanceof TemplateTransformModel) {
visit(null, (TemplateTransformModel) macroOrTransform, null);
}
else {
String nodeType = node.getNodeType();
if (nodeType != null) {
// If the node's type is 'text', we just output it.
if ((nodeType.equals("text") && node instanceof TemplateScalarModel))
{
out.write(((TemplateScalarModel) node).getAsString());
}
else if (nodeType.equals("document")) {
recurse(node, namespaces);
}
// We complain here, unless the node's type is 'pi', or "comment" or "document_type", in which case
// we just ignore it.
else if (!nodeType.equals("pi")
&& !nodeType.equals("comment")
&& !nodeType.equals("document_type"))
{
String nsBit = "";
String ns = node.getNodeNamespace();
if (ns != null) {
if (ns.length() >0) {
nsBit = " and namespace " + ns;
} else {
nsBit = " and no namespace";
}
}
throw new TemplateException("No macro or transform defined for node named "
+ node.getNodeName() + nsBit
+ ", and there is no fallback handler called @" + nodeType + " either.",
this);
}
}
else {
String nsBit = "";
String ns = node.getNodeNamespace();
if (ns != null) {
if (ns.length() >0) {
nsBit = " and namespace " + ns;
} else {
nsBit = " and no namespace";
}
}
throw new TemplateException("No macro or transform defined for node with name "
+ node.getNodeName() + nsBit
+ ", and there is no macro or transform called @default either.",
this);
}
}
}
finally {
this.currentVisitorNode = prevVisitorNode;
this.nodeNamespaceIndex = prevNodeNamespaceIndex;
this.currentNodeName = prevNodeName;
this.currentNodeNS = prevNodeNS;
this.nodeNamespaces = prevNodeNamespaces;
}
}
"Visit" A TemplateNodeModel |
void visit(TemplateElement element,
TemplateTransformModel transform,
Map args) throws IOException, TemplateException {
try {
Writer tw = transform.getWriter(out, args);
if (tw == null) tw = EMPTY_BODY_WRITER;
TransformControl tc =
tw instanceof TransformControl
? (TransformControl)tw
: null;
Writer prevOut = out;
out = tw;
try {
if(tc == null || tc.onStart() != TransformControl.SKIP_BODY) {
do {
if(element != null) {
visit(element);
}
} while(tc != null && tc.afterBody() == TransformControl.REPEAT_EVALUATION);
}
}
catch(Throwable t) {
try {
if(tc != null) {
tc.onError(t);
}
else {
throw t;
}
}
catch(TemplateException e) {
throw e;
}
catch(IOException e) {
throw e;
}
catch(RuntimeException e) {
throw e;
}
catch(Error e) {
throw e;
}
catch(Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
finally {
out = prevOut;
tw.close();
}
}
catch(TemplateException te) {
handleTemplateException(te);
}
}
"Visit" the template element, passing the output
through a TemplateTransformModel |
public void visit(TemplateElement element,
TemplateDirectiveModel directiveModel,
Map args,
List bodyParameterNames) throws IOException, TemplateException {
TemplateDirectiveBody nested;
if(element == null) {
nested = null;
}
else {
nested = new TemplateDirectiveBody() {
public void render(Writer newOut) throws TemplateException, IOException {
Writer prevOut = out;
out = newOut;
try {
Environment.this.visit(element);
}
finally {
out = prevOut;
}
}
};
}
final TemplateModel[] outArgs;
if(bodyParameterNames == null || bodyParameterNames.isEmpty()) {
outArgs = NO_OUT_ARGS;
}
else {
outArgs = new TemplateModel[bodyParameterNames.size()];
}
if(outArgs.length > 0) {
pushLocalContext(new LocalContext() {
public TemplateModel getLocalVariable(String name) {
int index = bodyParameterNames.indexOf(name);
return index != -1 ? outArgs[index] : null;
}
public Collection getLocalVariableNames() {
return bodyParameterNames;
}
});
}
try {
directiveModel.execute(this, args, outArgs, nested);
}
finally {
if(outArgs.length > 0) {
popLocalContext();
}
}
}
|
void visit(Macro macro,
Map namedArgs,
List positionalArgs,
List bodyParameterNames,
TemplateElement nestedBlock) throws IOException, TemplateException {
if (macro == Macro.DO_NOTHING_MACRO) {
return;
}
pushElement(macro);
try {
Macro.Context previousMacroContext = currentMacroContext;
Macro.Context mc = macro.new Context(this, nestedBlock, bodyParameterNames);
String catchAll = macro.getCatchAll();
TemplateModel unknownVars = null;
if (namedArgs != null) {
if (catchAll != null)
unknownVars = new SimpleHash();
for (Iterator it = namedArgs.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String varName = (String) entry.getKey();
boolean hasVar = macro.hasArgNamed(varName);
if (hasVar || catchAll != null) {
Expression arg = (Expression) entry.getValue();
TemplateModel value = arg.getAsTemplateModel(this);
if (hasVar) {
mc.setLocalVar(varName, value);
} else {
((SimpleHash)unknownVars).put(varName, value);
}
} else {
String msg = "Macro " + macro.getName() + " has no such argument: " + varName;
throw new TemplateException(msg, this);
}
}
}
else if (positionalArgs != null) {
if (catchAll != null)
unknownVars = new SimpleSequence();
String[] argumentNames = macro.getArgumentNames();
int size = positionalArgs.size();
if (argumentNames.length < size && catchAll == null) {
throw new TemplateException("Macro " + macro.getName()
+ " only accepts " + argumentNames.length + " parameters.", this);
}
for (int i = 0; i < size; i++) {
Expression argExp = (Expression) positionalArgs.get(i);
TemplateModel argModel = argExp.getAsTemplateModel(this);
try {
if (i < argumentNames.length) {
String argName = argumentNames[i];
mc.setLocalVar(argName, argModel);
} else {
((SimpleSequence)unknownVars).add(argModel);
}
} catch (RuntimeException re) {
throw new TemplateException(re, this);
}
}
}
if (catchAll != null) {
mc.setLocalVar(catchAll, unknownVars);
}
ArrayList prevLocalContextStack = localContextStack;
localContextStack = null;
Namespace prevNamespace = currentNamespace;
Configurable prevParent = getParent();
currentNamespace = (Namespace) macroToNamespaceLookup.get(macro);
currentMacroContext = mc;
try {
mc.runMacro(this);
}
catch (ReturnInstruction.Return re) {
}
catch (TemplateException te) {
handleTemplateException(te);
} finally {
currentMacroContext = previousMacroContext;
localContextStack = prevLocalContextStack;
currentNamespace = prevNamespace;
setParent(prevParent);
}
} finally {
popElement();
}
}
|
void visitMacroDef(Macro macro) {
macroToNamespaceLookup.put(macro, currentNamespace);
currentNamespace.put(macro.getName(), macro);
}
|