All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kohsuke.stapler.jelly.CustomJellyContext Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *
 * Copyright (c) 2004-2010 Oracle Corporation.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: 
 *
 *    Kohsuke Kawaguchi
 *     
 *******************************************************************************/ 

package org.kohsuke.stapler.jelly;

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;
import java.util.Map;
import java.util.HashMap;

/**
 * {@link XMLParser} that uses {@link JellyClassLoaderTearOff#EXPRESSION_FACTORY}
 * for expression parsing.
 *
 * @author Kohsuke Kawaguchi
*/
class CustomJellyContext extends JellyContext {
    private JellyClassLoaderTearOff jclt;

    CustomJellyContext() {
        init();
    }

    CustomJellyContext(URL url) {
        super(url);
        init();
    }

    CustomJellyContext(URL url, URL url1) {
        super(url, url1);
        init();
    }

    CustomJellyContext(JellyContext jellyContext) {
        super(jellyContext);
        init();
    }

    CustomJellyContext(JellyContext jellyContext, URL url) {
        super(jellyContext, url);
        init();
    }

    CustomJellyContext(JellyContext jellyContext, URL url, URL url1) {
        super(jellyContext, url, url1);
        init();
    }

    private void init() {
        // by not allowing the empty namespace URI "" to be handled as dynamic tags,
        // we achieve substantial performance improvement.
        registerTagLibrary("",ReallyStaticTagLibrary.INSTANCE);
        registerTagLibrary("this",ThisTagLibrary.INSTANCE);
    }

    @Override
    protected XMLParser createXMLParser() {
        return new CustomXMLParser();
    }

    @Override
    public void setClassLoader(ClassLoader classLoader) {
        super.setClassLoader(classLoader);
        jclt = MetaClassLoader.get(classLoader).loadTearOff(JellyClassLoaderTearOff.class);
    }

    @Override
    public TagLibrary getTagLibrary(String namespaceURI) {
        TagLibrary tl = super.getTagLibrary(namespaceURI);

        // delegate to JellyClassLoaderTearOff for taglib handling
        if(tl==null && jclt!=null) {
            tl = jclt.getTagLibrary(namespaceURI);
            if (tl!=null)
                registerTagLibrary(namespaceURI,tl);
        }
        return tl;
    }

    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;
        }

        public Expression createExpression(final String text) throws JellyException {
            if(text.startsWith("%")) {
                // this is a message resource reference
                return createI18nExp(text);
            } else {
                Matcher m = RESOURCE_LITERAL_STRING.matcher(text);
                if(m.find()) {
                    // contains the resource literal, so pre-process them.
                    
                    final StringBuilder buf = new StringBuilder();
                    final Map resourceLiterals = new HashMap();
                    int e=0;
                    do {
                        // copy the text preceding the match
                        buf.append(text.substring(e,m.start()));

                        String varName = "__resourceLiteral__"+resourceLiterals.size()+"__";
                        InternationalizedStringExpression exp = createI18nExp(unquote(m.group()));
                        resourceLiterals.put(varName,exp);

                        // replace the literal by the evaluation
                        buf.append(varName).append(".evaluate(context)");
                        e = m.end();
                    } while(m.find());

                    buf.append(text.substring(e));

                    return new I18nExpWithArgsExpression(text, resourceLiterals, buf.toString());
                }

                return JellyClassLoaderTearOff.EXPRESSION_FACTORY.createExpression(text);
            }
        }

        private InternationalizedStringExpression createI18nExp(String text) throws JellyException {
            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);
        }

        private ResourceBundle getResourceBundle() {
            if(resourceBundle==null)
                resourceBundle = ResourceBundle.load(locator.getSystemId());
            return resourceBundle;
        }

        /**
         * {@link Expression} that handles things like "%foo(a,b,c)"
         */
        private static class I18nExpWithArgsExpression extends ExpressionSupport {
            final Expression innerExpression;
            private final String text;
            private final Map resourceLiterals;

            public I18nExpWithArgsExpression(String text, Map resourceLiterals, String exp) throws JellyException {
                this.text = text;
                this.resourceLiterals = resourceLiterals;
                innerExpression = JellyClassLoaderTearOff.EXPRESSION_FACTORY.createExpression(exp);
            }

            public String getExpressionText() {
                return text;
            }

            public Object evaluate(JellyContext context) {
                context = new CustomJellyContext(context);
                context.setVariables(resourceLiterals);
                return innerExpression.evaluate(context);
            }
        }
    }

    // "%...."    string literal that starts with '%'
    private static final Pattern RESOURCE_LITERAL_STRING = Pattern.compile("(\"%[^\"]+\")|('%[^']+')");
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy