diff --git a/src/java/com/samskivert/servlet/MessageManager.java b/src/java/com/samskivert/servlet/MessageManager.java index 2d1e351e..efcd8356 100644 --- a/src/java/com/samskivert/servlet/MessageManager.java +++ b/src/java/com/samskivert/servlet/MessageManager.java @@ -179,6 +179,12 @@ public class MessageManager break; } String ref = message.substring(oidx+1, cidx); + // avoid trivial infinite recursion + if (ref.equals(path)) { + throw new IllegalStateException( + "Illegal self-referential message " + path + " = " + + message + "."); + } if (ref.length() > 0 && !Character.isDigit(ref.charAt(0))) { String refmsg = getMessage(req, ref, true); message = message.substring(0, oidx) + diff --git a/src/java/com/samskivert/velocity/CurrencyTool.java b/src/java/com/samskivert/velocity/CurrencyTool.java index 20b33222..316899f6 100644 --- a/src/java/com/samskivert/velocity/CurrencyTool.java +++ b/src/java/com/samskivert/velocity/CurrencyTool.java @@ -20,7 +20,7 @@ package com.samskivert.velocity; -import javax.servlet.http.HttpServletRequest; +import java.util.Locale; import com.samskivert.util.CurrencyUtil; @@ -33,9 +33,9 @@ public class CurrencyTool * Creates a new CurrencyTool which will used the supplied request to * look up the locale with which to do currency formatting in. */ - public CurrencyTool (HttpServletRequest req) + public CurrencyTool (Locale locale) { - _req = req; + _locale = locale; } /** @@ -61,7 +61,7 @@ public class CurrencyTool */ public String currency (double value) { - return CurrencyUtil.currency(value, _req.getLocale()); + return CurrencyUtil.currency(value, _locale); } /** @@ -70,7 +70,7 @@ public class CurrencyTool */ public String currencyPennies (double value) { - return CurrencyUtil.currencyPennies(value, _req.getLocale()); + return CurrencyUtil.currencyPennies(value, _locale); } /** @@ -82,6 +82,6 @@ public class CurrencyTool return "" + (pennies / 100.0); } - /** The servlet request we are providing currency functionality for. */ - protected HttpServletRequest _req; + /** The locale in which we are providing currency functionality. */ + protected Locale _locale; } diff --git a/src/java/com/samskivert/velocity/DispatcherServlet.java b/src/java/com/samskivert/velocity/DispatcherServlet.java index 98e6f58d..10af204b 100644 --- a/src/java/com/samskivert/velocity/DispatcherServlet.java +++ b/src/java/com/samskivert/velocity/DispatcherServlet.java @@ -345,7 +345,7 @@ public class DispatcherServlet extends VelocityServlet ictx.put(DATATOOL_KEY, datatool); // create a curreny tool set up to use the correct locale - CurrencyTool ctool = new CurrencyTool(req); + CurrencyTool ctool = new CurrencyTool(req.getLocale()); ictx.put(CURRENCYTOOL_KEY, ctool); // allow the application to prepare the context diff --git a/src/java/com/samskivert/velocity/FormTool.java b/src/java/com/samskivert/velocity/FormTool.java index abb37a03..1cfd0aa5 100644 --- a/src/java/com/samskivert/velocity/FormTool.java +++ b/src/java/com/samskivert/velocity/FormTool.java @@ -278,8 +278,9 @@ public class FormTool */ public String checkbox (String name, boolean defaultValue) { + String value = getParameter(name); return fixedCheckbox( - name, ParameterUtil.isSet(_req, name, defaultValue)); + name, (value == null) ? defaultValue : !value.equals("")); } /** @@ -413,7 +414,7 @@ public class FormTool */ protected String getValue (String name, Object defaultValue) { - String value = ParameterUtil.getParameter(_req, name, true); + String value = getParameter(name); if (StringUtil.isBlank(value)) { if (defaultValue == null) { value = ""; @@ -424,6 +425,14 @@ public class FormTool return HTMLUtil.entify(value); } + /** + * Returns value of the specified query parameter or null if it is not set. + */ + protected String getParameter (String name) + { + return ParameterUtil.getParameter(_req, name, true); + } + /** A reference to the servlet request in use by this form tool. */ protected HttpServletRequest _req; diff --git a/src/java/com/samskivert/velocity/I18nTool.java b/src/java/com/samskivert/velocity/I18nTool.java index 4765bf5e..c8597f9e 100644 --- a/src/java/com/samskivert/velocity/I18nTool.java +++ b/src/java/com/samskivert/velocity/I18nTool.java @@ -24,6 +24,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; +import java.util.Locale; import javax.servlet.http.HttpServletRequest; import com.samskivert.servlet.MessageManager; @@ -117,7 +118,7 @@ public class I18nTool if (when == null) { return format; } - SimpleDateFormat fmt = new SimpleDateFormat(format, _req.getLocale()); + SimpleDateFormat fmt = new SimpleDateFormat(format, getLocale()); return fmt.format(when); } @@ -154,7 +155,13 @@ public class I18nTool if (when == null) { return ""; } - return DateFormat.getDateInstance(style, _req.getLocale()).format(when); + return DateFormat.getDateInstance(style, getLocale()).format(when); + } + + /** Returns the locale associated with this request. */ + protected Locale getLocale () + { + return _req.getLocale(); } /** Converts an argument to a {@link Date} if possible. */ diff --git a/src/java/com/samskivert/velocity/VelocityTestCase.java b/src/java/com/samskivert/velocity/VelocityTestCase.java index 93d65ae0..8d9d5a5c 100644 --- a/src/java/com/samskivert/velocity/VelocityTestCase.java +++ b/src/java/com/samskivert/velocity/VelocityTestCase.java @@ -20,20 +20,22 @@ package com.samskivert.velocity; +import java.io.File; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Locale; import junit.framework.TestCase; -import org.apache.commons.io.IOUtils; - import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.RuntimeServices; import org.apache.velocity.runtime.log.LogChute; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import com.samskivert.servlet.MessageManager; + /** * An abstract base class for easily testing Velocity templates. */ @@ -68,7 +70,7 @@ public abstract class VelocityTestCase extends TestCase // first simply evaluate all of the templates for (String template : getTemplates()) { try { - evaluateTemplate(template); + testParse(template); } catch (Exception e) { fail("Failed to process " + template + ": " + e); } @@ -77,14 +79,14 @@ public abstract class VelocityTestCase extends TestCase // then try to actually merge them with a context for (String template : getTemplates()) { try { - mergeTemplate(template); + testMerge(template); } catch (Exception e) { fail("Failed to process " + template + ": " + e); } } } - protected void evaluateTemplate (String template) + protected void testParse (String template) throws Exception { InputStream tempin = @@ -100,36 +102,63 @@ public abstract class VelocityTestCase extends TestCase } } - protected void mergeTemplate (String template) + protected void testMerge (String template) throws Exception { - // first load our golden output - String goldpath = getGoldenFilePath(template); - InputStream goldin = - getClass().getClassLoader().getResourceAsStream(goldpath); - if (goldin == null) { - // if there is no golden template, we don't test this file - return; - } - String goldtext = IOUtils.toString(goldin); - // clear out any previous logging output _logbuf.getBuffer().setLength(0); - // now generate the test output - VelocityContext context = new VelocityContext(); - populateContext(template, context); - StringWriter writer = new StringWriter(); - _engine.mergeTemplate(template, context, writer); + // populate the context with useful bits + VelocityContext ctx = new VelocityContext(); + populateContext(template, ctx); - // now compare the two - String output = "Template output incorrect [template=" + template + - ", golden=" + goldpath + "]."; + // now merge the template + StringWriter writer = new StringWriter(); + _engine.mergeTemplate(template, ctx, writer); + + // if there was any logging output, the test failed String logout = _logbuf.toString(); if (logout.length() > 0) { - output = output + "\n" + logout; + fail("Template merge failed '" + template + "'.\n" + logout); } - assertEquals(output, goldtext, writer.toString()); + } + + /** + * Called before parsing each template to populate the context as needed + * for the template in question. The default implementation adds some + * standard bits provided by {@link DispatcherServlet}. + */ + protected void populateContext (String template, VelocityContext ctx) + { + ctx.put("context_path", "/test"); + + // populate the context with various standard tools + MessageManager msgmgr = getMessageManager(); + if (msgmgr != null) { + ctx.put(DispatcherServlet.I18NTOOL_KEY, new I18nTool(null, msgmgr) { + protected Locale getLocale () { + return Locale.getDefault(); + } + }); + } + ctx.put(DispatcherServlet.FORMTOOL_KEY, new FormTool(null) { + protected String getParameter (String name) { + return null; + } + }); + ctx.put(DispatcherServlet.STRINGTOOL_KEY, new StringTool()); + ctx.put(DispatcherServlet.DATATOOL_KEY, new DataTool()); + ctx.put(DispatcherServlet.CURRENCYTOOL_KEY, + new CurrencyTool(Locale.getDefault())); + } + + /** + * If the tests require translation, this method must return a message + * manager configured appropriately. + */ + protected MessageManager getMessageManager () + { + return null; } /** @@ -138,25 +167,6 @@ public abstract class VelocityTestCase extends TestCase */ protected abstract String[] getTemplates (); - /** - * Called before parsing each template to populate the context as needed - * for the template in question. The default implementation adds nothing to - * the context. - */ - protected void populateContext (String template, VelocityContext context) - { - } - - /** - * Returns the path (resolved via the classloader) of the golden file - * against which to compare the output of the supplied template. The - * default simply appends .test to the path. - */ - protected String getGoldenFilePath (String template) - { - return template + ".test"; - } - /** Accumulates logging to a buffer for later reporting. */ protected LogChute _logger = new LogChute() { public void init (RuntimeServices rs) throws Exception {