| author | Stuart McCulloch | 2012-04-12 11:54:47 (EDT) |
|---|---|---|
| committer | Stuart McCulloch | 2012-04-12 11:54:47 (EDT) |
| commit | 5cca490c5b9fc44f60a877f767508bc81bff5945 (patch) (side-by-side diff) | |
| tree | e0e35789a2d3b818df80aa1f346375d57ab2932a | |
| parent | 927dae24ffde533911d9a7b4dd445c629c6158f0 (diff) | |
| download | org.eclipse.hudson.stapler-5cca490c5b9fc44f60a877f767508bc81bff5945.zip org.eclipse.hudson.stapler-5cca490c5b9fc44f60a877f767508bc81bff5945.tar.gz org.eclipse.hudson.stapler-5cca490c5b9fc44f60a877f767508bc81bff5945.tar.bz2 | |
Support escape-by-default and raw HTML arguments in InternationalizedStringExpressions
| -rw-r--r-- | stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/CustomJellyContext.java | 30 | ||||
| -rw-r--r-- | stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/InternationalizedStringExpression.java | 76 |
2 files changed, 104 insertions, 2 deletions
diff --git a/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/CustomJellyContext.java b/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/CustomJellyContext.java index 4f1826f..6154280 100644 --- a/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/CustomJellyContext.java +++ b/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/CustomJellyContext.java @@ -19,11 +19,14 @@ import org.apache.commons.jelly.parser.XMLParser; import org.apache.commons.jelly.expression.ExpressionFactory; import org.apache.commons.jelly.expression.Expression; import org.apache.commons.jelly.expression.ExpressionSupport; +import org.apache.commons.jelly.impl.ExpressionScript; +import org.apache.commons.jelly.impl.ScriptBlock; import org.apache.commons.jelly.JellyContext; import org.apache.commons.jelly.JellyException; import org.apache.commons.jelly.TagLibrary; import org.kohsuke.stapler.MetaClassLoader; +import java.lang.reflect.Field; import java.net.URL; import java.util.regex.Pattern; import java.util.regex.Matcher; @@ -101,6 +104,18 @@ class CustomJellyContext extends JellyContext { private static class CustomXMLParser extends XMLParser implements ExpressionFactory { private ResourceBundle resourceBundle; + + private static Field escapeByDefaultField; + static + { + try { + escapeByDefaultField = XMLParser.class.getDeclaredField("escapeByDefault"); + escapeByDefaultField.setAccessible(true); + } catch (Exception e) { + // inconsistent base class - ignore + } + } + @Override protected ExpressionFactory createExpressionFactory() { return this; @@ -144,6 +159,19 @@ class CustomJellyContext extends JellyContext { return new InternationalizedStringExpression(getResourceBundle(),text); } + @Override + protected void addExpressionScript(ScriptBlock script, Expression exp) { + try { + if (exp instanceof InternationalizedStringExpression && escapeByDefaultField.getBoolean(this)) { + script.addScript(new ExpressionScript(((InternationalizedStringExpression) exp).escape())); + return; // stick with our escaped+internationalized script + } + } catch (Exception e) { + // fall back to original behaviour... + } + super.addExpressionScript(script, exp); + } + private String unquote(String s) { return s.substring(1,s.length()-1); } @@ -182,4 +210,4 @@ class CustomJellyContext extends JellyContext { // "%...." string literal that starts with '%' private static final Pattern RESOURCE_LITERAL_STRING = Pattern.compile("(\"%[^\"]+\")|('%[^']+')"); -}
\ No newline at end of file +} diff --git a/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/InternationalizedStringExpression.java b/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/InternationalizedStringExpression.java index 8ac3153..53c375f 100644 --- a/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/InternationalizedStringExpression.java +++ b/stapler-jelly/src/main/java/org/kohsuke/stapler/jelly/InternationalizedStringExpression.java @@ -22,9 +22,10 @@ import org.apache.commons.jelly.JellyContext; import org.jvnet.localizer.LocaleProvider; import org.kohsuke.stapler.Stapler; +import java.util.Calendar; +import java.util.Date; import java.util.List; import java.util.ArrayList; -import java.util.Locale; import java.util.Collections; import java.util.Arrays; @@ -125,10 +126,17 @@ public class InternationalizedStringExpression extends ExpressionSupport { } public Object evaluate(JellyContext jellyContext) { + return format(evaluateArguments(jellyContext)); + } + + Object[] evaluateArguments(JellyContext jellyContext) { Object[] args = new Object[arguments.length]; for (int i = 0; i < args.length; i++) args[i] = arguments[i].evaluate(jellyContext); + return args; + } + String format(Object[] args) { // notify the listener if set InternationalizedStringExpressionListener listener = (InternationalizedStringExpressionListener)Stapler.getCurrentRequest().getAttribute(LISTENER_NAME); if(listener!=null) @@ -137,6 +145,72 @@ public class InternationalizedStringExpression extends ExpressionSupport { return resourceBundle.format(LocaleProvider.getLocale(),key,args); } + /** + * Wraps value to indicate it contains raw HTML that should not be escaped. + */ + public Object rawHtml(Object value) { + return value != null ? new RawHtml(value) : null; + } + + static final class RawHtml { + final Object value; + + RawHtml(Object value) { + this.value = value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + Expression escape() { + return new ExpressionSupport() { + public String getExpressionText() { + return expressionText; + } + + public Object evaluate(JellyContext context) { + Object[] args = evaluateArguments(context); + for (int i = 0; i < args.length; i++) { + args[i] = escapeArgument(args[i]); + } + return format(args); + } + }; + } + + static Object escapeArgument(Object arg) { + if (arg instanceof RawHtml) { + return ((RawHtml)arg).value; // no escaping wanted + } + if ( arg == null || arg instanceof Number || arg instanceof Date || arg instanceof Calendar ) { + return arg; // no escaping required + } + final String text = arg.toString(); + StringBuilder buf = null; // create on-demand + for (int i = 0, len = text.length(); i < len; i++) { + final char c = text.charAt(i); + String replacement = null; + if (c == '&') { + replacement = "&"; + } else if (c == '<') { + replacement = "<"; + } else if (buf != null) { + buf.append(c); // maintain buffer + } + if (replacement != null) { + if (buf == null) { + // only create translation buffer when we actually need it + buf = new StringBuilder(len+8).append(text.substring(0, i)); + } + buf.append(replacement); + } + } + return buf != null ? buf : text; + } + private static final Expression[] EMPTY_ARGUMENTS = new Expression[0]; private static final String LISTENER_NAME = InternationalizedStringExpressionListener.class.getName(); } |

