org.apache.velocity.runtime.directive.Define Maven / Gradle / Ivy
package org.apache.velocity.runtime.directive;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.Writer;
import java.io.IOException;
import java.io.StringWriter;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.TemplateInitException;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.Renderable;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.log.Log;
import org.apache.velocity.runtime.parser.node.Node;
/**
* Directive that puts an unrendered AST block in the context
* under the specified key, postponing rendering until the
* reference is used and rendered.
*
* @author Andrew Tetlaw
* @author Nathan Bubna
* @version $Id: Define.java 686842 2008-08-18 18:29:31Z nbubna $
*/
public class Define extends Directive
{
private String key;
private Node block;
private Log log;
private int maxDepth;
private String definingTemplate;
/**
* Return name of this directive.
*/
public String getName()
{
return "define";
}
/**
* Return type of this directive.
*/
public int getType()
{
return BLOCK;
}
/**
* simple init - get the key
*/
public void init(RuntimeServices rs, InternalContextAdapter context, Node node)
throws TemplateInitException
{
super.init(rs, context, node);
log = rs.getLog();
/*
* default max depth of two is used because intentional recursion is
* unlikely and discouraged, so make unintentional ones end fast
*/
maxDepth = rs.getInt(RuntimeConstants.DEFINE_DIRECTIVE_MAXDEPTH, 2);
/*
* first token is the name of the block. We don't even check the format,
* just assume it looks like this: $block_name. Should we check if it has
* a '$' or not?
*/
key = node.jjtGetChild(0).getFirstToken().image.substring(1);
/**
* No checking is done. We just grab the second child node and assume
* that it's the block!
*/
block = node.jjtGetChild(1);
/**
* keep tabs on the template this came from
*/
definingTemplate = context.getCurrentTemplateName();
}
/**
* directive.render() simply makes an instance of the Block inner class
* and places it into the context as indicated.
*/
public boolean render(InternalContextAdapter context, Writer writer, Node node)
{
/* put a Block instance into the context,
* using the user-defined key, for later inline rendering.
*/
context.put(key, new Block(context, this));
return true;
}
/**
* Creates a string identifying the source and location of the block
* definition, and the current template being rendered if that is
* different.
*/
protected String id(InternalContextAdapter context)
{
StrBuilder str = new StrBuilder(100)
.append("block $").append(key)
.append(" (defined in ").append(definingTemplate)
.append(" [line ").append(getLine())
.append(", column ").append(getColumn()).append("])");
if (!context.getCurrentTemplateName().equals(definingTemplate))
{
str.append(" used in ").append(context.getCurrentTemplateName());
}
return str.toString();
}
/**
* actual class placed in the context, holds the context and writer
* being used for the render, as well as the parent (which already holds
* everything else we need).
*/
public static class Block implements Renderable
{
private InternalContextAdapter context;
private Define parent;
private int depth;
public Block(InternalContextAdapter context, Define parent)
{
this.context = context;
this.parent = parent;
}
/**
*
*/
public boolean render(InternalContextAdapter context, Writer writer)
{
try
{
depth++;
if (depth > parent.maxDepth)
{
/* this is only a debug message, as recursion can
* happen in quasi-innocent situations and is relatively
* harmless due to how we handle it here.
* this is more to help anyone nuts enough to intentionally
* use recursive block definitions and having problems
* pulling it off properly.
*/
parent.log.debug("Max recursion depth reached for "+parent.id(context));
depth--;
return false;
}
else
{
parent.block.render(context, writer);
depth--;
return true;
}
}
catch (IOException e)
{
String msg = "Failed to render "+parent.id(context)+" to writer";
parent.log.error(msg, e);
throw new RuntimeException(msg, e);
}
catch (VelocityException ve)
{
String msg = "Failed to render "+parent.id(context)+" due to "+ve;
parent.log.error(msg, ve);
throw ve;
}
}
public String toString()
{
Writer stringwriter = new StringWriter();
if(render(context,stringwriter))
{
return stringwriter.toString();
}
else
{
return null;
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy