package org.apache.velocity.tools.view.jsp;
/*
* 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.StringWriter;
import java.io.Writer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import org.apache.velocity.Template;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
import org.apache.velocity.tools.view.ServletUtils;
import org.apache.velocity.tools.view.ViewToolContext;
import org.apache.velocity.tools.view.VelocityView;
/**
* <p>This tag enables use of Velocity and VelocityTools within JSP files and tags.
* This makes it trivial to render embedded VTL (Velocity Template Language)
* or include a separate Velocity template within a JSP using the current
* page context. This also automatically provides the typical
* {@link VelocityView} toolbox support, much like the VelocityViewServlet
* and VelocityLayoutServlets have. In fact, this will by default share
* the {@link VelocityView} instance used with those servlets. This allows
* for consistent configuration and shared resources (better performance).
* </p>
*
* @author Nathan Bubna
* @version $Id: VelocityViewTag.java,v 1.1 2001/08/14 00:07:39 geirm Exp $
* @since VelocityTools 2.0
*/
public class VelocityViewTag extends BodyTagSupport
{
public static final String DEFAULT_BODY_CONTENT_KEY = "bodyContent";
public static final String DEFAULT_NAME =
VelocityViewTag.class.getSimpleName();
private static int count = 0;
private static final long serialVersionUID = -3329444102562079189L;
protected transient VelocityView view;
protected transient ViewToolContext context;
protected String var;
protected String scope;
protected String template;
protected String bodyContentKey = DEFAULT_BODY_CONTENT_KEY;
private boolean cacheable = true;
private boolean uncached = true;
public VelocityViewTag()
{
// always try to have some sort of unique id set, since
// this serves as a log tag and may serve as a cache name later
setId(DEFAULT_NAME + count++);
}
public void setId(String id)
{
// always try to have some sort of id set because
// this is the log tag and cache name
if (id == null)
{
throw new NullPointerException("id cannot be null");
}
super.setId(id);
}
public void setVar(String var)
{
this.var = var;
}
public String getVar()
{
return this.var;
}
public void setScope(String scope)
{
this.scope = scope;
}
public String getScope()
{
return this.scope;
}
public void setTemplate(String template)
{
this.template = template;
}
public String getTemplate()
{
return this.template;
}
public void setBodyContentKey(String key)
{
this.bodyContentKey = DEFAULT_BODY_CONTENT_KEY;
}
public String getBodyContentKey()
{
return this.bodyContentKey;
}
public VelocityView getVelocityView()
{
return this.view;
}
public void setVelocityView(VelocityView view)
{
this.view = view;
}
public ViewToolContext getViewToolContext()
{
return this.context;
}
public void setViewToolContext(ViewToolContext context)
{
this.context = context;
}
public int doStartTag() throws JspException
{
initializeView();
return EVAL_BODY_BUFFERED;
}
public int doEndTag() throws JspException
{
if (hasContent())
{
try
{
// check for a var attribute
String varname = getVar();
if (varname == null)
{
// if none, render into the pageout
renderContent(this.pageContext.getOut());
}
else
{
// if we have a var, render into a string
StringWriter out = new StringWriter();
renderContent(out);
// and insert the string into the specified scope
// if none specified, default is PAGE_SCOPE
this.pageContext.setAttribute(varname,
out.toString(),
toScopeInt(getScope()));
}
}
catch (Exception e)
{
throw new JspException("Failed to render " + getClass() +
": "+getId(), e);
}
}
return EVAL_PAGE;
}
protected void initializeView()
{
// get the VelocityView for this app
VelocityView view =
ServletUtils.getVelocityView(this.pageContext.getServletConfig());
// now make a Context
ViewToolContext context =
new JspToolContext(view.getVelocityEngine(), this.pageContext);
view.prepareContext(context, (HttpServletRequest)this.pageContext.getRequest());
setVelocityView(view);
setViewToolContext(context);
}
protected boolean hasContent()
{
return (getBodyContent() != null || getTemplate() != null);
}
protected void renderContent(Writer out) throws Exception
{
if (getTemplate() != null)
{
VelocityView view = getVelocityView();
ViewToolContext context = getViewToolContext();
// get the actual Template
Template template = view.getTemplate(getTemplate());
if (getBodyContent() != null)
{
context.put(getBodyContentKey(), getRenderedBody());
}
// render the template into the writer
template.merge(context, out);
}
else
{
// render the body into the writer
renderBody(out);
}
}
protected String getRenderedBody() throws Exception
{
// render the body into a string
StringWriter out = new StringWriter();
renderBody(out);
return out.toString();
}
protected void renderBody(Writer out) throws Exception
{
// if it hasn't been cached, try that
if (uncached && cacheable)
{
cache(getId(), getBodyContent().getString());
}
// if it can't be cached, eval it
if (!cacheable)
{
evalBody(out);
}
else
{
// load template from cache
Template template = getVelocityView().getTemplate(getId());
template.merge(getViewToolContext(), out);
}
}
protected void evalBody(Writer out) throws Exception
{
VelocityEngine engine = getVelocityView().getVelocityEngine();
engine.evaluate(getViewToolContext(), out, getId(),
getBodyContent().getReader());
}
protected static int toScopeInt(String scope)
{
if (scope == null)
{
return PageContext.PAGE_SCOPE;
}
if (scope.equalsIgnoreCase("request"))
{
return PageContext.REQUEST_SCOPE;
}
if (scope.equalsIgnoreCase("session"))
{
return PageContext.SESSION_SCOPE;
}
if (scope.equalsIgnoreCase("application"))
{
return PageContext.APPLICATION_SCOPE;
}
if (scope.equalsIgnoreCase("page"))
{
return PageContext.PAGE_SCOPE;
}
throw new IllegalArgumentException("Unknown scope: "+scope);
}
private void cache(String name, String template)
{
try
{
StringResourceLoader.getRepository().putStringResource(name, template);
uncached = false;
}
catch (Exception cnfe)
{
cacheable = false;
}
}
}